Added code to prevent unnecessary width updates.
[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 \r
1396   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1397   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1398   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1399   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1400   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1401   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1402   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1403   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1404   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1405   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1406   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1407   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1408   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1409   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1410   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1411   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1412   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1413   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1414   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1415   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1416   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1417   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1418   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1419   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1420   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1421   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1422   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1423   /* [AS] Layout stuff */\r
1424   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1425   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1426   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1427   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1428   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1429 \r
1430   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1431   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1432   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1433   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1434   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1435 \r
1436   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1437   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1438   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1439   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1440   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1441 \r
1442   { NULL, ArgNone, NULL, FALSE }\r
1443 };\r
1444 \r
1445 \r
1446 /* Kludge for indirection files on command line */\r
1447 char* lastIndirectionFilename;\r
1448 ArgDescriptor argDescriptorIndirection =\r
1449 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1450 \r
1451 \r
1452 VOID\r
1453 ExitArgError(char *msg, char *badArg)\r
1454 {\r
1455   char buf[MSG_SIZ];\r
1456 \r
1457   sprintf(buf, "%s %s", msg, badArg);\r
1458   DisplayFatalError(buf, 0, 2);\r
1459   exit(2);\r
1460 }\r
1461 \r
1462 /* Command line font name parser.  NULL name means do nothing.\r
1463    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1464    For backward compatibility, syntax without the colon is also\r
1465    accepted, but font names with digits in them won't work in that case.\r
1466 */\r
1467 VOID\r
1468 ParseFontName(char *name, MyFontParams *mfp)\r
1469 {\r
1470   char *p, *q;\r
1471   if (name == NULL) return;\r
1472   p = name;\r
1473   q = strchr(p, ':');\r
1474   if (q) {\r
1475     if (q - p >= sizeof(mfp->faceName))\r
1476       ExitArgError("Font name too long:", name);\r
1477     memcpy(mfp->faceName, p, q - p);\r
1478     mfp->faceName[q - p] = NULLCHAR;\r
1479     p = q + 1;\r
1480   } else {\r
1481     q = mfp->faceName;\r
1482     while (*p && !isdigit(*p)) {\r
1483       *q++ = *p++;\r
1484       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1485         ExitArgError("Font name too long:", name);\r
1486     }\r
1487     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1488     *q = NULLCHAR;\r
1489   }\r
1490   if (!*p) ExitArgError("Font point size missing:", name);\r
1491   mfp->pointSize = (float) atof(p);\r
1492   mfp->bold = (strchr(p, 'b') != NULL);\r
1493   mfp->italic = (strchr(p, 'i') != NULL);\r
1494   mfp->underline = (strchr(p, 'u') != NULL);\r
1495   mfp->strikeout = (strchr(p, 's') != NULL);\r
1496 }\r
1497 \r
1498 /* Color name parser.\r
1499    X version accepts X color names, but this one\r
1500    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1501 COLORREF\r
1502 ParseColorName(char *name)\r
1503 {\r
1504   int red, green, blue, count;\r
1505   char buf[MSG_SIZ];\r
1506 \r
1507   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1508   if (count != 3) {\r
1509     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1510       &red, &green, &blue);\r
1511   }\r
1512   if (count != 3) {\r
1513     sprintf(buf, "Can't parse color name %s", name);\r
1514     DisplayError(buf, 0);\r
1515     return RGB(0, 0, 0);\r
1516   }\r
1517   return PALETTERGB(red, green, blue);\r
1518 }\r
1519 \r
1520 \r
1521 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1522 {\r
1523   char *e = argValue;\r
1524   int eff = 0;\r
1525 \r
1526   while (*e) {\r
1527     if (*e == 'b')      eff |= CFE_BOLD;\r
1528     else if (*e == 'i') eff |= CFE_ITALIC;\r
1529     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1530     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1531     else if (*e == '#' || isdigit(*e)) break;\r
1532     e++;\r
1533   }\r
1534   *effects = eff;\r
1535   *color   = ParseColorName(e);\r
1536 }\r
1537 \r
1538 \r
1539 BoardSize\r
1540 ParseBoardSize(char *name)\r
1541 {\r
1542   BoardSize bs = SizeTiny;\r
1543   while (sizeInfo[bs].name != NULL) {\r
1544     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1545     bs++;\r
1546   }\r
1547   ExitArgError("Unrecognized board size value", name);\r
1548   return bs; /* not reached */\r
1549 }\r
1550 \r
1551 \r
1552 char\r
1553 StringGet(void *getClosure)\r
1554 {\r
1555   char **p = (char **) getClosure;\r
1556   return *((*p)++);\r
1557 }\r
1558 \r
1559 char\r
1560 FileGet(void *getClosure)\r
1561 {\r
1562   int c;\r
1563   FILE* f = (FILE*) getClosure;\r
1564 \r
1565   c = getc(f);\r
1566   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1567   if (c == EOF)\r
1568     return NULLCHAR;\r
1569   else\r
1570     return (char) c;\r
1571 }\r
1572 \r
1573 /* Parse settings file named "name". If file found, return the\r
1574    full name in fullname and return TRUE; else return FALSE */\r
1575 BOOLEAN\r
1576 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1577 {\r
1578   char *dummy;\r
1579   FILE *f;\r
1580   int ok; char buf[MSG_SIZ];\r
1581 \r
1582   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1583   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1584     sprintf(buf, "%s.ini", name);\r
1585     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1586   }\r
1587   if (ok) {\r
1588     f = fopen(fullname, "r");\r
1589     if (f != NULL) {\r
1590       ParseArgs(FileGet, f);\r
1591       fclose(f);\r
1592       return TRUE;\r
1593     }\r
1594   }\r
1595   return FALSE;\r
1596 }\r
1597 \r
1598 VOID\r
1599 ParseArgs(GetFunc get, void *cl)\r
1600 {\r
1601   char argName[ARG_MAX];\r
1602   char argValue[ARG_MAX];\r
1603   ArgDescriptor *ad;\r
1604   char start;\r
1605   char *q;\r
1606   int i, octval;\r
1607   char ch;\r
1608   int posarg = 0;\r
1609 \r
1610   ch = get(cl);\r
1611   for (;;) {\r
1612     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1613     if (ch == NULLCHAR) break;\r
1614     if (ch == ';') {\r
1615       /* Comment to end of line */\r
1616       ch = get(cl);\r
1617       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1618       continue;\r
1619     } else if (ch == '/' || ch == '-') {\r
1620       /* Switch */\r
1621       q = argName;\r
1622       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1623              ch != '\n' && ch != '\t') {\r
1624         *q++ = ch;\r
1625         ch = get(cl);\r
1626       }\r
1627       *q = NULLCHAR;\r
1628 \r
1629       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1630         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1631 \r
1632       if (ad->argName == NULL)\r
1633         ExitArgError("Unrecognized argument", argName);\r
1634 \r
1635     } else if (ch == '@') {\r
1636       /* Indirection file */\r
1637       ad = &argDescriptorIndirection;\r
1638       ch = get(cl);\r
1639     } else {\r
1640       /* Positional argument */\r
1641       ad = &argDescriptors[posarg++];\r
1642       strcpy(argName, ad->argName);\r
1643     }\r
1644 \r
1645     if (ad->argType == ArgTrue) {\r
1646       *(Boolean *) ad->argLoc = TRUE;\r
1647       continue;\r
1648     }\r
1649     if (ad->argType == ArgFalse) {\r
1650       *(Boolean *) ad->argLoc = FALSE;\r
1651       continue;\r
1652     }\r
1653 \r
1654     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1655     if (ch == NULLCHAR || ch == '\n') {\r
1656       ExitArgError("No value provided for argument", argName);\r
1657     }\r
1658     q = argValue;\r
1659     if (ch == '{') {\r
1660       // Quoting with { }.  No characters have to (or can) be escaped.\r
1661       // Thus the string cannot contain a '}' character.\r
1662       start = ch;\r
1663       ch = get(cl);\r
1664       while (start) {\r
1665         switch (ch) {\r
1666         case NULLCHAR:\r
1667           start = NULLCHAR;\r
1668           break;\r
1669           \r
1670         case '}':\r
1671           ch = get(cl);\r
1672           start = NULLCHAR;\r
1673           break;\r
1674 \r
1675         default:\r
1676           *q++ = ch;\r
1677           ch = get(cl);\r
1678           break;\r
1679         }\r
1680       }   \r
1681     } else if (ch == '\'' || ch == '"') {\r
1682       // Quoting with ' ' or " ", with \ as escape character.\r
1683       // Inconvenient for long strings that may contain Windows filenames.\r
1684       start = ch;\r
1685       ch = get(cl);\r
1686       while (start) {\r
1687         switch (ch) {\r
1688         case NULLCHAR:\r
1689           start = NULLCHAR;\r
1690           break;\r
1691 \r
1692         default:\r
1693         not_special:\r
1694           *q++ = ch;\r
1695           ch = get(cl);\r
1696           break;\r
1697 \r
1698         case '\'':\r
1699         case '\"':\r
1700           if (ch == start) {\r
1701             ch = get(cl);\r
1702             start = NULLCHAR;\r
1703             break;\r
1704           } else {\r
1705             goto not_special;\r
1706           }\r
1707 \r
1708         case '\\':\r
1709           if (ad->argType == ArgFilename\r
1710               || ad->argType == ArgSettingsFilename) {\r
1711               goto not_special;\r
1712           }\r
1713           ch = get(cl);\r
1714           switch (ch) {\r
1715           case NULLCHAR:\r
1716             ExitArgError("Incomplete \\ escape in value for", argName);\r
1717             break;\r
1718           case 'n':\r
1719             *q++ = '\n';\r
1720             ch = get(cl);\r
1721             break;\r
1722           case 'r':\r
1723             *q++ = '\r';\r
1724             ch = get(cl);\r
1725             break;\r
1726           case 't':\r
1727             *q++ = '\t';\r
1728             ch = get(cl);\r
1729             break;\r
1730           case 'b':\r
1731             *q++ = '\b';\r
1732             ch = get(cl);\r
1733             break;\r
1734           case 'f':\r
1735             *q++ = '\f';\r
1736             ch = get(cl);\r
1737             break;\r
1738           default:\r
1739             octval = 0;\r
1740             for (i = 0; i < 3; i++) {\r
1741               if (ch >= '0' && ch <= '7') {\r
1742                 octval = octval*8 + (ch - '0');\r
1743                 ch = get(cl);\r
1744               } else {\r
1745                 break;\r
1746               }\r
1747             }\r
1748             if (i > 0) {\r
1749               *q++ = (char) octval;\r
1750             } else {\r
1751               *q++ = ch;\r
1752               ch = get(cl);\r
1753             }\r
1754             break;\r
1755           }\r
1756           break;\r
1757         }\r
1758       }\r
1759     } else {\r
1760       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1761         *q++ = ch;\r
1762         ch = get(cl);\r
1763       }\r
1764     }\r
1765     *q = NULLCHAR;\r
1766 \r
1767     switch (ad->argType) {\r
1768     case ArgInt:\r
1769       *(int *) ad->argLoc = atoi(argValue);\r
1770       break;\r
1771 \r
1772     case ArgX:\r
1773       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1774       break;\r
1775 \r
1776     case ArgY:\r
1777       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1778       break;\r
1779 \r
1780     case ArgZ:\r
1781       *(int *) ad->argLoc = atoi(argValue);\r
1782       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1783       break;\r
1784 \r
1785     case ArgFloat:\r
1786       *(float *) ad->argLoc = (float) atof(argValue);\r
1787       break;\r
1788 \r
1789     case ArgString:\r
1790     case ArgFilename:\r
1791       *(char **) ad->argLoc = strdup(argValue);\r
1792       break;\r
1793 \r
1794     case ArgSettingsFilename:\r
1795       {\r
1796         char fullname[MSG_SIZ];\r
1797         if (ParseSettingsFile(argValue, fullname)) {\r
1798           if (ad->argLoc != NULL) {\r
1799             *(char **) ad->argLoc = strdup(fullname);\r
1800           }\r
1801         } else {\r
1802           if (ad->argLoc != NULL) {\r
1803           } else {\r
1804             ExitArgError("Failed to open indirection file", argValue);\r
1805           }\r
1806         }\r
1807       }\r
1808       break;\r
1809 \r
1810     case ArgBoolean:\r
1811       switch (argValue[0]) {\r
1812       case 't':\r
1813       case 'T':\r
1814         *(Boolean *) ad->argLoc = TRUE;\r
1815         break;\r
1816       case 'f':\r
1817       case 'F':\r
1818         *(Boolean *) ad->argLoc = FALSE;\r
1819         break;\r
1820       default:\r
1821         ExitArgError("Unrecognized boolean argument value", argValue);\r
1822         break;\r
1823       }\r
1824       break;\r
1825 \r
1826     case ArgColor:\r
1827       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1828       break;\r
1829 \r
1830     case ArgAttribs: {\r
1831       ColorClass cc = (ColorClass)ad->argLoc;\r
1832       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1833       }\r
1834       break;\r
1835       \r
1836     case ArgBoardSize:\r
1837       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1838       break;\r
1839 \r
1840     case ArgFont:\r
1841       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1842       break;\r
1843 \r
1844     case ArgCommSettings:\r
1845       ParseCommSettings(argValue, &dcb);\r
1846       break;\r
1847 \r
1848     case ArgNone:\r
1849       ExitArgError("Unrecognized argument", argValue);\r
1850       break;\r
1851     case ArgTrue:\r
1852     case ArgFalse: ;\r
1853     }\r
1854   }\r
1855 }\r
1856 \r
1857 VOID\r
1858 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1859 {\r
1860   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1861   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1862   DeleteDC(hdc);\r
1863   lf->lfWidth = 0;\r
1864   lf->lfEscapement = 0;\r
1865   lf->lfOrientation = 0;\r
1866   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1867   lf->lfItalic = mfp->italic;\r
1868   lf->lfUnderline = mfp->underline;\r
1869   lf->lfStrikeOut = mfp->strikeout;\r
1870   lf->lfCharSet = DEFAULT_CHARSET;\r
1871   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1872   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1873   lf->lfQuality = DEFAULT_QUALITY;\r
1874   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1875   strcpy(lf->lfFaceName, mfp->faceName);\r
1876 }\r
1877 \r
1878 VOID\r
1879 CreateFontInMF(MyFont *mf)\r
1880 {\r
1881   LFfromMFP(&mf->lf, &mf->mfp);\r
1882   if (mf->hf) DeleteObject(mf->hf);\r
1883   mf->hf = CreateFontIndirect(&mf->lf);\r
1884 }\r
1885 \r
1886 VOID\r
1887 SetDefaultTextAttribs()\r
1888 {\r
1889   ColorClass cc;\r
1890   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1891     ParseAttribs(&textAttribs[cc].color, \r
1892                  &textAttribs[cc].effects, \r
1893                  defaultTextAttribs[cc]);\r
1894   }\r
1895 }\r
1896 \r
1897 VOID\r
1898 SetDefaultSounds()\r
1899 {\r
1900   ColorClass cc;\r
1901   SoundClass sc;\r
1902   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1903     textAttribs[cc].sound.name = strdup("");\r
1904     textAttribs[cc].sound.data = NULL;\r
1905   }\r
1906   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1907     sounds[sc].name = strdup("");\r
1908     sounds[sc].data = NULL;\r
1909   }\r
1910   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1911 }\r
1912 \r
1913 VOID\r
1914 LoadAllSounds()\r
1915 {\r
1916   ColorClass cc;\r
1917   SoundClass sc;\r
1918   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1919     MyLoadSound(&textAttribs[cc].sound);\r
1920   }\r
1921   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1922     MyLoadSound(&sounds[sc]);\r
1923   }\r
1924 }\r
1925 \r
1926 VOID\r
1927 InitAppData(LPSTR lpCmdLine)\r
1928 {\r
1929   int i, j;\r
1930   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1931   char *dummy, *p;\r
1932 \r
1933   programName = szAppName;\r
1934 \r
1935   /* Initialize to defaults */\r
1936   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1937   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1938   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1939   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1940   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1941   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1942   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1943   SetDefaultTextAttribs();\r
1944   SetDefaultSounds();\r
1945   appData.movesPerSession = MOVES_PER_SESSION;\r
1946   appData.initString = INIT_STRING;\r
1947   appData.secondInitString = INIT_STRING;\r
1948   appData.firstComputerString = COMPUTER_STRING;\r
1949   appData.secondComputerString = COMPUTER_STRING;\r
1950   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1951   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1952   appData.firstPlaysBlack = FALSE;\r
1953   appData.noChessProgram = FALSE;\r
1954   chessProgram = FALSE;\r
1955   appData.firstHost = FIRST_HOST;\r
1956   appData.secondHost = SECOND_HOST;\r
1957   appData.firstDirectory = FIRST_DIRECTORY;\r
1958   appData.secondDirectory = SECOND_DIRECTORY;\r
1959   appData.bitmapDirectory = "";\r
1960   appData.remoteShell = REMOTE_SHELL;\r
1961   appData.remoteUser = "";\r
1962   appData.timeDelay = TIME_DELAY;\r
1963   appData.timeControl = TIME_CONTROL;\r
1964   appData.timeIncrement = TIME_INCREMENT;\r
1965   appData.icsActive = FALSE;\r
1966   appData.icsHost = "";\r
1967   appData.icsPort = ICS_PORT;\r
1968   appData.icsCommPort = ICS_COMM_PORT;\r
1969   appData.icsLogon = ICS_LOGON;\r
1970   appData.icsHelper = "";\r
1971   appData.useTelnet = FALSE;\r
1972   appData.telnetProgram = TELNET_PROGRAM;\r
1973   appData.gateway = "";\r
1974   appData.loadGameFile = "";\r
1975   appData.loadGameIndex = 0;\r
1976   appData.saveGameFile = "";\r
1977   appData.autoSaveGames = FALSE;\r
1978   appData.loadPositionFile = "";\r
1979   appData.loadPositionIndex = 1;\r
1980   appData.savePositionFile = "";\r
1981   appData.matchMode = FALSE;\r
1982   appData.matchGames = 0;\r
1983   appData.monoMode = FALSE;\r
1984   appData.debugMode = FALSE;\r
1985   appData.clockMode = TRUE;\r
1986   boardSize = (BoardSize) -1; /* determine by screen size */\r
1987   appData.Iconic = FALSE; /*unused*/\r
1988   appData.searchTime = "";\r
1989   appData.searchDepth = 0;\r
1990   appData.showCoords = FALSE;\r
1991   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1992   appData.autoCallFlag = FALSE;\r
1993   appData.flipView = FALSE;\r
1994   appData.autoFlipView = TRUE;\r
1995   appData.cmailGameName = "";\r
1996   appData.alwaysPromoteToQueen = FALSE;\r
1997   appData.oldSaveStyle = FALSE;\r
1998   appData.quietPlay = FALSE;\r
1999   appData.showThinking = FALSE;\r
2000   appData.ponderNextMove = TRUE;\r
2001   appData.periodicUpdates = TRUE;\r
2002   appData.popupExitMessage = TRUE;\r
2003   appData.popupMoveErrors = FALSE;\r
2004   appData.autoObserve = FALSE;\r
2005   appData.autoComment = FALSE;\r
2006   appData.animate = TRUE;\r
2007   appData.animSpeed = 10;\r
2008   appData.animateDragging = TRUE;\r
2009   appData.highlightLastMove = TRUE;\r
2010   appData.getMoveList = TRUE;\r
2011   appData.testLegality = TRUE;\r
2012   appData.premove = TRUE;\r
2013   appData.premoveWhite = FALSE;\r
2014   appData.premoveWhiteText = "";\r
2015   appData.premoveBlack = FALSE;\r
2016   appData.premoveBlackText = "";\r
2017   appData.icsAlarm = TRUE;\r
2018   appData.icsAlarmTime = 5000;\r
2019   appData.autoRaiseBoard = TRUE;\r
2020   appData.localLineEditing = TRUE;\r
2021   appData.colorize = TRUE;\r
2022   appData.reuseFirst = TRUE;\r
2023   appData.reuseSecond = TRUE;\r
2024   appData.blindfold = FALSE;\r
2025   appData.icsEngineAnalyze = FALSE;\r
2026   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2027   dcb.DCBlength = sizeof(DCB);\r
2028   dcb.BaudRate = 9600;\r
2029   dcb.fBinary = TRUE;\r
2030   dcb.fParity = FALSE;\r
2031   dcb.fOutxCtsFlow = FALSE;\r
2032   dcb.fOutxDsrFlow = FALSE;\r
2033   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2034   dcb.fDsrSensitivity = FALSE;\r
2035   dcb.fTXContinueOnXoff = TRUE;\r
2036   dcb.fOutX = FALSE;\r
2037   dcb.fInX = FALSE;\r
2038   dcb.fNull = FALSE;\r
2039   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2040   dcb.fAbortOnError = FALSE;\r
2041   dcb.ByteSize = 7;\r
2042   dcb.Parity = SPACEPARITY;\r
2043   dcb.StopBits = ONESTOPBIT;\r
2044   settingsFileName = SETTINGS_FILE;\r
2045   saveSettingsOnExit = TRUE;\r
2046   boardX = CW_USEDEFAULT;\r
2047   boardY = CW_USEDEFAULT;\r
2048   analysisX = CW_USEDEFAULT; \r
2049   analysisY = CW_USEDEFAULT; \r
2050   analysisW = CW_USEDEFAULT;\r
2051   analysisH = CW_USEDEFAULT;\r
2052   commentX = CW_USEDEFAULT; \r
2053   commentY = CW_USEDEFAULT; \r
2054   commentW = CW_USEDEFAULT;\r
2055   commentH = CW_USEDEFAULT;\r
2056   editTagsX = CW_USEDEFAULT; \r
2057   editTagsY = CW_USEDEFAULT; \r
2058   editTagsW = CW_USEDEFAULT;\r
2059   editTagsH = CW_USEDEFAULT;\r
2060   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2061   icsNames = ICS_NAMES;\r
2062   firstChessProgramNames = FCP_NAMES;\r
2063   secondChessProgramNames = SCP_NAMES;\r
2064   appData.initialMode = "";\r
2065   appData.variant = "normal";\r
2066   appData.firstProtocolVersion = PROTOVER;\r
2067   appData.secondProtocolVersion = PROTOVER;\r
2068   appData.showButtonBar = TRUE;\r
2069 \r
2070    /* [AS] New properties (see comments in header file) */\r
2071   appData.firstScoreIsAbsolute = FALSE;\r
2072   appData.secondScoreIsAbsolute = FALSE;\r
2073   appData.saveExtendedInfoInPGN = FALSE;\r
2074   appData.hideThinkingFromHuman = FALSE;\r
2075   appData.liteBackTextureFile = "";\r
2076   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2077   appData.darkBackTextureFile = "";\r
2078   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2079   appData.renderPiecesWithFont = "";\r
2080   appData.fontToPieceTable = "";\r
2081   appData.fontBackColorWhite = 0;\r
2082   appData.fontForeColorWhite = 0;\r
2083   appData.fontBackColorBlack = 0;\r
2084   appData.fontForeColorBlack = 0;\r
2085   appData.fontPieceSize = 80;\r
2086   appData.overrideLineGap = 1;\r
2087   appData.adjudicateLossThreshold = 0;\r
2088   appData.delayBeforeQuit = 0;\r
2089   appData.delayAfterQuit = 0;\r
2090   appData.nameOfDebugFile = "winboard.debug";\r
2091   appData.pgnEventHeader = "Computer Chess Game";\r
2092   appData.defaultFrcPosition = -1;\r
2093   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2094   appData.saveOutOfBookInfo = TRUE;\r
2095   appData.showEvalInMoveHistory = TRUE;\r
2096   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2097   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2098   appData.highlightMoveWithArrow = FALSE;\r
2099   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2100   appData.useStickyWindows = TRUE;\r
2101   appData.adjudicateDrawMoves = 0;\r
2102   appData.autoDisplayComment = TRUE;\r
2103   appData.autoDisplayTags = TRUE;\r
2104   appData.firstIsUCI = FALSE;\r
2105   appData.secondIsUCI = FALSE;\r
2106   appData.firstHasOwnBookUCI = TRUE;\r
2107   appData.secondHasOwnBookUCI = TRUE;\r
2108   appData.polyglotDir = "";\r
2109   appData.usePolyglotBook = FALSE;\r
2110   appData.polyglotBook = "";\r
2111   appData.defaultHashSize = 64;\r
2112   appData.defaultCacheSizeEGTB = 4;\r
2113   appData.defaultPathEGTB = "c:\\egtb";\r
2114   appData.firstOptions = "";\r
2115   appData.secondOptions = "";\r
2116 \r
2117   InitWindowPlacement( &wpGameList );\r
2118   InitWindowPlacement( &wpMoveHistory );\r
2119   InitWindowPlacement( &wpEvalGraph );\r
2120   InitWindowPlacement( &wpEngineOutput );\r
2121   InitWindowPlacement( &wpConsole );\r
2122 \r
2123   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2124   appData.NrFiles      = -1;\r
2125   appData.NrRanks      = -1;\r
2126   appData.holdingsSize = -1;\r
2127   appData.testClaims   = FALSE;\r
2128   appData.checkMates   = FALSE;\r
2129   appData.materialDraws= FALSE;\r
2130   appData.trivialDraws = FALSE;\r
2131   appData.ruleMoves    = 51;\r
2132   appData.drawRepeats  = 6;\r
2133   appData.matchPause   = 10000;\r
2134   appData.alphaRank    = FALSE;\r
2135   appData.allWhite     = FALSE;\r
2136   appData.upsideDown   = FALSE;\r
2137   appData.serverPause  = 15;\r
2138   appData.serverMovesName   = NULL;\r
2139   appData.suppressLoadMoves = FALSE;\r
2140   appData.firstTimeOdds  = 1;\r
2141   appData.secondTimeOdds = 1;\r
2142   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2143   appData.secondAccumulateTC = 1;\r
2144   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2145   appData.secondNPS = -1;\r
2146   appData.engineComments = 1;\r
2147   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2148   appData.egtFormats = "";\r
2149 \r
2150 #ifdef ZIPPY\r
2151   appData.zippyTalk = ZIPPY_TALK;\r
2152   appData.zippyPlay = ZIPPY_PLAY;\r
2153   appData.zippyLines = ZIPPY_LINES;\r
2154   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2155   appData.zippyPassword = ZIPPY_PASSWORD;\r
2156   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2157   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2158   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2159   appData.zippyUseI = ZIPPY_USE_I;\r
2160   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2161   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2162   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2163   appData.zippyGameStart = ZIPPY_GAME_START;\r
2164   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2165   appData.zippyAbort = ZIPPY_ABORT;\r
2166   appData.zippyVariants = ZIPPY_VARIANTS;\r
2167   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2168   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2169 #endif\r
2170 \r
2171   /* Point font array elements to structures and\r
2172      parse default font names */\r
2173   for (i=0; i<NUM_FONTS; i++) {\r
2174     for (j=0; j<NUM_SIZES; j++) {\r
2175       font[j][i] = &fontRec[j][i];\r
2176       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2177     }\r
2178   }\r
2179   \r
2180   /* Parse default settings file if any */\r
2181   if (ParseSettingsFile(settingsFileName, buf)) {\r
2182     settingsFileName = strdup(buf);\r
2183   }\r
2184 \r
2185   /* Parse command line */\r
2186   ParseArgs(StringGet, &lpCmdLine);\r
2187 \r
2188   /* [HGM] make sure board size is acceptable */\r
2189   if(appData.NrFiles > BOARD_SIZE ||\r
2190      appData.NrRanks > BOARD_SIZE   )\r
2191       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2192 \r
2193   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2194    * with options from the command line, we now make an even higher priority\r
2195    * overrule by WB options attached to the engine command line. This so that\r
2196    * tournament managers can use WB options (such as /timeOdds) that follow\r
2197    * the engines.\r
2198    */\r
2199   if(appData.firstChessProgram != NULL) {\r
2200       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2201       static char *f = "first";\r
2202       char buf[MSG_SIZ], *q = buf;\r
2203       if(p != NULL) { // engine command line contains WinBoard options\r
2204           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2205           ParseArgs(StringGet, &q);\r
2206           p[-1] = 0; // cut them offengine command line\r
2207       }\r
2208   }\r
2209   // now do same for second chess program\r
2210   if(appData.secondChessProgram != NULL) {\r
2211       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2212       static char *s = "second";\r
2213       char buf[MSG_SIZ], *q = buf;\r
2214       if(p != NULL) { // engine command line contains WinBoard options\r
2215           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2216           ParseArgs(StringGet, &q);\r
2217           p[-1] = 0; // cut them offengine command line\r
2218       }\r
2219   }\r
2220 \r
2221 \r
2222   /* Propagate options that affect others */\r
2223   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2224   if (appData.icsActive || appData.noChessProgram) {\r
2225      chessProgram = FALSE;  /* not local chess program mode */\r
2226   }\r
2227 \r
2228   /* Open startup dialog if needed */\r
2229   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2230       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2231       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2232                         *appData.secondChessProgram == NULLCHAR))) {\r
2233     FARPROC lpProc;\r
2234     \r
2235     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2236     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2237     FreeProcInstance(lpProc);\r
2238   }\r
2239 \r
2240   /* Make sure save files land in the right (?) directory */\r
2241   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2242     appData.saveGameFile = strdup(buf);\r
2243   }\r
2244   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2245     appData.savePositionFile = strdup(buf);\r
2246   }\r
2247 \r
2248   /* Finish initialization for fonts and sounds */\r
2249   for (i=0; i<NUM_FONTS; i++) {\r
2250     for (j=0; j<NUM_SIZES; j++) {\r
2251       CreateFontInMF(font[j][i]);\r
2252     }\r
2253   }\r
2254   /* xboard, and older WinBoards, controlled the move sound with the\r
2255      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2256      always turn the option on (so that the backend will call us),\r
2257      then let the user turn the sound off by setting it to silence if\r
2258      desired.  To accommodate old winboard.ini files saved by old\r
2259      versions of WinBoard, we also turn off the sound if the option\r
2260      was initially set to false. */\r
2261   if (!appData.ringBellAfterMoves) {\r
2262     sounds[(int)SoundMove].name = strdup("");\r
2263     appData.ringBellAfterMoves = TRUE;\r
2264   }\r
2265   GetCurrentDirectory(MSG_SIZ, currDir);\r
2266   SetCurrentDirectory(installDir);\r
2267   LoadAllSounds();\r
2268   SetCurrentDirectory(currDir);\r
2269 \r
2270   p = icsTextMenuString;\r
2271   if (p[0] == '@') {\r
2272     FILE* f = fopen(p + 1, "r");\r
2273     if (f == NULL) {\r
2274       DisplayFatalError(p + 1, errno, 2);\r
2275       return;\r
2276     }\r
2277     i = fread(buf, 1, sizeof(buf)-1, f);\r
2278     fclose(f);\r
2279     buf[i] = NULLCHAR;\r
2280     p = buf;\r
2281   }\r
2282   ParseIcsTextMenu(strdup(p));\r
2283 }\r
2284 \r
2285 \r
2286 VOID\r
2287 InitMenuChecks()\r
2288 {\r
2289   HMENU hmenu = GetMenu(hwndMain);\r
2290 \r
2291   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2292                         MF_BYCOMMAND|((appData.icsActive &&\r
2293                                        *appData.icsCommPort != NULLCHAR) ?\r
2294                                       MF_ENABLED : MF_GRAYED));\r
2295   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2296                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2297                                      MF_CHECKED : MF_UNCHECKED));\r
2298 }\r
2299 \r
2300 \r
2301 VOID\r
2302 SaveSettings(char* name)\r
2303 {\r
2304   FILE *f;\r
2305   ArgDescriptor *ad;\r
2306   WINDOWPLACEMENT wp;\r
2307   char dir[MSG_SIZ];\r
2308 \r
2309   if (!hwndMain) return;\r
2310 \r
2311   GetCurrentDirectory(MSG_SIZ, dir);\r
2312   SetCurrentDirectory(installDir);\r
2313   f = fopen(name, "w");\r
2314   SetCurrentDirectory(dir);\r
2315   if (f == NULL) {\r
2316     DisplayError(name, errno);\r
2317     return;\r
2318   }\r
2319   fprintf(f, ";\n");\r
2320   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2321   fprintf(f, ";\n");\r
2322   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2323   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2324   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2325   fprintf(f, ";\n");\r
2326 \r
2327   wp.length = sizeof(WINDOWPLACEMENT);\r
2328   GetWindowPlacement(hwndMain, &wp);\r
2329   boardX = wp.rcNormalPosition.left;\r
2330   boardY = wp.rcNormalPosition.top;\r
2331 \r
2332   if (hwndConsole) {\r
2333     GetWindowPlacement(hwndConsole, &wp);\r
2334     wpConsole.x = wp.rcNormalPosition.left;\r
2335     wpConsole.y = wp.rcNormalPosition.top;\r
2336     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2337     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2338   }\r
2339 \r
2340   if (analysisDialog) {\r
2341     GetWindowPlacement(analysisDialog, &wp);\r
2342     analysisX = wp.rcNormalPosition.left;\r
2343     analysisY = wp.rcNormalPosition.top;\r
2344     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2345     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2346   }\r
2347 \r
2348   if (commentDialog) {\r
2349     GetWindowPlacement(commentDialog, &wp);\r
2350     commentX = wp.rcNormalPosition.left;\r
2351     commentY = wp.rcNormalPosition.top;\r
2352     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2353     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2354   }\r
2355 \r
2356   if (editTagsDialog) {\r
2357     GetWindowPlacement(editTagsDialog, &wp);\r
2358     editTagsX = wp.rcNormalPosition.left;\r
2359     editTagsY = wp.rcNormalPosition.top;\r
2360     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2361     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2362   }\r
2363 \r
2364   if (gameListDialog) {\r
2365     GetWindowPlacement(gameListDialog, &wp);\r
2366     wpGameList.x = wp.rcNormalPosition.left;\r
2367     wpGameList.y = wp.rcNormalPosition.top;\r
2368     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2369     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2370   }\r
2371 \r
2372   /* [AS] Move history */\r
2373   wpMoveHistory.visible = MoveHistoryIsUp();\r
2374   \r
2375   if( moveHistoryDialog ) {\r
2376     GetWindowPlacement(moveHistoryDialog, &wp);\r
2377     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2378     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2379     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2380     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2381   }\r
2382 \r
2383   /* [AS] Eval graph */\r
2384   wpEvalGraph.visible = EvalGraphIsUp();\r
2385 \r
2386   if( evalGraphDialog ) {\r
2387     GetWindowPlacement(evalGraphDialog, &wp);\r
2388     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2389     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2390     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2391     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2392   }\r
2393 \r
2394   /* [AS] Engine output */\r
2395   wpEngineOutput.visible = EngineOutputIsUp();\r
2396 \r
2397   if( engineOutputDialog ) {\r
2398     GetWindowPlacement(engineOutputDialog, &wp);\r
2399     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2400     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2401     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2402     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2403   }\r
2404 \r
2405   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2406     if (!ad->save) continue;\r
2407     switch (ad->argType) {\r
2408     case ArgString:\r
2409       {\r
2410         char *p = *(char **)ad->argLoc;\r
2411         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2412           /* Quote multiline values or \-containing values\r
2413              with { } if possible */\r
2414           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2415         } else {\r
2416           /* Else quote with " " */\r
2417           fprintf(f, "/%s=\"", ad->argName);\r
2418           while (*p) {\r
2419             if (*p == '\n') fprintf(f, "\n");\r
2420             else if (*p == '\r') fprintf(f, "\\r");\r
2421             else if (*p == '\t') fprintf(f, "\\t");\r
2422             else if (*p == '\b') fprintf(f, "\\b");\r
2423             else if (*p == '\f') fprintf(f, "\\f");\r
2424             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2425             else if (*p == '\"') fprintf(f, "\\\"");\r
2426             else if (*p == '\\') fprintf(f, "\\\\");\r
2427             else putc(*p, f);\r
2428             p++;\r
2429           }\r
2430           fprintf(f, "\"\n");\r
2431         }\r
2432       }\r
2433       break;\r
2434     case ArgInt:\r
2435     case ArgZ:\r
2436       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2437       break;\r
2438     case ArgX:\r
2439       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2440       break;\r
2441     case ArgY:\r
2442       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2443       break;\r
2444     case ArgFloat:\r
2445       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2446       break;\r
2447     case ArgBoolean:\r
2448       fprintf(f, "/%s=%s\n", ad->argName, \r
2449         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2450       break;\r
2451     case ArgTrue:\r
2452       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2453       break;\r
2454     case ArgFalse:\r
2455       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2456       break;\r
2457     case ArgColor:\r
2458       {\r
2459         COLORREF color = *(COLORREF *)ad->argLoc;\r
2460         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2461           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2462       }\r
2463       break;\r
2464     case ArgAttribs:\r
2465       {\r
2466         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2467         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2468           (ta->effects & CFE_BOLD) ? "b" : "",\r
2469           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2470           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2471           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2472           (ta->effects) ? " " : "",\r
2473           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2474       }\r
2475       break;\r
2476     case ArgFilename:\r
2477       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2478         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2479       } else {\r
2480         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2481       }\r
2482       break;\r
2483     case ArgBoardSize:\r
2484       fprintf(f, "/%s=%s\n", ad->argName,\r
2485               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2486       break;\r
2487     case ArgFont:\r
2488       {\r
2489         int bs;\r
2490         for (bs=0; bs<NUM_SIZES; bs++) {\r
2491           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2492           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2493           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2494             ad->argName, mfp->faceName, mfp->pointSize,\r
2495             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2496             mfp->bold ? "b" : "",\r
2497             mfp->italic ? "i" : "",\r
2498             mfp->underline ? "u" : "",\r
2499             mfp->strikeout ? "s" : "");\r
2500         }\r
2501       }\r
2502       break;\r
2503     case ArgCommSettings:\r
2504       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2505     case ArgNone:\r
2506     case ArgSettingsFilename: ;\r
2507     }\r
2508   }\r
2509   fclose(f);\r
2510 }\r
2511 \r
2512 \r
2513 \r
2514 /*---------------------------------------------------------------------------*\\r
2515  *\r
2516  * GDI board drawing routines\r
2517  *\r
2518 \*---------------------------------------------------------------------------*/\r
2519 \r
2520 /* [AS] Draw square using background texture */\r
2521 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2522 {\r
2523     XFORM   x;\r
2524 \r
2525     if( mode == 0 ) {\r
2526         return; /* Should never happen! */\r
2527     }\r
2528 \r
2529     SetGraphicsMode( dst, GM_ADVANCED );\r
2530 \r
2531     switch( mode ) {\r
2532     case 1:\r
2533         /* Identity */\r
2534         break;\r
2535     case 2:\r
2536         /* X reflection */\r
2537         x.eM11 = -1.0;\r
2538         x.eM12 = 0;\r
2539         x.eM21 = 0;\r
2540         x.eM22 = 1.0;\r
2541         x.eDx = (FLOAT) dw + dx - 1;\r
2542         x.eDy = 0;\r
2543         dx = 0;\r
2544         SetWorldTransform( dst, &x );\r
2545         break;\r
2546     case 3:\r
2547         /* Y reflection */\r
2548         x.eM11 = 1.0;\r
2549         x.eM12 = 0;\r
2550         x.eM21 = 0;\r
2551         x.eM22 = -1.0;\r
2552         x.eDx = 0;\r
2553         x.eDy = (FLOAT) dh + dy - 1;\r
2554         dy = 0;\r
2555         SetWorldTransform( dst, &x );\r
2556         break;\r
2557     case 4:\r
2558         /* X/Y flip */\r
2559         x.eM11 = 0;\r
2560         x.eM12 = 1.0;\r
2561         x.eM21 = 1.0;\r
2562         x.eM22 = 0;\r
2563         x.eDx = (FLOAT) dx;\r
2564         x.eDy = (FLOAT) dy;\r
2565         dx = 0;\r
2566         dy = 0;\r
2567         SetWorldTransform( dst, &x );\r
2568         break;\r
2569     }\r
2570 \r
2571     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2572 \r
2573     x.eM11 = 1.0;\r
2574     x.eM12 = 0;\r
2575     x.eM21 = 0;\r
2576     x.eM22 = 1.0;\r
2577     x.eDx = 0;\r
2578     x.eDy = 0;\r
2579     SetWorldTransform( dst, &x );\r
2580 \r
2581     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2582 }\r
2583 \r
2584 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2585 enum {\r
2586     PM_WP = (int) WhitePawn, \r
2587     PM_WN = (int) WhiteKnight, \r
2588     PM_WB = (int) WhiteBishop, \r
2589     PM_WR = (int) WhiteRook, \r
2590     PM_WQ = (int) WhiteQueen, \r
2591     PM_WF = (int) WhiteFerz, \r
2592     PM_WW = (int) WhiteWazir, \r
2593     PM_WE = (int) WhiteAlfil, \r
2594     PM_WM = (int) WhiteMan, \r
2595     PM_WO = (int) WhiteCannon, \r
2596     PM_WU = (int) WhiteUnicorn, \r
2597     PM_WH = (int) WhiteNightrider, \r
2598     PM_WA = (int) WhiteAngel, \r
2599     PM_WC = (int) WhiteMarshall, \r
2600     PM_WAB = (int) WhiteCardinal, \r
2601     PM_WD = (int) WhiteDragon, \r
2602     PM_WL = (int) WhiteLance, \r
2603     PM_WS = (int) WhiteCobra, \r
2604     PM_WV = (int) WhiteFalcon, \r
2605     PM_WSG = (int) WhiteSilver, \r
2606     PM_WG = (int) WhiteGrasshopper, \r
2607     PM_WK = (int) WhiteKing,\r
2608     PM_BP = (int) BlackPawn, \r
2609     PM_BN = (int) BlackKnight, \r
2610     PM_BB = (int) BlackBishop, \r
2611     PM_BR = (int) BlackRook, \r
2612     PM_BQ = (int) BlackQueen, \r
2613     PM_BF = (int) BlackFerz, \r
2614     PM_BW = (int) BlackWazir, \r
2615     PM_BE = (int) BlackAlfil, \r
2616     PM_BM = (int) BlackMan,\r
2617     PM_BO = (int) BlackCannon, \r
2618     PM_BU = (int) BlackUnicorn, \r
2619     PM_BH = (int) BlackNightrider, \r
2620     PM_BA = (int) BlackAngel, \r
2621     PM_BC = (int) BlackMarshall, \r
2622     PM_BG = (int) BlackGrasshopper, \r
2623     PM_BAB = (int) BlackCardinal,\r
2624     PM_BD = (int) BlackDragon,\r
2625     PM_BL = (int) BlackLance,\r
2626     PM_BS = (int) BlackCobra,\r
2627     PM_BV = (int) BlackFalcon,\r
2628     PM_BSG = (int) BlackSilver,\r
2629     PM_BK = (int) BlackKing\r
2630 };\r
2631 \r
2632 static HFONT hPieceFont = NULL;\r
2633 static HBITMAP hPieceMask[(int) EmptySquare];\r
2634 static HBITMAP hPieceFace[(int) EmptySquare];\r
2635 static int fontBitmapSquareSize = 0;\r
2636 static char pieceToFontChar[(int) EmptySquare] =\r
2637                               { 'p', 'n', 'b', 'r', 'q', \r
2638                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2639                       'k', 'o', 'm', 'v', 't', 'w', \r
2640                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2641                                                               'l' };\r
2642 \r
2643 extern BOOL SetCharTable( char *table, const char * map );\r
2644 /* [HGM] moved to backend.c */\r
2645 \r
2646 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2647 {\r
2648     HBRUSH hbrush;\r
2649     BYTE r1 = GetRValue( color );\r
2650     BYTE g1 = GetGValue( color );\r
2651     BYTE b1 = GetBValue( color );\r
2652     BYTE r2 = r1 / 2;\r
2653     BYTE g2 = g1 / 2;\r
2654     BYTE b2 = b1 / 2;\r
2655     RECT rc;\r
2656 \r
2657     /* Create a uniform background first */\r
2658     hbrush = CreateSolidBrush( color );\r
2659     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2660     FillRect( hdc, &rc, hbrush );\r
2661     DeleteObject( hbrush );\r
2662     \r
2663     if( mode == 1 ) {\r
2664         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2665         int steps = squareSize / 2;\r
2666         int i;\r
2667 \r
2668         for( i=0; i<steps; i++ ) {\r
2669             BYTE r = r1 - (r1-r2) * i / steps;\r
2670             BYTE g = g1 - (g1-g2) * i / steps;\r
2671             BYTE b = b1 - (b1-b2) * i / steps;\r
2672 \r
2673             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2674             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2675             FillRect( hdc, &rc, hbrush );\r
2676             DeleteObject(hbrush);\r
2677         }\r
2678     }\r
2679     else if( mode == 2 ) {\r
2680         /* Diagonal gradient, good more or less for every piece */\r
2681         POINT triangle[3];\r
2682         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2683         HBRUSH hbrush_old;\r
2684         int steps = squareSize;\r
2685         int i;\r
2686 \r
2687         triangle[0].x = squareSize - steps;\r
2688         triangle[0].y = squareSize;\r
2689         triangle[1].x = squareSize;\r
2690         triangle[1].y = squareSize;\r
2691         triangle[2].x = squareSize;\r
2692         triangle[2].y = squareSize - steps;\r
2693 \r
2694         for( i=0; i<steps; i++ ) {\r
2695             BYTE r = r1 - (r1-r2) * i / steps;\r
2696             BYTE g = g1 - (g1-g2) * i / steps;\r
2697             BYTE b = b1 - (b1-b2) * i / steps;\r
2698 \r
2699             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2700             hbrush_old = SelectObject( hdc, hbrush );\r
2701             Polygon( hdc, triangle, 3 );\r
2702             SelectObject( hdc, hbrush_old );\r
2703             DeleteObject(hbrush);\r
2704             triangle[0].x++;\r
2705             triangle[2].y++;\r
2706         }\r
2707 \r
2708         SelectObject( hdc, hpen );\r
2709     }\r
2710 }\r
2711 \r
2712 /*\r
2713     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2714     seems to work ok. The main problem here is to find the "inside" of a chess\r
2715     piece: follow the steps as explained below.\r
2716 */\r
2717 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2718 {\r
2719     HBITMAP hbm;\r
2720     HBITMAP hbm_old;\r
2721     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2722     RECT rc;\r
2723     SIZE sz;\r
2724     POINT pt;\r
2725     int backColor = whitePieceColor; \r
2726     int foreColor = blackPieceColor;\r
2727     \r
2728     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2729         backColor = appData.fontBackColorWhite;\r
2730         foreColor = appData.fontForeColorWhite;\r
2731     }\r
2732     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2733         backColor = appData.fontBackColorBlack;\r
2734         foreColor = appData.fontForeColorBlack;\r
2735     }\r
2736 \r
2737     /* Mask */\r
2738     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2739 \r
2740     hbm_old = SelectObject( hdc, hbm );\r
2741 \r
2742     rc.left = 0;\r
2743     rc.top = 0;\r
2744     rc.right = squareSize;\r
2745     rc.bottom = squareSize;\r
2746 \r
2747     /* Step 1: background is now black */\r
2748     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2749 \r
2750     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2751 \r
2752     pt.x = (squareSize - sz.cx) / 2;\r
2753     pt.y = (squareSize - sz.cy) / 2;\r
2754 \r
2755     SetBkMode( hdc, TRANSPARENT );\r
2756     SetTextColor( hdc, chroma );\r
2757     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2758     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2759 \r
2760     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2761     /* Step 3: the area outside the piece is filled with white */\r
2762 //    FloodFill( hdc, 0, 0, chroma );\r
2763     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2764     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2765     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2766     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2767     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2768     /* \r
2769         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2770         but if the start point is not inside the piece we're lost!\r
2771         There should be a better way to do this... if we could create a region or path\r
2772         from the fill operation we would be fine for example.\r
2773     */\r
2774 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2775     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2776 \r
2777     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2778         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2779         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2780 \r
2781         SelectObject( dc2, bm2 );\r
2782         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2783         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2784         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2785         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2786         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2787 \r
2788         DeleteDC( dc2 );\r
2789         DeleteObject( bm2 );\r
2790     }\r
2791 \r
2792     SetTextColor( hdc, 0 );\r
2793     /* \r
2794         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2795         draw the piece again in black for safety.\r
2796     */\r
2797     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2798 \r
2799     SelectObject( hdc, hbm_old );\r
2800 \r
2801     if( hPieceMask[index] != NULL ) {\r
2802         DeleteObject( hPieceMask[index] );\r
2803     }\r
2804 \r
2805     hPieceMask[index] = hbm;\r
2806 \r
2807     /* Face */\r
2808     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2809 \r
2810     SelectObject( hdc, hbm );\r
2811 \r
2812     {\r
2813         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2814         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2815         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2816 \r
2817         SelectObject( dc1, hPieceMask[index] );\r
2818         SelectObject( dc2, bm2 );\r
2819         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2820         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2821         \r
2822         /* \r
2823             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2824             the piece background and deletes (makes transparent) the rest.\r
2825             Thanks to that mask, we are free to paint the background with the greates\r
2826             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2827             We use this, to make gradients and give the pieces a "roundish" look.\r
2828         */\r
2829         SetPieceBackground( hdc, backColor, 2 );\r
2830         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2831 \r
2832         DeleteDC( dc2 );\r
2833         DeleteDC( dc1 );\r
2834         DeleteObject( bm2 );\r
2835     }\r
2836 \r
2837     SetTextColor( hdc, foreColor );\r
2838     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2839 \r
2840     SelectObject( hdc, hbm_old );\r
2841 \r
2842     if( hPieceFace[index] != NULL ) {\r
2843         DeleteObject( hPieceFace[index] );\r
2844     }\r
2845 \r
2846     hPieceFace[index] = hbm;\r
2847 }\r
2848 \r
2849 static int TranslatePieceToFontPiece( int piece )\r
2850 {\r
2851     switch( piece ) {\r
2852     case BlackPawn:\r
2853         return PM_BP;\r
2854     case BlackKnight:\r
2855         return PM_BN;\r
2856     case BlackBishop:\r
2857         return PM_BB;\r
2858     case BlackRook:\r
2859         return PM_BR;\r
2860     case BlackQueen:\r
2861         return PM_BQ;\r
2862     case BlackKing:\r
2863         return PM_BK;\r
2864     case WhitePawn:\r
2865         return PM_WP;\r
2866     case WhiteKnight:\r
2867         return PM_WN;\r
2868     case WhiteBishop:\r
2869         return PM_WB;\r
2870     case WhiteRook:\r
2871         return PM_WR;\r
2872     case WhiteQueen:\r
2873         return PM_WQ;\r
2874     case WhiteKing:\r
2875         return PM_WK;\r
2876 \r
2877     case BlackAngel:\r
2878         return PM_BA;\r
2879     case BlackMarshall:\r
2880         return PM_BC;\r
2881     case BlackFerz:\r
2882         return PM_BF;\r
2883     case BlackNightrider:\r
2884         return PM_BH;\r
2885     case BlackAlfil:\r
2886         return PM_BE;\r
2887     case BlackWazir:\r
2888         return PM_BW;\r
2889     case BlackUnicorn:\r
2890         return PM_BU;\r
2891     case BlackCannon:\r
2892         return PM_BO;\r
2893     case BlackGrasshopper:\r
2894         return PM_BG;\r
2895     case BlackMan:\r
2896         return PM_BM;\r
2897     case BlackSilver:\r
2898         return PM_BSG;\r
2899     case BlackLance:\r
2900         return PM_BL;\r
2901     case BlackFalcon:\r
2902         return PM_BV;\r
2903     case BlackCobra:\r
2904         return PM_BS;\r
2905     case BlackCardinal:\r
2906         return PM_BAB;\r
2907     case BlackDragon:\r
2908         return PM_BD;\r
2909 \r
2910     case WhiteAngel:\r
2911         return PM_WA;\r
2912     case WhiteMarshall:\r
2913         return PM_WC;\r
2914     case WhiteFerz:\r
2915         return PM_WF;\r
2916     case WhiteNightrider:\r
2917         return PM_WH;\r
2918     case WhiteAlfil:\r
2919         return PM_WE;\r
2920     case WhiteWazir:\r
2921         return PM_WW;\r
2922     case WhiteUnicorn:\r
2923         return PM_WU;\r
2924     case WhiteCannon:\r
2925         return PM_WO;\r
2926     case WhiteGrasshopper:\r
2927         return PM_WG;\r
2928     case WhiteMan:\r
2929         return PM_WM;\r
2930     case WhiteSilver:\r
2931         return PM_WSG;\r
2932     case WhiteLance:\r
2933         return PM_WL;\r
2934     case WhiteFalcon:\r
2935         return PM_WV;\r
2936     case WhiteCobra:\r
2937         return PM_WS;\r
2938     case WhiteCardinal:\r
2939         return PM_WAB;\r
2940     case WhiteDragon:\r
2941         return PM_WD;\r
2942     }\r
2943 \r
2944     return 0;\r
2945 }\r
2946 \r
2947 void CreatePiecesFromFont()\r
2948 {\r
2949     LOGFONT lf;\r
2950     HDC hdc_window = NULL;\r
2951     HDC hdc = NULL;\r
2952     HFONT hfont_old;\r
2953     int fontHeight;\r
2954     int i;\r
2955 \r
2956     if( fontBitmapSquareSize < 0 ) {\r
2957         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2958         return;\r
2959     }\r
2960 \r
2961     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2962         fontBitmapSquareSize = -1;\r
2963         return;\r
2964     }\r
2965 \r
2966     if( fontBitmapSquareSize != squareSize ) {\r
2967         hdc_window = GetDC( hwndMain );\r
2968         hdc = CreateCompatibleDC( hdc_window );\r
2969 \r
2970         if( hPieceFont != NULL ) {\r
2971             DeleteObject( hPieceFont );\r
2972         }\r
2973         else {\r
2974             for( i=0; i<=(int)BlackKing; i++ ) {\r
2975                 hPieceMask[i] = NULL;\r
2976                 hPieceFace[i] = NULL;\r
2977             }\r
2978         }\r
2979 \r
2980         fontHeight = 75;\r
2981 \r
2982         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2983             fontHeight = appData.fontPieceSize;\r
2984         }\r
2985 \r
2986         fontHeight = (fontHeight * squareSize) / 100;\r
2987 \r
2988         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2989         lf.lfWidth = 0;\r
2990         lf.lfEscapement = 0;\r
2991         lf.lfOrientation = 0;\r
2992         lf.lfWeight = FW_NORMAL;\r
2993         lf.lfItalic = 0;\r
2994         lf.lfUnderline = 0;\r
2995         lf.lfStrikeOut = 0;\r
2996         lf.lfCharSet = DEFAULT_CHARSET;\r
2997         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2998         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2999         lf.lfQuality = PROOF_QUALITY;\r
3000         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
3001         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
3002         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
3003 \r
3004         hPieceFont = CreateFontIndirect( &lf );\r
3005 \r
3006         if( hPieceFont == NULL ) {\r
3007             fontBitmapSquareSize = -2;\r
3008         }\r
3009         else {\r
3010             /* Setup font-to-piece character table */\r
3011             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
3012                 /* No (or wrong) global settings, try to detect the font */\r
3013                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
3014                     /* Alpha */\r
3015                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
3016                 }\r
3017                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3018                     /* DiagramTT* family */\r
3019                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3020                 }\r
3021                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3022                     /* Fairy symbols */\r
3023                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3024                 }\r
3025                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3026                     /* Good Companion (Some characters get warped as literal :-( */\r
3027                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3028                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3029                     SetCharTable(pieceToFontChar, s);\r
3030                 }\r
3031                 else {\r
3032                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3033                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3034                 }\r
3035             }\r
3036 \r
3037             /* Create bitmaps */\r
3038             hfont_old = SelectObject( hdc, hPieceFont );\r
3039             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3040                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3041                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3042 \r
3043             SelectObject( hdc, hfont_old );\r
3044 \r
3045             fontBitmapSquareSize = squareSize;\r
3046         }\r
3047     }\r
3048 \r
3049     if( hdc != NULL ) {\r
3050         DeleteDC( hdc );\r
3051     }\r
3052 \r
3053     if( hdc_window != NULL ) {\r
3054         ReleaseDC( hwndMain, hdc_window );\r
3055     }\r
3056 }\r
3057 \r
3058 HBITMAP\r
3059 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3060 {\r
3061   char name[128];\r
3062 \r
3063   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3064   if (gameInfo.event &&\r
3065       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3066       strcmp(name, "k80s") == 0) {\r
3067     strcpy(name, "tim");\r
3068   }\r
3069   return LoadBitmap(hinst, name);\r
3070 }\r
3071 \r
3072 \r
3073 /* Insert a color into the program's logical palette\r
3074    structure.  This code assumes the given color is\r
3075    the result of the RGB or PALETTERGB macro, and it\r
3076    knows how those macros work (which is documented).\r
3077 */\r
3078 VOID\r
3079 InsertInPalette(COLORREF color)\r
3080 {\r
3081   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3082 \r
3083   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3084     DisplayFatalError("Too many colors", 0, 1);\r
3085     pLogPal->palNumEntries--;\r
3086     return;\r
3087   }\r
3088 \r
3089   pe->peFlags = (char) 0;\r
3090   pe->peRed = (char) (0xFF & color);\r
3091   pe->peGreen = (char) (0xFF & (color >> 8));\r
3092   pe->peBlue = (char) (0xFF & (color >> 16));\r
3093   return;\r
3094 }\r
3095 \r
3096 \r
3097 VOID\r
3098 InitDrawingColors()\r
3099 {\r
3100   if (pLogPal == NULL) {\r
3101     /* Allocate enough memory for a logical palette with\r
3102      * PALETTESIZE entries and set the size and version fields\r
3103      * of the logical palette structure.\r
3104      */\r
3105     pLogPal = (NPLOGPALETTE)\r
3106       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3107                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3108     pLogPal->palVersion    = 0x300;\r
3109   }\r
3110   pLogPal->palNumEntries = 0;\r
3111 \r
3112   InsertInPalette(lightSquareColor);\r
3113   InsertInPalette(darkSquareColor);\r
3114   InsertInPalette(whitePieceColor);\r
3115   InsertInPalette(blackPieceColor);\r
3116   InsertInPalette(highlightSquareColor);\r
3117   InsertInPalette(premoveHighlightColor);\r
3118 \r
3119   /*  create a logical color palette according the information\r
3120    *  in the LOGPALETTE structure.\r
3121    */\r
3122   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3123 \r
3124   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3125   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3126   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3127   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3128   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3129   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3130   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3131   /* [AS] Force rendering of the font-based pieces */\r
3132   if( fontBitmapSquareSize > 0 ) {\r
3133     fontBitmapSquareSize = 0;\r
3134   }\r
3135 }\r
3136 \r
3137 \r
3138 int\r
3139 BoardWidth(int boardSize, int n)\r
3140 { /* [HGM] argument n added to allow different width and height */\r
3141   int lineGap = sizeInfo[boardSize].lineGap;\r
3142 \r
3143   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3144       lineGap = appData.overrideLineGap;\r
3145   }\r
3146 \r
3147   return (n + 1) * lineGap +\r
3148           n * sizeInfo[boardSize].squareSize;\r
3149 }\r
3150 \r
3151 /* Respond to board resize by dragging edge */\r
3152 VOID\r
3153 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3154 {\r
3155   BoardSize newSize = NUM_SIZES - 1;\r
3156   static int recurse = 0;\r
3157   if (IsIconic(hwndMain)) return;\r
3158   if (recurse > 0) return;\r
3159   recurse++;\r
3160   while (newSize > 0) {\r
3161         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3162         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3163            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3164     newSize--;\r
3165   } \r
3166   boardSize = newSize;\r
3167   InitDrawingSizes(boardSize, flags);\r
3168   recurse--;\r
3169 }\r
3170 \r
3171 \r
3172 \r
3173 VOID\r
3174 InitDrawingSizes(BoardSize boardSize, int flags)\r
3175 {\r
3176   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3177   ChessSquare piece;\r
3178   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3179   HDC hdc;\r
3180   SIZE clockSize, messageSize;\r
3181   HFONT oldFont;\r
3182   char buf[MSG_SIZ];\r
3183   char *str;\r
3184   HMENU hmenu = GetMenu(hwndMain);\r
3185   RECT crect, wrect, oldRect;\r
3186   int offby;\r
3187   LOGBRUSH logbrush;\r
3188 \r
3189   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3190   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3191 \r
3192   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3193   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3194 \r
3195   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3196   oldRect.top = boardY;\r
3197   oldRect.right = boardX + winWidth;\r
3198   oldRect.bottom = boardY + winHeight;\r
3199 \r
3200   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3201   smallLayout = sizeInfo[boardSize].smallLayout;\r
3202   squareSize = sizeInfo[boardSize].squareSize;\r
3203   lineGap = sizeInfo[boardSize].lineGap;\r
3204   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3205 \r
3206   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3207       lineGap = appData.overrideLineGap;\r
3208   }\r
3209 \r
3210   if (tinyLayout != oldTinyLayout) {\r
3211     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3212     if (tinyLayout) {\r
3213       style &= ~WS_SYSMENU;\r
3214       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3215                  "&Minimize\tCtrl+F4");\r
3216     } else {\r
3217       style |= WS_SYSMENU;\r
3218       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3219     }\r
3220     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3221 \r
3222     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3223       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3224         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3225     }\r
3226     DrawMenuBar(hwndMain);\r
3227   }\r
3228 \r
3229   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3230   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3231 \r
3232   /* Get text area sizes */\r
3233   hdc = GetDC(hwndMain);\r
3234   if (appData.clockMode) {\r
3235     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3236   } else {\r
3237     sprintf(buf, "White");\r
3238   }\r
3239   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3240   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3241   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3242   str = "We only care about the height here";\r
3243   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3244   SelectObject(hdc, oldFont);\r
3245   ReleaseDC(hwndMain, hdc);\r
3246 \r
3247   /* Compute where everything goes */\r
3248   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3249         /* [HGM] logo: if either logo is on, reserve space for it */\r
3250         logoHeight =  2*clockSize.cy;\r
3251         leftLogoRect.left   = OUTER_MARGIN;\r
3252         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3253         leftLogoRect.top    = OUTER_MARGIN;\r
3254         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3255 \r
3256         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3257         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3258         rightLogoRect.top    = OUTER_MARGIN;\r
3259         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3260 \r
3261 \r
3262     whiteRect.left = leftLogoRect.right;\r
3263     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3264     whiteRect.top = OUTER_MARGIN;\r
3265     whiteRect.bottom = whiteRect.top + logoHeight;\r
3266 \r
3267     blackRect.right = rightLogoRect.left;\r
3268     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3269     blackRect.top = whiteRect.top;\r
3270     blackRect.bottom = whiteRect.bottom;\r
3271   } else {\r
3272     whiteRect.left = OUTER_MARGIN;\r
3273     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3274     whiteRect.top = OUTER_MARGIN;\r
3275     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3276 \r
3277     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3278     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3279     blackRect.top = whiteRect.top;\r
3280     blackRect.bottom = whiteRect.bottom;\r
3281   }\r
3282 \r
3283   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3284   if (appData.showButtonBar) {\r
3285     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3286       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3287   } else {\r
3288     messageRect.right = OUTER_MARGIN + boardWidth;\r
3289   }\r
3290   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3291   messageRect.bottom = messageRect.top + messageSize.cy;\r
3292 \r
3293   boardRect.left = OUTER_MARGIN;\r
3294   boardRect.right = boardRect.left + boardWidth;\r
3295   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3296   boardRect.bottom = boardRect.top + boardHeight;\r
3297 \r
3298   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3299   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3300   oldBoardSize = boardSize;\r
3301   oldTinyLayout = tinyLayout;\r
3302   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3303   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3304     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3305   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3306   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3307   winHeight = winH; //       without disturbing window attachments\r
3308   GetWindowRect(hwndMain, &wrect);\r
3309   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3310                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3311 \r
3312   // [HGM] placement: let attached windows follow size change.\r
3313   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3314   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3315   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3316   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3317   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3318 \r
3319   /* compensate if menu bar wrapped */\r
3320   GetClientRect(hwndMain, &crect);\r
3321   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3322   winHeight += offby;\r
3323   switch (flags) {\r
3324   case WMSZ_TOPLEFT:\r
3325     SetWindowPos(hwndMain, NULL, \r
3326                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3327                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3328     break;\r
3329 \r
3330   case WMSZ_TOPRIGHT:\r
3331   case WMSZ_TOP:\r
3332     SetWindowPos(hwndMain, NULL, \r
3333                  wrect.left, wrect.bottom - winHeight, \r
3334                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3335     break;\r
3336 \r
3337   case WMSZ_BOTTOMLEFT:\r
3338   case WMSZ_LEFT:\r
3339     SetWindowPos(hwndMain, NULL, \r
3340                  wrect.right - winWidth, wrect.top, \r
3341                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3342     break;\r
3343 \r
3344   case WMSZ_BOTTOMRIGHT:\r
3345   case WMSZ_BOTTOM:\r
3346   case WMSZ_RIGHT:\r
3347   default:\r
3348     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3349                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3350     break;\r
3351   }\r
3352 \r
3353   hwndPause = NULL;\r
3354   for (i = 0; i < N_BUTTONS; i++) {\r
3355     if (buttonDesc[i].hwnd != NULL) {\r
3356       DestroyWindow(buttonDesc[i].hwnd);\r
3357       buttonDesc[i].hwnd = NULL;\r
3358     }\r
3359     if (appData.showButtonBar) {\r
3360       buttonDesc[i].hwnd =\r
3361         CreateWindow("BUTTON", buttonDesc[i].label,\r
3362                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3363                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3364                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3365                      (HMENU) buttonDesc[i].id,\r
3366                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3367       if (tinyLayout) {\r
3368         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3369                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3370                     MAKELPARAM(FALSE, 0));\r
3371       }\r
3372       if (buttonDesc[i].id == IDM_Pause)\r
3373         hwndPause = buttonDesc[i].hwnd;\r
3374       buttonDesc[i].wndproc = (WNDPROC)\r
3375         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3376     }\r
3377   }\r
3378   if (gridPen != NULL) DeleteObject(gridPen);\r
3379   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3380   if (premovePen != NULL) DeleteObject(premovePen);\r
3381   if (lineGap != 0) {\r
3382     logbrush.lbStyle = BS_SOLID;\r
3383     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3384     gridPen =\r
3385       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3386                    lineGap, &logbrush, 0, NULL);\r
3387     logbrush.lbColor = highlightSquareColor;\r
3388     highlightPen =\r
3389       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3390                    lineGap, &logbrush, 0, NULL);\r
3391 \r
3392     logbrush.lbColor = premoveHighlightColor; \r
3393     premovePen =\r
3394       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3395                    lineGap, &logbrush, 0, NULL);\r
3396 \r
3397     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3398     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3399       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3400       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3401         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3402       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3403         BOARD_WIDTH * (squareSize + lineGap);\r
3404       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3405     }\r
3406     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3407       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3408       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3409         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3410         lineGap / 2 + (i * (squareSize + lineGap));\r
3411       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3412         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3413       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3414     }\r
3415   }\r
3416 \r
3417   /* [HGM] Licensing requirement */\r
3418 #ifdef GOTHIC\r
3419   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3420 #endif\r
3421 #ifdef FALCON\r
3422   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3423 #endif\r
3424   GothicPopUp( "", VariantNormal);\r
3425 \r
3426 \r
3427 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3428 \r
3429   /* Load piece bitmaps for this board size */\r
3430   for (i=0; i<=2; i++) {\r
3431     for (piece = WhitePawn;\r
3432          (int) piece < (int) BlackPawn;\r
3433          piece = (ChessSquare) ((int) piece + 1)) {\r
3434       if (pieceBitmap[i][piece] != NULL)\r
3435         DeleteObject(pieceBitmap[i][piece]);\r
3436     }\r
3437   }\r
3438 \r
3439   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3440   // Orthodox Chess pieces\r
3441   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3442   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3443   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3444   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3445   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3446   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3447   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3448   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3449   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3450   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3451   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3452   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3453   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3454   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3455   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3456   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3457     // in Shogi, Hijack the unused Queen for Lance\r
3458     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3459     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3460     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3461   } else {\r
3462     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3463     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3464     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3465   }\r
3466 \r
3467   if(squareSize <= 72 && squareSize >= 33) { \r
3468     /* A & C are available in most sizes now */\r
3469     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3470       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3471       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3472       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3473       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3474       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3475       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3476       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3477       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3478       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3479       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3480       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3481       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3482     } else { // Smirf-like\r
3483       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3484       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3485       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3486     }\r
3487     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3488       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3489       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3490       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3491     } else { // WinBoard standard\r
3492       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3493       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3494       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3495     }\r
3496   }\r
3497 \r
3498 \r
3499   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3500     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3501     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3502     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3503     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3504     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3505     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3506     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3507     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3508     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3509     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3510     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3511     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3512     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3513     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3514     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3515     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3516     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3517     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3518     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3519     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3520     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3521     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3522     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3523     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3524     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3525     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3526     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3527     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3528     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3529     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3530 \r
3531     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3532       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3533       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3534       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3535       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3536       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3537       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3538       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3539       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3540       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3541       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3542       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3543       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3544     } else {\r
3545       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3546       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3547       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3548       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3549       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3550       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3551       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3552       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3553       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3554       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3555       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3556       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3557     }\r
3558 \r
3559   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3560     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3561     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3562     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3563     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3564     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3565     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3566     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3567     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3568     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3569     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3570     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3571     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3572     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3573     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3574   }\r
3575 \r
3576 \r
3577   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3578   /* special Shogi support in this size */\r
3579   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3580       for (piece = WhitePawn;\r
3581            (int) piece < (int) BlackPawn;\r
3582            piece = (ChessSquare) ((int) piece + 1)) {\r
3583         if (pieceBitmap[i][piece] != NULL)\r
3584           DeleteObject(pieceBitmap[i][piece]);\r
3585       }\r
3586     }\r
3587   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3588   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3589   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3590   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3591   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3592   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3593   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3594   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3595   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3596   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3597   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3598   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3599   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3600   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3601   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3602   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3603   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3604   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3605   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3606   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3607   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3608   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3609   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3610   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3611   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3612   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3613   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3614   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3615   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3616   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3617   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3618   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3619   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3620   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3621   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3622   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3623   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3624   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3625   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3626   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3627   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3628   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3629   minorSize = 0;\r
3630   }\r
3631 }\r
3632 \r
3633 HBITMAP\r
3634 PieceBitmap(ChessSquare p, int kind)\r
3635 {\r
3636   if ((int) p >= (int) BlackPawn)\r
3637     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3638 \r
3639   return pieceBitmap[kind][(int) p];\r
3640 }\r
3641 \r
3642 /***************************************************************/\r
3643 \r
3644 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3645 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3646 /*\r
3647 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3648 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3649 */\r
3650 \r
3651 VOID\r
3652 SquareToPos(int row, int column, int * x, int * y)\r
3653 {\r
3654   if (flipView) {\r
3655     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3656     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3657   } else {\r
3658     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3659     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3660   }\r
3661 }\r
3662 \r
3663 VOID\r
3664 DrawCoordsOnDC(HDC hdc)\r
3665 {\r
3666   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
3667   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
3668   char str[2] = { NULLCHAR, NULLCHAR };\r
3669   int oldMode, oldAlign, x, y, start, i;\r
3670   HFONT oldFont;\r
3671   HBRUSH oldBrush;\r
3672 \r
3673   if (!appData.showCoords)\r
3674     return;\r
3675 \r
3676   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3677 \r
3678   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3679   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3680   oldAlign = GetTextAlign(hdc);\r
3681   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3682 \r
3683   y = boardRect.top + lineGap;\r
3684   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3685 \r
3686   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3687   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3688     str[0] = files[start + i];\r
3689     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3690     y += squareSize + lineGap;\r
3691   }\r
3692 \r
3693   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3694 \r
3695   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3696   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3697     str[0] = ranks[start + i];\r
3698     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3699     x += squareSize + lineGap;\r
3700   }    \r
3701 \r
3702   SelectObject(hdc, oldBrush);\r
3703   SetBkMode(hdc, oldMode);\r
3704   SetTextAlign(hdc, oldAlign);\r
3705   SelectObject(hdc, oldFont);\r
3706 }\r
3707 \r
3708 VOID\r
3709 DrawGridOnDC(HDC hdc)\r
3710 {\r
3711   HPEN oldPen;\r
3712  \r
3713   if (lineGap != 0) {\r
3714     oldPen = SelectObject(hdc, gridPen);\r
3715     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3716     SelectObject(hdc, oldPen);\r
3717   }\r
3718 }\r
3719 \r
3720 #define HIGHLIGHT_PEN 0\r
3721 #define PREMOVE_PEN   1\r
3722 \r
3723 VOID\r
3724 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3725 {\r
3726   int x1, y1;\r
3727   HPEN oldPen, hPen;\r
3728   if (lineGap == 0) return;\r
3729   if (flipView) {\r
3730     x1 = boardRect.left +\r
3731       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3732     y1 = boardRect.top +\r
3733       lineGap/2 + y * (squareSize + lineGap);\r
3734   } else {\r
3735     x1 = boardRect.left +\r
3736       lineGap/2 + x * (squareSize + lineGap);\r
3737     y1 = boardRect.top +\r
3738       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3739   }\r
3740   hPen = pen ? premovePen : highlightPen;\r
3741   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3742   MoveToEx(hdc, x1, y1, NULL);\r
3743   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3744   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3745   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3746   LineTo(hdc, x1, y1);\r
3747   SelectObject(hdc, oldPen);\r
3748 }\r
3749 \r
3750 VOID\r
3751 DrawHighlightsOnDC(HDC hdc)\r
3752 {\r
3753   int i;\r
3754   for (i=0; i<2; i++) {\r
3755     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3756       DrawHighlightOnDC(hdc, TRUE,\r
3757                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3758                         HIGHLIGHT_PEN);\r
3759   }\r
3760   for (i=0; i<2; i++) {\r
3761     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3762         premoveHighlightInfo.sq[i].y >= 0) {\r
3763         DrawHighlightOnDC(hdc, TRUE,\r
3764                           premoveHighlightInfo.sq[i].x, \r
3765                           premoveHighlightInfo.sq[i].y,\r
3766                           PREMOVE_PEN);\r
3767     }\r
3768   }\r
3769 }\r
3770 \r
3771 /* Note: sqcolor is used only in monoMode */\r
3772 /* Note that this code is largely duplicated in woptions.c,\r
3773    function DrawSampleSquare, so that needs to be updated too */\r
3774 VOID\r
3775 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3776 {\r
3777   HBITMAP oldBitmap;\r
3778   HBRUSH oldBrush;\r
3779   int tmpSize;\r
3780 \r
3781   if (appData.blindfold) return;\r
3782 \r
3783   /* [AS] Use font-based pieces if needed */\r
3784   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3785     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3786     CreatePiecesFromFont();\r
3787 \r
3788     if( fontBitmapSquareSize == squareSize ) {\r
3789         int index = TranslatePieceToFontPiece(piece);\r
3790 \r
3791         SelectObject( tmphdc, hPieceMask[ index ] );\r
3792 \r
3793         BitBlt( hdc,\r
3794             x, y,\r
3795             squareSize, squareSize,\r
3796             tmphdc,\r
3797             0, 0,\r
3798             SRCAND );\r
3799 \r
3800         SelectObject( tmphdc, hPieceFace[ index ] );\r
3801 \r
3802         BitBlt( hdc,\r
3803             x, y,\r
3804             squareSize, squareSize,\r
3805             tmphdc,\r
3806             0, 0,\r
3807             SRCPAINT );\r
3808 \r
3809         return;\r
3810     }\r
3811   }\r
3812 \r
3813   if (appData.monoMode) {\r
3814     SelectObject(tmphdc, PieceBitmap(piece, \r
3815       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3816     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3817            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3818   } else {\r
3819     tmpSize = squareSize;\r
3820     if(minorSize &&\r
3821         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3822          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3823       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3824       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3825       x += (squareSize - minorSize)>>1;\r
3826       y += squareSize - minorSize - 2;\r
3827       tmpSize = minorSize;\r
3828     }\r
3829     if (color || appData.allWhite ) {\r
3830       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3831       if( color )\r
3832               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3833       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3834       if(appData.upsideDown && color==flipView)\r
3835         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3836       else\r
3837         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3838       /* Use black for outline of white pieces */\r
3839       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3840       if(appData.upsideDown && color==flipView)\r
3841         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3842       else\r
3843         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3844     } else {\r
3845       /* Use square color for details of black pieces */\r
3846       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3847       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3848       if(appData.upsideDown && !flipView)\r
3849         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3850       else\r
3851         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3852     }\r
3853     SelectObject(hdc, oldBrush);\r
3854     SelectObject(tmphdc, oldBitmap);\r
3855   }\r
3856 }\r
3857 \r
3858 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3859 int GetBackTextureMode( int algo )\r
3860 {\r
3861     int result = BACK_TEXTURE_MODE_DISABLED;\r
3862 \r
3863     switch( algo ) \r
3864     {\r
3865         case BACK_TEXTURE_MODE_PLAIN:\r
3866             result = 1; /* Always use identity map */\r
3867             break;\r
3868         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3869             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3870             break;\r
3871     }\r
3872 \r
3873     return result;\r
3874 }\r
3875 \r
3876 /* \r
3877     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3878     to handle redraws cleanly (as random numbers would always be different).\r
3879 */\r
3880 VOID RebuildTextureSquareInfo()\r
3881 {\r
3882     BITMAP bi;\r
3883     int lite_w = 0;\r
3884     int lite_h = 0;\r
3885     int dark_w = 0;\r
3886     int dark_h = 0;\r
3887     int row;\r
3888     int col;\r
3889 \r
3890     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3891 \r
3892     if( liteBackTexture != NULL ) {\r
3893         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3894             lite_w = bi.bmWidth;\r
3895             lite_h = bi.bmHeight;\r
3896         }\r
3897     }\r
3898 \r
3899     if( darkBackTexture != NULL ) {\r
3900         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3901             dark_w = bi.bmWidth;\r
3902             dark_h = bi.bmHeight;\r
3903         }\r
3904     }\r
3905 \r
3906     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3907         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3908             if( (col + row) & 1 ) {\r
3909                 /* Lite square */\r
3910                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3911                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3912                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3913                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3914                 }\r
3915             }\r
3916             else {\r
3917                 /* Dark square */\r
3918                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3919                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3920                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3921                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3922                 }\r
3923             }\r
3924         }\r
3925     }\r
3926 }\r
3927 \r
3928 /* [AS] Arrow highlighting support */\r
3929 \r
3930 static int A_WIDTH = 5; /* Width of arrow body */\r
3931 \r
3932 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3933 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3934 \r
3935 static double Sqr( double x )\r
3936 {\r
3937     return x*x;\r
3938 }\r
3939 \r
3940 static int Round( double x )\r
3941 {\r
3942     return (int) (x + 0.5);\r
3943 }\r
3944 \r
3945 /* Draw an arrow between two points using current settings */\r
3946 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3947 {\r
3948     POINT arrow[7];\r
3949     double dx, dy, j, k, x, y;\r
3950 \r
3951     if( d_x == s_x ) {\r
3952         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3953 \r
3954         arrow[0].x = s_x + A_WIDTH;\r
3955         arrow[0].y = s_y;\r
3956 \r
3957         arrow[1].x = s_x + A_WIDTH;\r
3958         arrow[1].y = d_y - h;\r
3959 \r
3960         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3961         arrow[2].y = d_y - h;\r
3962 \r
3963         arrow[3].x = d_x;\r
3964         arrow[3].y = d_y;\r
3965 \r
3966         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3967         arrow[4].y = d_y - h;\r
3968 \r
3969         arrow[5].x = s_x - A_WIDTH;\r
3970         arrow[5].y = d_y - h;\r
3971 \r
3972         arrow[6].x = s_x - A_WIDTH;\r
3973         arrow[6].y = s_y;\r
3974     }\r
3975     else if( d_y == s_y ) {\r
3976         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3977 \r
3978         arrow[0].x = s_x;\r
3979         arrow[0].y = s_y + A_WIDTH;\r
3980 \r
3981         arrow[1].x = d_x - w;\r
3982         arrow[1].y = s_y + A_WIDTH;\r
3983 \r
3984         arrow[2].x = d_x - w;\r
3985         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3986 \r
3987         arrow[3].x = d_x;\r
3988         arrow[3].y = d_y;\r
3989 \r
3990         arrow[4].x = d_x - w;\r
3991         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3992 \r
3993         arrow[5].x = d_x - w;\r
3994         arrow[5].y = s_y - A_WIDTH;\r
3995 \r
3996         arrow[6].x = s_x;\r
3997         arrow[6].y = s_y - A_WIDTH;\r
3998     }\r
3999     else {\r
4000         /* [AS] Needed a lot of paper for this! :-) */\r
4001         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4002         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4003   \r
4004         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4005 \r
4006         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4007 \r
4008         x = s_x;\r
4009         y = s_y;\r
4010 \r
4011         arrow[0].x = Round(x - j);\r
4012         arrow[0].y = Round(y + j*dx);\r
4013 \r
4014         arrow[1].x = Round(x + j);\r
4015         arrow[1].y = Round(y - j*dx);\r
4016 \r
4017         if( d_x > s_x ) {\r
4018             x = (double) d_x - k;\r
4019             y = (double) d_y - k*dy;\r
4020         }\r
4021         else {\r
4022             x = (double) d_x + k;\r
4023             y = (double) d_y + k*dy;\r
4024         }\r
4025 \r
4026         arrow[2].x = Round(x + j);\r
4027         arrow[2].y = Round(y - j*dx);\r
4028 \r
4029         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4030         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4031 \r
4032         arrow[4].x = d_x;\r
4033         arrow[4].y = d_y;\r
4034 \r
4035         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4036         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4037 \r
4038         arrow[6].x = Round(x - j);\r
4039         arrow[6].y = Round(y + j*dx);\r
4040     }\r
4041 \r
4042     Polygon( hdc, arrow, 7 );\r
4043 }\r
4044 \r
4045 /* [AS] Draw an arrow between two squares */\r
4046 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4047 {\r
4048     int s_x, s_y, d_x, d_y;\r
4049     HPEN hpen;\r
4050     HPEN holdpen;\r
4051     HBRUSH hbrush;\r
4052     HBRUSH holdbrush;\r
4053     LOGBRUSH stLB;\r
4054 \r
4055     if( s_col == d_col && s_row == d_row ) {\r
4056         return;\r
4057     }\r
4058 \r
4059     /* Get source and destination points */\r
4060     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4061     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4062 \r
4063     if( d_y > s_y ) {\r
4064         d_y += squareSize / 4;\r
4065     }\r
4066     else if( d_y < s_y ) {\r
4067         d_y += 3 * squareSize / 4;\r
4068     }\r
4069     else {\r
4070         d_y += squareSize / 2;\r
4071     }\r
4072 \r
4073     if( d_x > s_x ) {\r
4074         d_x += squareSize / 4;\r
4075     }\r
4076     else if( d_x < s_x ) {\r
4077         d_x += 3 * squareSize / 4;\r
4078     }\r
4079     else {\r
4080         d_x += squareSize / 2;\r
4081     }\r
4082 \r
4083     s_x += squareSize / 2;\r
4084     s_y += squareSize / 2;\r
4085 \r
4086     /* Adjust width */\r
4087     A_WIDTH = squareSize / 14;\r
4088 \r
4089     /* Draw */\r
4090     stLB.lbStyle = BS_SOLID;\r
4091     stLB.lbColor = appData.highlightArrowColor;\r
4092     stLB.lbHatch = 0;\r
4093 \r
4094     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4095     holdpen = SelectObject( hdc, hpen );\r
4096     hbrush = CreateBrushIndirect( &stLB );\r
4097     holdbrush = SelectObject( hdc, hbrush );\r
4098 \r
4099     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4100 \r
4101     SelectObject( hdc, holdpen );\r
4102     SelectObject( hdc, holdbrush );\r
4103     DeleteObject( hpen );\r
4104     DeleteObject( hbrush );\r
4105 }\r
4106 \r
4107 BOOL HasHighlightInfo()\r
4108 {\r
4109     BOOL result = FALSE;\r
4110 \r
4111     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4112         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4113     {\r
4114         result = TRUE;\r
4115     }\r
4116 \r
4117     return result;\r
4118 }\r
4119 \r
4120 BOOL IsDrawArrowEnabled()\r
4121 {\r
4122     BOOL result = FALSE;\r
4123 \r
4124     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4125         result = TRUE;\r
4126     }\r
4127 \r
4128     return result;\r
4129 }\r
4130 \r
4131 VOID DrawArrowHighlight( HDC hdc )\r
4132 {\r
4133     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4134         DrawArrowBetweenSquares( hdc,\r
4135             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4136             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4137     }\r
4138 }\r
4139 \r
4140 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4141 {\r
4142     HRGN result = NULL;\r
4143 \r
4144     if( HasHighlightInfo() ) {\r
4145         int x1, y1, x2, y2;\r
4146         int sx, sy, dx, dy;\r
4147 \r
4148         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4149         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4150 \r
4151         sx = MIN( x1, x2 );\r
4152         sy = MIN( y1, y2 );\r
4153         dx = MAX( x1, x2 ) + squareSize;\r
4154         dy = MAX( y1, y2 ) + squareSize;\r
4155 \r
4156         result = CreateRectRgn( sx, sy, dx, dy );\r
4157     }\r
4158 \r
4159     return result;\r
4160 }\r
4161 \r
4162 /*\r
4163     Warning: this function modifies the behavior of several other functions. \r
4164     \r
4165     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4166     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4167     repaint is scattered all over the place, which is not good for features such as\r
4168     "arrow highlighting" that require a full repaint of the board.\r
4169 \r
4170     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4171     user interaction, when speed is not so important) but especially to avoid errors\r
4172     in the displayed graphics.\r
4173 \r
4174     In such patched places, I always try refer to this function so there is a single\r
4175     place to maintain knowledge.\r
4176     \r
4177     To restore the original behavior, just return FALSE unconditionally.\r
4178 */\r
4179 BOOL IsFullRepaintPreferrable()\r
4180 {\r
4181     BOOL result = FALSE;\r
4182 \r
4183     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4184         /* Arrow may appear on the board */\r
4185         result = TRUE;\r
4186     }\r
4187 \r
4188     return result;\r
4189 }\r
4190 \r
4191 /* \r
4192     This function is called by DrawPosition to know whether a full repaint must\r
4193     be forced or not.\r
4194 \r
4195     Only DrawPosition may directly call this function, which makes use of \r
4196     some state information. Other function should call DrawPosition specifying \r
4197     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4198 */\r
4199 BOOL DrawPositionNeedsFullRepaint()\r
4200 {\r
4201     BOOL result = FALSE;\r
4202 \r
4203     /* \r
4204         Probably a slightly better policy would be to trigger a full repaint\r
4205         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4206         but animation is fast enough that it's difficult to notice.\r
4207     */\r
4208     if( animInfo.piece == EmptySquare ) {\r
4209         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4210             result = TRUE;\r
4211         }\r
4212     }\r
4213 \r
4214     return result;\r
4215 }\r
4216 \r
4217 VOID\r
4218 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4219 {\r
4220   int row, column, x, y, square_color, piece_color;\r
4221   ChessSquare piece;\r
4222   HBRUSH oldBrush;\r
4223   HDC texture_hdc = NULL;\r
4224 \r
4225   /* [AS] Initialize background textures if needed */\r
4226   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4227       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4228       if( backTextureSquareSize != squareSize \r
4229        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4230           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4231           backTextureSquareSize = squareSize;\r
4232           RebuildTextureSquareInfo();\r
4233       }\r
4234 \r
4235       texture_hdc = CreateCompatibleDC( hdc );\r
4236   }\r
4237 \r
4238   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4239     for (column = 0; column < BOARD_WIDTH; column++) {\r
4240   \r
4241       SquareToPos(row, column, &x, &y);\r
4242 \r
4243       piece = board[row][column];\r
4244 \r
4245       square_color = ((column + row) % 2) == 1;\r
4246       if( gameInfo.variant == VariantXiangqi ) {\r
4247           square_color = !InPalace(row, column);\r
4248           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4249           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4250       }\r
4251       piece_color = (int) piece < (int) BlackPawn;\r
4252 \r
4253 \r
4254       /* [HGM] holdings file: light square or black */\r
4255       if(column == BOARD_LEFT-2) {\r
4256             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4257                 square_color = 1;\r
4258             else {\r
4259                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4260                 continue;\r
4261             }\r
4262       } else\r
4263       if(column == BOARD_RGHT + 1 ) {\r
4264             if( row < gameInfo.holdingsSize )\r
4265                 square_color = 1;\r
4266             else {\r
4267                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4268                 continue;\r
4269             }\r
4270       }\r
4271       if(column == BOARD_LEFT-1 ) /* left align */\r
4272             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4273       else if( column == BOARD_RGHT) /* right align */\r
4274             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4275       else\r
4276       if (appData.monoMode) {\r
4277         if (piece == EmptySquare) {\r
4278           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4279                  square_color ? WHITENESS : BLACKNESS);\r
4280         } else {\r
4281           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4282         }\r
4283       } \r
4284       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4285           /* [AS] Draw the square using a texture bitmap */\r
4286           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4287           int r = row, c = column; // [HGM] do not flip board in flipView\r
4288           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4289 \r
4290           DrawTile( x, y, \r
4291               squareSize, squareSize, \r
4292               hdc, \r
4293               texture_hdc,\r
4294               backTextureSquareInfo[r][c].mode,\r
4295               backTextureSquareInfo[r][c].x,\r
4296               backTextureSquareInfo[r][c].y );\r
4297 \r
4298           SelectObject( texture_hdc, hbm );\r
4299 \r
4300           if (piece != EmptySquare) {\r
4301               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4302           }\r
4303       }\r
4304       else {\r
4305         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4306 \r
4307         oldBrush = SelectObject(hdc, brush );\r
4308         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4309         SelectObject(hdc, oldBrush);\r
4310         if (piece != EmptySquare)\r
4311           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4312       }\r
4313     }\r
4314   }\r
4315 \r
4316   if( texture_hdc != NULL ) {\r
4317     DeleteDC( texture_hdc );\r
4318   }\r
4319 }\r
4320 \r
4321 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4322 void fputDW(FILE *f, int x)\r
4323 {\r
4324         fputc(x     & 255, f);\r
4325         fputc(x>>8  & 255, f);\r
4326         fputc(x>>16 & 255, f);\r
4327         fputc(x>>24 & 255, f);\r
4328 }\r
4329 \r
4330 #define MAX_CLIPS 200   /* more than enough */\r
4331 \r
4332 VOID\r
4333 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4334 {\r
4335 //  HBITMAP bufferBitmap;\r
4336   BITMAP bi;\r
4337 //  RECT Rect;\r
4338   HDC tmphdc;\r
4339   HBITMAP hbm;\r
4340   int w = 100, h = 50;\r
4341 \r
4342   if(logo == NULL) return;\r
4343 //  GetClientRect(hwndMain, &Rect);\r
4344 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4345 //                                      Rect.bottom-Rect.top+1);\r
4346   tmphdc = CreateCompatibleDC(hdc);\r
4347   hbm = SelectObject(tmphdc, logo);\r
4348   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4349             w = bi.bmWidth;\r
4350             h = bi.bmHeight;\r
4351   }\r
4352   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4353                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4354   SelectObject(tmphdc, hbm);\r
4355   DeleteDC(tmphdc);\r
4356 }\r
4357 \r
4358 VOID\r
4359 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4360 {\r
4361   static Board lastReq, lastDrawn;\r
4362   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4363   static int lastDrawnFlipView = 0;\r
4364   static int lastReqValid = 0, lastDrawnValid = 0;\r
4365   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4366   HDC tmphdc;\r
4367   HDC hdcmem;\r
4368   HBITMAP bufferBitmap;\r
4369   HBITMAP oldBitmap;\r
4370   RECT Rect;\r
4371   HRGN clips[MAX_CLIPS];\r
4372   ChessSquare dragged_piece = EmptySquare;\r
4373 \r
4374   /* I'm undecided on this - this function figures out whether a full\r
4375    * repaint is necessary on its own, so there's no real reason to have the\r
4376    * caller tell it that.  I think this can safely be set to FALSE - but\r
4377    * if we trust the callers not to request full repaints unnessesarily, then\r
4378    * we could skip some clipping work.  In other words, only request a full\r
4379    * redraw when the majority of pieces have changed positions (ie. flip, \r
4380    * gamestart and similar)  --Hawk\r
4381    */\r
4382   Boolean fullrepaint = repaint;\r
4383 \r
4384   if( DrawPositionNeedsFullRepaint() ) {\r
4385       fullrepaint = TRUE;\r
4386   }\r
4387 \r
4388   if (board == NULL) {\r
4389     if (!lastReqValid) {\r
4390       return;\r
4391     }\r
4392     board = lastReq;\r
4393   } else {\r
4394     CopyBoard(lastReq, board);\r
4395     lastReqValid = 1;\r
4396   }\r
4397 \r
4398   if (doingSizing) {\r
4399     return;\r
4400   }\r
4401 \r
4402   if (IsIconic(hwndMain)) {\r
4403     return;\r
4404   }\r
4405 \r
4406   if (hdc == NULL) {\r
4407     hdc = GetDC(hwndMain);\r
4408     if (!appData.monoMode) {\r
4409       SelectPalette(hdc, hPal, FALSE);\r
4410       RealizePalette(hdc);\r
4411     }\r
4412     releaseDC = TRUE;\r
4413   } else {\r
4414     releaseDC = FALSE;\r
4415   }\r
4416 \r
4417   /* Create some work-DCs */\r
4418   hdcmem = CreateCompatibleDC(hdc);\r
4419   tmphdc = CreateCompatibleDC(hdc);\r
4420 \r
4421   /* If dragging is in progress, we temporarely remove the piece */\r
4422   /* [HGM] or temporarily decrease count if stacked              */\r
4423   /*       !! Moved to before board compare !!                   */\r
4424   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4425     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4426     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4427             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4428         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4429     } else \r
4430     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4431             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4432         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4433     } else \r
4434         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4435   }\r
4436 \r
4437   /* Figure out which squares need updating by comparing the \r
4438    * newest board with the last drawn board and checking if\r
4439    * flipping has changed.\r
4440    */\r
4441   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4442     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4443       for (column = 0; column < BOARD_WIDTH; column++) {\r
4444         if (lastDrawn[row][column] != board[row][column]) {\r
4445           SquareToPos(row, column, &x, &y);\r
4446           clips[num_clips++] =\r
4447             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4448         }\r
4449       }\r
4450     }\r
4451     for (i=0; i<2; i++) {\r
4452       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4453           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4454         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4455             lastDrawnHighlight.sq[i].y >= 0) {\r
4456           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4457                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4458           clips[num_clips++] =\r
4459             CreateRectRgn(x - lineGap, y - lineGap, \r
4460                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4461         }\r
4462         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4463           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4464           clips[num_clips++] =\r
4465             CreateRectRgn(x - lineGap, y - lineGap, \r
4466                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4467         }\r
4468       }\r
4469     }\r
4470     for (i=0; i<2; i++) {\r
4471       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4472           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4473         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4474             lastDrawnPremove.sq[i].y >= 0) {\r
4475           SquareToPos(lastDrawnPremove.sq[i].y,\r
4476                       lastDrawnPremove.sq[i].x, &x, &y);\r
4477           clips[num_clips++] =\r
4478             CreateRectRgn(x - lineGap, y - lineGap, \r
4479                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4480         }\r
4481         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4482             premoveHighlightInfo.sq[i].y >= 0) {\r
4483           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4484                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4485           clips[num_clips++] =\r
4486             CreateRectRgn(x - lineGap, y - lineGap, \r
4487                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4488         }\r
4489       }\r
4490     }\r
4491   } else {\r
4492     fullrepaint = TRUE;\r
4493   }\r
4494 \r
4495   /* Create a buffer bitmap - this is the actual bitmap\r
4496    * being written to.  When all the work is done, we can\r
4497    * copy it to the real DC (the screen).  This avoids\r
4498    * the problems with flickering.\r
4499    */\r
4500   GetClientRect(hwndMain, &Rect);\r
4501   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4502                                         Rect.bottom-Rect.top+1);\r
4503   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4504   if (!appData.monoMode) {\r
4505     SelectPalette(hdcmem, hPal, FALSE);\r
4506   }\r
4507 \r
4508   /* Create clips for dragging */\r
4509   if (!fullrepaint) {\r
4510     if (dragInfo.from.x >= 0) {\r
4511       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4512       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4513     }\r
4514     if (dragInfo.start.x >= 0) {\r
4515       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4516       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4517     }\r
4518     if (dragInfo.pos.x >= 0) {\r
4519       x = dragInfo.pos.x - squareSize / 2;\r
4520       y = dragInfo.pos.y - squareSize / 2;\r
4521       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4522     }\r
4523     if (dragInfo.lastpos.x >= 0) {\r
4524       x = dragInfo.lastpos.x - squareSize / 2;\r
4525       y = dragInfo.lastpos.y - squareSize / 2;\r
4526       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4527     }\r
4528   }\r
4529 \r
4530   /* Are we animating a move?  \r
4531    * If so, \r
4532    *   - remove the piece from the board (temporarely)\r
4533    *   - calculate the clipping region\r
4534    */\r
4535   if (!fullrepaint) {\r
4536     if (animInfo.piece != EmptySquare) {\r
4537       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4538       x = boardRect.left + animInfo.lastpos.x;\r
4539       y = boardRect.top + animInfo.lastpos.y;\r
4540       x2 = boardRect.left + animInfo.pos.x;\r
4541       y2 = boardRect.top + animInfo.pos.y;\r
4542       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4543       /* Slight kludge.  The real problem is that after AnimateMove is\r
4544          done, the position on the screen does not match lastDrawn.\r
4545          This currently causes trouble only on e.p. captures in\r
4546          atomic, where the piece moves to an empty square and then\r
4547          explodes.  The old and new positions both had an empty square\r
4548          at the destination, but animation has drawn a piece there and\r
4549          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4550       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4551     }\r
4552   }\r
4553 \r
4554   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4555   if (num_clips == 0)\r
4556     fullrepaint = TRUE;\r
4557 \r
4558   /* Set clipping on the memory DC */\r
4559   if (!fullrepaint) {\r
4560     SelectClipRgn(hdcmem, clips[0]);\r
4561     for (x = 1; x < num_clips; x++) {\r
4562       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4563         abort();  // this should never ever happen!\r
4564     }\r
4565   }\r
4566 \r
4567   /* Do all the drawing to the memory DC */\r
4568   if(explodeInfo.radius) { // [HGM] atomic\r
4569         HBRUSH oldBrush;\r
4570         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4571         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4572         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4573         x += squareSize/2;\r
4574         y += squareSize/2;\r
4575         if(!fullrepaint) {\r
4576           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4577           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4578         }\r
4579         DrawGridOnDC(hdcmem);\r
4580         DrawHighlightsOnDC(hdcmem);\r
4581         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4582         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4583         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4584         SelectObject(hdcmem, oldBrush);\r
4585   } else {\r
4586     DrawGridOnDC(hdcmem);\r
4587     DrawHighlightsOnDC(hdcmem);\r
4588     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4589   }\r
4590   if(logoHeight) {\r
4591         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4592         if(appData.autoLogo) {\r
4593           \r
4594           switch(gameMode) { // pick logos based on game mode\r
4595             case IcsObserving:\r
4596                 whiteLogo = second.programLogo; // ICS logo\r
4597                 blackLogo = second.programLogo;\r
4598             default:\r
4599                 break;\r
4600             case IcsPlayingWhite:\r
4601                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4602                 blackLogo = second.programLogo; // ICS logo\r
4603                 break;\r
4604             case IcsPlayingBlack:\r
4605                 whiteLogo = second.programLogo; // ICS logo\r
4606                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4607                 break;\r
4608             case TwoMachinesPlay:\r
4609                 if(first.twoMachinesColor[0] == 'b') {\r
4610                     whiteLogo = second.programLogo;\r
4611                     blackLogo = first.programLogo;\r
4612                 }\r
4613                 break;\r
4614             case MachinePlaysWhite:\r
4615                 blackLogo = userLogo;\r
4616                 break;\r
4617             case MachinePlaysBlack:\r
4618                 whiteLogo = userLogo;\r
4619                 blackLogo = first.programLogo;\r
4620           }\r
4621         }\r
4622         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4623         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4624   }\r
4625 \r
4626   if( appData.highlightMoveWithArrow ) {\r
4627     DrawArrowHighlight(hdcmem);\r
4628   }\r
4629 \r
4630   DrawCoordsOnDC(hdcmem);\r
4631 \r
4632   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4633                  /* to make sure lastDrawn contains what is actually drawn */\r
4634 \r
4635   /* Put the dragged piece back into place and draw it (out of place!) */\r
4636     if (dragged_piece != EmptySquare) {\r
4637     /* [HGM] or restack */\r
4638     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4639                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4640     else\r
4641     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4642                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4643     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4644     x = dragInfo.pos.x - squareSize / 2;\r
4645     y = dragInfo.pos.y - squareSize / 2;\r
4646     DrawPieceOnDC(hdcmem, dragged_piece,\r
4647                   ((int) dragged_piece < (int) BlackPawn), \r
4648                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4649   }   \r
4650   \r
4651   /* Put the animated piece back into place and draw it */\r
4652   if (animInfo.piece != EmptySquare) {\r
4653     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4654     x = boardRect.left + animInfo.pos.x;\r
4655     y = boardRect.top + animInfo.pos.y;\r
4656     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4657                   ((int) animInfo.piece < (int) BlackPawn),\r
4658                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4659   }\r
4660 \r
4661   /* Release the bufferBitmap by selecting in the old bitmap \r
4662    * and delete the memory DC\r
4663    */\r
4664   SelectObject(hdcmem, oldBitmap);\r
4665   DeleteDC(hdcmem);\r
4666 \r
4667   /* Set clipping on the target DC */\r
4668   if (!fullrepaint) {\r
4669     SelectClipRgn(hdc, clips[0]);\r
4670     for (x = 1; x < num_clips; x++) {\r
4671       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4672         abort();   // this should never ever happen!\r
4673     } \r
4674   }\r
4675 \r
4676   /* Copy the new bitmap onto the screen in one go.\r
4677    * This way we avoid any flickering\r
4678    */\r
4679   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4680   BitBlt(hdc, boardRect.left, boardRect.top,\r
4681          boardRect.right - boardRect.left,\r
4682          boardRect.bottom - boardRect.top,\r
4683          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4684   if(saveDiagFlag) { \r
4685     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4686     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4687 \r
4688     GetObject(bufferBitmap, sizeof(b), &b);\r
4689     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4690         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4691         bih.biWidth = b.bmWidth;\r
4692         bih.biHeight = b.bmHeight;\r
4693         bih.biPlanes = 1;\r
4694         bih.biBitCount = b.bmBitsPixel;\r
4695         bih.biCompression = 0;\r
4696         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4697         bih.biXPelsPerMeter = 0;\r
4698         bih.biYPelsPerMeter = 0;\r
4699         bih.biClrUsed = 0;\r
4700         bih.biClrImportant = 0;\r
4701 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4702 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4703         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4704 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4705 \r
4706         wb = b.bmWidthBytes;\r
4707         // count colors\r
4708         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4709                 int k = ((int*) pData)[i];\r
4710                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4711                 if(j >= 16) break;\r
4712                 color[j] = k;\r
4713                 if(j >= nrColors) nrColors = j+1;\r
4714         }\r
4715         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4716                 INT p = 0;\r
4717                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4718                     for(w=0; w<(wb>>2); w+=2) {\r
4719                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4720                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4721                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4722                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4723                         pData[p++] = m | j<<4;\r
4724                     }\r
4725                     while(p&3) pData[p++] = 0;\r
4726                 }\r
4727                 fac = 3;\r
4728                 wb = ((wb+31)>>5)<<2;\r
4729         }\r
4730         // write BITMAPFILEHEADER\r
4731         fprintf(diagFile, "BM");\r
4732         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4733         fputDW(diagFile, 0);\r
4734         fputDW(diagFile, 0x36 + (fac?64:0));\r
4735         // write BITMAPINFOHEADER\r
4736         fputDW(diagFile, 40);\r
4737         fputDW(diagFile, b.bmWidth);\r
4738         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4739         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4740         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4741         fputDW(diagFile, 0);\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         // write color table\r
4748         if(fac)\r
4749         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4750         // write bitmap data\r
4751         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4752                 fputc(pData[i], diagFile);\r
4753      }\r
4754   }\r
4755 \r
4756   SelectObject(tmphdc, oldBitmap);\r
4757 \r
4758   /* Massive cleanup */\r
4759   for (x = 0; x < num_clips; x++)\r
4760     DeleteObject(clips[x]);\r
4761 \r
4762   DeleteDC(tmphdc);\r
4763   DeleteObject(bufferBitmap);\r
4764 \r
4765   if (releaseDC) \r
4766     ReleaseDC(hwndMain, hdc);\r
4767   \r
4768   if (lastDrawnFlipView != flipView) {\r
4769     if (flipView)\r
4770       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4771     else\r
4772       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4773   }\r
4774 \r
4775 /*  CopyBoard(lastDrawn, board);*/\r
4776   lastDrawnHighlight = highlightInfo;\r
4777   lastDrawnPremove   = premoveHighlightInfo;\r
4778   lastDrawnFlipView = flipView;\r
4779   lastDrawnValid = 1;\r
4780 }\r
4781 \r
4782 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4783 int\r
4784 SaveDiagram(f)\r
4785      FILE *f;\r
4786 {\r
4787     saveDiagFlag = 1; diagFile = f;\r
4788     HDCDrawPosition(NULL, TRUE, NULL);\r
4789 \r
4790     saveDiagFlag = 0;\r
4791 \r
4792 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4793     \r
4794     fclose(f);\r
4795     return TRUE;\r
4796 }\r
4797 \r
4798 \r
4799 /*---------------------------------------------------------------------------*\\r
4800 | CLIENT PAINT PROCEDURE\r
4801 |   This is the main event-handler for the WM_PAINT message.\r
4802 |\r
4803 \*---------------------------------------------------------------------------*/\r
4804 VOID\r
4805 PaintProc(HWND hwnd)\r
4806 {\r
4807   HDC         hdc;\r
4808   PAINTSTRUCT ps;\r
4809   HFONT       oldFont;\r
4810 \r
4811   if((hdc = BeginPaint(hwnd, &ps))) {\r
4812     if (IsIconic(hwnd)) {\r
4813       DrawIcon(hdc, 2, 2, iconCurrent);\r
4814     } else {\r
4815       if (!appData.monoMode) {\r
4816         SelectPalette(hdc, hPal, FALSE);\r
4817         RealizePalette(hdc);\r
4818       }\r
4819       HDCDrawPosition(hdc, 1, NULL);\r
4820       oldFont =\r
4821         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4822       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4823                  ETO_CLIPPED|ETO_OPAQUE,\r
4824                  &messageRect, messageText, strlen(messageText), NULL);\r
4825       SelectObject(hdc, oldFont);\r
4826       DisplayBothClocks();\r
4827     }\r
4828     EndPaint(hwnd,&ps);\r
4829   }\r
4830 \r
4831   return;\r
4832 }\r
4833 \r
4834 \r
4835 /*\r
4836  * If the user selects on a border boundary, return -1; if off the board,\r
4837  *   return -2.  Otherwise map the event coordinate to the square.\r
4838  * The offset boardRect.left or boardRect.top must already have been\r
4839  *   subtracted from x.\r
4840  */\r
4841 int\r
4842 EventToSquare(int x)\r
4843 {\r
4844   if (x <= 0)\r
4845     return -2;\r
4846   if (x < lineGap)\r
4847     return -1;\r
4848   x -= lineGap;\r
4849   if ((x % (squareSize + lineGap)) >= squareSize)\r
4850     return -1;\r
4851   x /= (squareSize + lineGap);\r
4852   if (x >= BOARD_SIZE)\r
4853     return -2;\r
4854   return x;\r
4855 }\r
4856 \r
4857 typedef struct {\r
4858   char piece;\r
4859   int command;\r
4860   char* name;\r
4861 } DropEnable;\r
4862 \r
4863 DropEnable dropEnables[] = {\r
4864   { 'P', DP_Pawn, "Pawn" },\r
4865   { 'N', DP_Knight, "Knight" },\r
4866   { 'B', DP_Bishop, "Bishop" },\r
4867   { 'R', DP_Rook, "Rook" },\r
4868   { 'Q', DP_Queen, "Queen" },\r
4869 };\r
4870 \r
4871 VOID\r
4872 SetupDropMenu(HMENU hmenu)\r
4873 {\r
4874   int i, count, enable;\r
4875   char *p;\r
4876   extern char white_holding[], black_holding[];\r
4877   char item[MSG_SIZ];\r
4878 \r
4879   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4880     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4881                dropEnables[i].piece);\r
4882     count = 0;\r
4883     while (p && *p++ == dropEnables[i].piece) count++;\r
4884     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4885     enable = count > 0 || !appData.testLegality\r
4886       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4887                       && !appData.icsActive);\r
4888     ModifyMenu(hmenu, dropEnables[i].command,\r
4889                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4890                dropEnables[i].command, item);\r
4891   }\r
4892 }\r
4893 \r
4894 /* Event handler for mouse messages */\r
4895 VOID\r
4896 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4897 {\r
4898   int x, y;\r
4899   POINT pt;\r
4900   static int recursive = 0;\r
4901   HMENU hmenu;\r
4902 //  BOOLEAN needsRedraw = FALSE;\r
4903   BOOLEAN saveAnimate;\r
4904   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4905   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4906   ChessMove moveType;\r
4907 \r
4908   if (recursive) {\r
4909     if (message == WM_MBUTTONUP) {\r
4910       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4911          to the middle button: we simulate pressing the left button too!\r
4912          */\r
4913       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4914       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4915     }\r
4916     return;\r
4917   }\r
4918   recursive++;\r
4919   \r
4920   pt.x = LOWORD(lParam);\r
4921   pt.y = HIWORD(lParam);\r
4922   x = EventToSquare(pt.x - boardRect.left);\r
4923   y = EventToSquare(pt.y - boardRect.top);\r
4924   if (!flipView && y >= 0) {\r
4925     y = BOARD_HEIGHT - 1 - y;\r
4926   }\r
4927   if (flipView && x >= 0) {\r
4928     x = BOARD_WIDTH - 1 - x;\r
4929   }\r
4930 \r
4931   switch (message) {\r
4932   case WM_LBUTTONDOWN:\r
4933     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4934         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4935         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4936         if(gameInfo.holdingsWidth && \r
4937                 (WhiteOnMove(currentMove) \r
4938                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4939                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4940             // click in right holdings, for determining promotion piece\r
4941             ChessSquare p = boards[currentMove][y][x];\r
4942             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4943             if(p != EmptySquare) {\r
4944                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4945                 fromX = fromY = -1;\r
4946                 break;\r
4947             }\r
4948         }\r
4949         DrawPosition(FALSE, boards[currentMove]);\r
4950         break;\r
4951     }\r
4952     ErrorPopDown();\r
4953     sameAgain = FALSE;\r
4954     if (y == -2) {\r
4955       /* Downclick vertically off board; check if on clock */\r
4956       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4957         if (gameMode == EditPosition) {\r
4958           SetWhiteToPlayEvent();\r
4959         } else if (gameMode == IcsPlayingBlack ||\r
4960                    gameMode == MachinePlaysWhite) {\r
4961           CallFlagEvent();\r
4962         } else if (gameMode == EditGame) {\r
4963           AdjustClock(flipClock, -1);\r
4964         }\r
4965       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4966         if (gameMode == EditPosition) {\r
4967           SetBlackToPlayEvent();\r
4968         } else if (gameMode == IcsPlayingWhite ||\r
4969                    gameMode == MachinePlaysBlack) {\r
4970           CallFlagEvent();\r
4971         } else if (gameMode == EditGame) {\r
4972           AdjustClock(!flipClock, -1);\r
4973         }\r
4974       }\r
4975       if (!appData.highlightLastMove) {\r
4976         ClearHighlights();\r
4977         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4978       }\r
4979       fromX = fromY = -1;\r
4980       dragInfo.start.x = dragInfo.start.y = -1;\r
4981       dragInfo.from = dragInfo.start;\r
4982       break;\r
4983     } else if (x < 0 || y < 0\r
4984       /* [HGM] block clicks between board and holdings */\r
4985               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4986               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4987               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4988         /* EditPosition, empty square, or different color piece;\r
4989            click-click move is possible */\r
4990                                ) {\r
4991       break;\r
4992     } else if (fromX == x && fromY == y) {\r
4993       /* Downclick on same square again */\r
4994       ClearHighlights();\r
4995       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4996       sameAgain = TRUE;  \r
4997     } else if (fromX != -1 &&\r
4998                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4999                                                                         ) {\r
5000       /* Downclick on different square. */\r
5001       /* [HGM] if on holdings file, should count as new first click ! */\r
5002       /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5003         toX = x;\r
5004         toY = y;\r
5005         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5006            to make sure move is legal before showing promotion popup */\r
5007         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR, FALSE);\r
5008         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5009                 fromX = fromY = -1; \r
5010                 ClearHighlights();\r
5011                 DrawPosition(FALSE, boards[currentMove]);\r
5012                 break; \r
5013         } else \r
5014         if(moveType != ImpossibleMove && moveType != Comment) {\r
5015           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5016           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5017             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5018               appData.alwaysPromoteToQueen)) {\r
5019                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5020                   if (!appData.highlightLastMove) {\r
5021                       ClearHighlights();\r
5022                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5023                   }\r
5024           } else\r
5025           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5026                   SetHighlights(fromX, fromY, toX, toY);\r
5027                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5028                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5029                      If promotion to Q is legal, all are legal! */\r
5030                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5031                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5032                     // kludge to temporarily execute move on display, without promoting yet\r
5033                     promotionChoice = TRUE;\r
5034                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5035                     boards[currentMove][toY][toX] = p;\r
5036                     DrawPosition(FALSE, boards[currentMove]);\r
5037                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5038                     boards[currentMove][toY][toX] = q;\r
5039                     DisplayMessage("Select piece from holdings", "");\r
5040                   } else\r
5041                   PromotionPopup(hwnd);\r
5042                   goto noClear;\r
5043           } else { // not a promotion. Move can be illegal if testLegality off, and should be made then.\r
5044              if (appData.animate || appData.highlightLastMove) {\r
5045                  SetHighlights(fromX, fromY, toX, toY);\r
5046              } else {\r
5047                  ClearHighlights();\r
5048              }\r
5049              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5050              if (appData.animate && !appData.highlightLastMove) {\r
5051                   ClearHighlights();\r
5052                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5053              }\r
5054           }\r
5055           fromX = fromY = -1;\r
5056         noClear:\r
5057           break;\r
5058         }\r
5059         if (gotPremove && moveType != Comment) {\r
5060             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5061 //            DrawPosition(forceFullRepaint || FALSE, NULL);\r
5062         } else ClearHighlights();\r
5063         fromX = fromY = -1;\r
5064         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5065         if(moveType != Comment) break;\r
5066     }\r
5067     /* First downclick, or restart on a square with same color piece */\r
5068     if (!frozen && OKToStartUserMove(x, y)) {\r
5069       fromX = x;\r
5070       fromY = y;\r
5071       dragInfo.lastpos = pt;\r
5072       dragInfo.from.x = fromX;\r
5073       dragInfo.from.y = fromY;\r
5074       dragInfo.start = dragInfo.from;\r
5075       SetCapture(hwndMain);\r
5076     } else {\r
5077       fromX = fromY = -1;\r
5078       dragInfo.start.x = dragInfo.start.y = -1;\r
5079       dragInfo.from = dragInfo.start;\r
5080       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5081     }\r
5082     break;\r
5083 \r
5084   case WM_LBUTTONUP:\r
5085     ReleaseCapture();\r
5086     if (fromX == -1) break;\r
5087     if (x == fromX && y == fromY) {\r
5088       dragInfo.from.x = dragInfo.from.y = -1;\r
5089       /* Upclick on same square */\r
5090       if (sameAgain) {\r
5091         /* Clicked same square twice: abort click-click move */\r
5092         fromX = fromY = -1;\r
5093         gotPremove = 0;\r
5094         ClearPremoveHighlights();\r
5095       } else {\r
5096         /* First square clicked: start click-click move */\r
5097         SetHighlights(fromX, fromY, -1, -1);\r
5098       }\r
5099       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5100     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5101       /* Errant click; ignore */\r
5102       break;\r
5103     } else {\r
5104       /* Finish drag move. */\r
5105     if (appData.debugMode) {\r
5106         fprintf(debugFP, "release\n");\r
5107     }\r
5108       dragInfo.from.x = dragInfo.from.y = -1;\r
5109       toX = x;\r
5110       toY = y;\r
5111       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5112       appData.animate = appData.animate && !appData.animateDragging;\r
5113       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR, TRUE);\r
5114       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5115                 fromX = fromY = -1; \r
5116                 ClearHighlights();\r
5117                 DrawPosition(FALSE, boards[currentMove]);\r
5118                 appData.animate = saveAnimate;\r
5119                 break; \r
5120       } else \r
5121       if(moveType != ImpossibleMove) {\r
5122           /* [HGM] use move type to determine if move is promotion.\r
5123              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5124           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5125             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5126               appData.alwaysPromoteToQueen)) \r
5127                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5128           else \r
5129           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5130                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5131                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5132                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5133                     // kludge to temporarily execute move on display, wthout promotng yet\r
5134                     promotionChoice = TRUE;\r
5135                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5136                     boards[currentMove][toY][toX] = p;\r
5137                     DrawPosition(FALSE, boards[currentMove]);\r
5138                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5139                     boards[currentMove][toY][toX] = q;\r
5140                     appData.animate = saveAnimate;\r
5141                     DisplayMessage("Select piece from holdings", "");\r
5142                     break;\r
5143                   } else\r
5144                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5145           } else {\r
5146             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5147                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5148                                         moveType == WhiteCapturesEnPassant || \r
5149                                         moveType == BlackCapturesEnPassant   ) )\r
5150                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5151             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5152           }\r
5153       }\r
5154       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5155       appData.animate = saveAnimate;\r
5156       fromX = fromY = -1;\r
5157       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5158         ClearHighlights();\r
5159       }\r
5160       if (appData.animate || appData.animateDragging ||\r
5161           appData.highlightDragging || gotPremove) {\r
5162         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5163       }\r
5164     }\r
5165     dragInfo.start.x = dragInfo.start.y = -1; \r
5166     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5167     break;\r
5168 \r
5169   case WM_MOUSEMOVE:\r
5170     if ((appData.animateDragging || appData.highlightDragging)\r
5171         && (wParam & MK_LBUTTON)\r
5172         && dragInfo.from.x >= 0) \r
5173     {\r
5174       BOOL full_repaint = FALSE;\r
5175 \r
5176       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5177       if (appData.animateDragging) {\r
5178         dragInfo.pos = pt;\r
5179       }\r
5180       if (appData.highlightDragging) {\r
5181         SetHighlights(fromX, fromY, x, y);\r
5182         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5183             full_repaint = TRUE;\r
5184         }\r
5185       }\r
5186       \r
5187       DrawPosition( full_repaint, NULL);\r
5188       \r
5189       dragInfo.lastpos = dragInfo.pos;\r
5190     }\r
5191     break;\r
5192 \r
5193   case WM_MOUSEWHEEL: // [DM]\r
5194     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5195        /* Mouse Wheel is being rolled forward\r
5196         * Play moves forward\r
5197         */\r
5198        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5199                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5200        /* Mouse Wheel is being rolled backward\r
5201         * Play moves backward\r
5202         */\r
5203        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5204                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5205     }\r
5206     break;\r
5207 \r
5208   case WM_MBUTTONDOWN:\r
5209   case WM_RBUTTONDOWN:\r
5210     ErrorPopDown();\r
5211     ReleaseCapture();\r
5212     fromX = fromY = -1;\r
5213     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5214     dragInfo.start.x = dragInfo.start.y = -1;\r
5215     dragInfo.from = dragInfo.start;\r
5216     dragInfo.lastpos = dragInfo.pos;\r
5217     if (appData.highlightDragging) {\r
5218       ClearHighlights();\r
5219     }\r
5220     if(y == -2) {\r
5221       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5222       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5223           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5224       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5225           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5226       }\r
5227     }\r
5228     DrawPosition(TRUE, NULL);\r
5229 \r
5230     switch (gameMode) {\r
5231     case EditPosition:\r
5232     case IcsExamining:\r
5233       if (x < 0 || y < 0) break;\r
5234       fromX = x;\r
5235       fromY = y;\r
5236       if (message == WM_MBUTTONDOWN) {\r
5237         buttonCount = 3;  /* even if system didn't think so */\r
5238         if (wParam & MK_SHIFT) \r
5239           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5240         else\r
5241           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5242       } else { /* message == WM_RBUTTONDOWN */\r
5243         /* Just have one menu, on the right button.  Windows users don't\r
5244            think to try the middle one, and sometimes other software steals\r
5245            it, or it doesn't really exist. */\r
5246         if(gameInfo.variant != VariantShogi)\r
5247             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5248         else\r
5249             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5250       }\r
5251       break;\r
5252     case IcsPlayingWhite:\r
5253     case IcsPlayingBlack:\r
5254     case EditGame:\r
5255     case MachinePlaysWhite:\r
5256     case MachinePlaysBlack:\r
5257       if (appData.testLegality &&\r
5258           gameInfo.variant != VariantBughouse &&\r
5259           gameInfo.variant != VariantCrazyhouse) break;\r
5260       if (x < 0 || y < 0) break;\r
5261       fromX = x;\r
5262       fromY = y;\r
5263       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5264       SetupDropMenu(hmenu);\r
5265       MenuPopup(hwnd, pt, hmenu, -1);\r
5266       break;\r
5267     default:\r
5268       break;\r
5269     }\r
5270     break;\r
5271   }\r
5272 \r
5273   recursive--;\r
5274 }\r
5275 \r
5276 /* Preprocess messages for buttons in main window */\r
5277 LRESULT CALLBACK\r
5278 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5279 {\r
5280   int id = GetWindowLong(hwnd, GWL_ID);\r
5281   int i, dir;\r
5282 \r
5283   for (i=0; i<N_BUTTONS; i++) {\r
5284     if (buttonDesc[i].id == id) break;\r
5285   }\r
5286   if (i == N_BUTTONS) return 0;\r
5287   switch (message) {\r
5288   case WM_KEYDOWN:\r
5289     switch (wParam) {\r
5290     case VK_LEFT:\r
5291     case VK_RIGHT:\r
5292       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5293       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5294       return TRUE;\r
5295     }\r
5296     break;\r
5297   case WM_CHAR:\r
5298     switch (wParam) {\r
5299     case '\r':\r
5300       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5301       return TRUE;\r
5302     default:\r
5303       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5304         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5305         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5306         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5307         SetFocus(h);\r
5308         SendMessage(h, WM_CHAR, wParam, lParam);\r
5309         return TRUE;\r
5310       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5311         PopUpMoveDialog((char)wParam);\r
5312       }\r
5313       break;\r
5314     }\r
5315     break;\r
5316   }\r
5317   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5318 }\r
5319 \r
5320 /* Process messages for Promotion dialog box */\r
5321 LRESULT CALLBACK\r
5322 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5323 {\r
5324   char promoChar;\r
5325 \r
5326   switch (message) {\r
5327   case WM_INITDIALOG: /* message: initialize dialog box */\r
5328     /* Center the dialog over the application window */\r
5329     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5330     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5331       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5332        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5333                SW_SHOW : SW_HIDE);\r
5334     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5335     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5336        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5337          PieceToChar(WhiteAngel) != '~') ||\r
5338         (PieceToChar(BlackAngel) >= 'A' &&\r
5339          PieceToChar(BlackAngel) != '~')   ) ?\r
5340                SW_SHOW : SW_HIDE);\r
5341     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5342        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5343          PieceToChar(WhiteMarshall) != '~') ||\r
5344         (PieceToChar(BlackMarshall) >= 'A' &&\r
5345          PieceToChar(BlackMarshall) != '~')   ) ?\r
5346                SW_SHOW : SW_HIDE);\r
5347     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5348     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5349        gameInfo.variant != VariantShogi ?\r
5350                SW_SHOW : SW_HIDE);\r
5351     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5352        gameInfo.variant != VariantShogi ?\r
5353                SW_SHOW : SW_HIDE);\r
5354     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5355        gameInfo.variant == VariantShogi ?\r
5356                SW_SHOW : SW_HIDE);\r
5357     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5358        gameInfo.variant == VariantShogi ?\r
5359                SW_SHOW : SW_HIDE);\r
5360     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5361        gameInfo.variant == VariantSuper ?\r
5362                SW_SHOW : SW_HIDE);\r
5363     return TRUE;\r
5364 \r
5365   case WM_COMMAND: /* message: received a command */\r
5366     switch (LOWORD(wParam)) {\r
5367     case IDCANCEL:\r
5368       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5369       ClearHighlights();\r
5370       DrawPosition(FALSE, NULL);\r
5371       return TRUE;\r
5372     case PB_King:\r
5373       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5374       break;\r
5375     case PB_Queen:\r
5376       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5377       break;\r
5378     case PB_Rook:\r
5379       promoChar = PieceToChar(BlackRook);\r
5380       break;\r
5381     case PB_Bishop:\r
5382       promoChar = PieceToChar(BlackBishop);\r
5383       break;\r
5384     case PB_Chancellor:\r
5385       promoChar = PieceToChar(BlackMarshall);\r
5386       break;\r
5387     case PB_Archbishop:\r
5388       promoChar = PieceToChar(BlackAngel);\r
5389       break;\r
5390     case PB_Knight:\r
5391       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5392       break;\r
5393     default:\r
5394       return FALSE;\r
5395     }\r
5396     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5397     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5398        only show the popup when we are already sure the move is valid or\r
5399        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5400        will figure out it is a promotion from the promoChar. */\r
5401     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5402     if (!appData.highlightLastMove) {\r
5403       ClearHighlights();\r
5404       DrawPosition(FALSE, NULL);\r
5405     }\r
5406     return TRUE;\r
5407   }\r
5408   return FALSE;\r
5409 }\r
5410 \r
5411 /* Pop up promotion dialog */\r
5412 VOID\r
5413 PromotionPopup(HWND hwnd)\r
5414 {\r
5415   FARPROC lpProc;\r
5416 \r
5417   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5418   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5419     hwnd, (DLGPROC)lpProc);\r
5420   FreeProcInstance(lpProc);\r
5421 }\r
5422 \r
5423 /* Toggle ShowThinking */\r
5424 VOID\r
5425 ToggleShowThinking()\r
5426 {\r
5427   appData.showThinking = !appData.showThinking;\r
5428   ShowThinkingEvent();\r
5429 }\r
5430 \r
5431 VOID\r
5432 LoadGameDialog(HWND hwnd, char* title)\r
5433 {\r
5434   UINT number = 0;\r
5435   FILE *f;\r
5436   char fileTitle[MSG_SIZ];\r
5437   f = OpenFileDialog(hwnd, "rb", "",\r
5438                      appData.oldSaveStyle ? "gam" : "pgn",\r
5439                      GAME_FILT,\r
5440                      title, &number, fileTitle, NULL);\r
5441   if (f != NULL) {\r
5442     cmailMsgLoaded = FALSE;\r
5443     if (number == 0) {\r
5444       int error = GameListBuild(f);\r
5445       if (error) {\r
5446         DisplayError("Cannot build game list", error);\r
5447       } else if (!ListEmpty(&gameList) &&\r
5448                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5449         GameListPopUp(f, fileTitle);\r
5450         return;\r
5451       }\r
5452       GameListDestroy();\r
5453       number = 1;\r
5454     }\r
5455     LoadGame(f, number, fileTitle, FALSE);\r
5456   }\r
5457 }\r
5458 \r
5459 void UpdateICSWidth(HWND hText)\r
5460 {\r
5461         HDC hdc;\r
5462         TEXTMETRIC tm;\r
5463         RECT rc;\r
5464         HFONT hfont, hold_font;\r
5465         LONG old_width, new_width;\r
5466         \r
5467         // get the text metrics\r
5468         hdc = GetDC(hText);\r
5469         hfont = CreateFontIndirect(&font[boardSize][CONSOLE_FONT]->lf);\r
5470         hold_font = SelectObject(hdc, hfont);\r
5471         GetTextMetrics(hdc, &tm);\r
5472         SelectObject(hdc, hold_font);\r
5473         DeleteObject(hfont);\r
5474         ReleaseDC(hText, hdc);\r
5475 \r
5476         // get the rectangle\r
5477         SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
5478 \r
5479         // update the width\r
5480         new_width = (rc.right-rc.left) / tm.tmAveCharWidth;\r
5481         old_width = GetWindowLong(hText, GWL_USERDATA);\r
5482         if (new_width != old_width)\r
5483         {\r
5484                 ics_update_width(new_width);\r
5485                 SetWindowLong(hText, GWL_USERDATA, new_width);\r
5486         }\r
5487 }\r
5488 \r
5489 VOID\r
5490 ChangedConsoleFont()\r
5491 {\r
5492   CHARFORMAT cfmt;\r
5493   CHARRANGE tmpsel, sel;\r
5494   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5495   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5496   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5497   PARAFORMAT paraf;\r
5498 \r
5499   cfmt.cbSize = sizeof(CHARFORMAT);\r
5500   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5501   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5502   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5503    * size.  This was undocumented in the version of MSVC++ that I had\r
5504    * when I wrote the code, but is apparently documented now.\r
5505    */\r
5506   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5507   cfmt.bCharSet = f->lf.lfCharSet;\r
5508   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5509   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5510   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5511   /* Why are the following seemingly needed too? */\r
5512   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5513   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5514   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5515   tmpsel.cpMin = 0;\r
5516   tmpsel.cpMax = -1; /*999999?*/\r
5517   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5518   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5519   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5520    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5521    */\r
5522   paraf.cbSize = sizeof(paraf);\r
5523   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5524   paraf.dxStartIndent = 0;\r
5525   paraf.dxOffset = WRAP_INDENT;\r
5526   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5527   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5528   UpdateICSWidth(hText);\r
5529 }\r
5530 \r
5531 /*---------------------------------------------------------------------------*\\r
5532  *\r
5533  * Window Proc for main window\r
5534  *\r
5535 \*---------------------------------------------------------------------------*/\r
5536 \r
5537 /* Process messages for main window, etc. */\r
5538 LRESULT CALLBACK\r
5539 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5540 {\r
5541   FARPROC lpProc;\r
5542   int wmId, wmEvent;\r
5543   char *defName;\r
5544   FILE *f;\r
5545   UINT number;\r
5546   char fileTitle[MSG_SIZ];\r
5547   char buf[MSG_SIZ];\r
5548   static SnapData sd;\r
5549 \r
5550   switch (message) {\r
5551 \r
5552   case WM_PAINT: /* message: repaint portion of window */\r
5553     PaintProc(hwnd);\r
5554     break;\r
5555 \r
5556   case WM_ERASEBKGND:\r
5557     if (IsIconic(hwnd)) {\r
5558       /* Cheat; change the message */\r
5559       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5560     } else {\r
5561       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5562     }\r
5563     break;\r
5564 \r
5565   case WM_LBUTTONDOWN:\r
5566   case WM_MBUTTONDOWN:\r
5567   case WM_RBUTTONDOWN:\r
5568   case WM_LBUTTONUP:\r
5569   case WM_MBUTTONUP:\r
5570   case WM_RBUTTONUP:\r
5571   case WM_MOUSEMOVE:\r
5572   case WM_MOUSEWHEEL:\r
5573     MouseEvent(hwnd, message, wParam, lParam);\r
5574     break;\r
5575 \r
5576   JAWS_KB_NAVIGATION\r
5577 \r
5578   case WM_CHAR:\r
5579     \r
5580     JAWS_ALT_INTERCEPT\r
5581 \r
5582     if (appData.icsActive && (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9')) { \r
5583         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5584         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5585         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5586         SetFocus(h);\r
5587         SendMessage(h, message, wParam, lParam);\r
5588     } else if(lParam != KF_REPEAT) {\r
5589         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5590                 PopUpMoveDialog((char)wParam);\r
5591         } else if((char)wParam == 003) CopyGameToClipboard();\r
5592          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5593     }\r
5594 \r
5595     break;\r
5596 \r
5597   case WM_PALETTECHANGED:\r
5598     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5599       int nnew;\r
5600       HDC hdc = GetDC(hwndMain);\r
5601       SelectPalette(hdc, hPal, TRUE);\r
5602       nnew = RealizePalette(hdc);\r
5603       if (nnew > 0) {\r
5604         paletteChanged = TRUE;\r
5605         InvalidateRect(hwnd, &boardRect, FALSE);\r
5606       }\r
5607       ReleaseDC(hwnd, hdc);\r
5608     }\r
5609     break;\r
5610 \r
5611   case WM_QUERYNEWPALETTE:\r
5612     if (!appData.monoMode /*&& paletteChanged*/) {\r
5613       int nnew;\r
5614       HDC hdc = GetDC(hwndMain);\r
5615       paletteChanged = FALSE;\r
5616       SelectPalette(hdc, hPal, FALSE);\r
5617       nnew = RealizePalette(hdc);\r
5618       if (nnew > 0) {\r
5619         InvalidateRect(hwnd, &boardRect, FALSE);\r
5620       }\r
5621       ReleaseDC(hwnd, hdc);\r
5622       return TRUE;\r
5623     }\r
5624     return FALSE;\r
5625 \r
5626   case WM_COMMAND: /* message: command from application menu */\r
5627     wmId    = LOWORD(wParam);\r
5628     wmEvent = HIWORD(wParam);\r
5629 \r
5630     switch (wmId) {\r
5631     case IDM_NewGame:\r
5632       ResetGameEvent();\r
5633       AnalysisPopDown();\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         AnalysisPopDown();\r
5641       }\r
5642       break;\r
5643 \r
5644     case IDM_NewVariant:\r
5645       NewVariantPopup(hwnd);\r
5646       break;\r
5647 \r
5648     case IDM_LoadGame:\r
5649       LoadGameDialog(hwnd, "Load Game from File");\r
5650       break;\r
5651 \r
5652     case IDM_LoadNextGame:\r
5653       ReloadGame(1);\r
5654       break;\r
5655 \r
5656     case IDM_LoadPrevGame:\r
5657       ReloadGame(-1);\r
5658       break;\r
5659 \r
5660     case IDM_ReloadGame:\r
5661       ReloadGame(0);\r
5662       break;\r
5663 \r
5664     case IDM_LoadPosition:\r
5665       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5666         Reset(FALSE, TRUE);\r
5667       }\r
5668       number = 1;\r
5669       f = OpenFileDialog(hwnd, "rb", "",\r
5670                          appData.oldSaveStyle ? "pos" : "fen",\r
5671                          POSITION_FILT,\r
5672                          "Load Position from File", &number, fileTitle, NULL);\r
5673       if (f != NULL) {\r
5674         LoadPosition(f, number, fileTitle);\r
5675       }\r
5676       break;\r
5677 \r
5678     case IDM_LoadNextPosition:\r
5679       ReloadPosition(1);\r
5680       break;\r
5681 \r
5682     case IDM_LoadPrevPosition:\r
5683       ReloadPosition(-1);\r
5684       break;\r
5685 \r
5686     case IDM_ReloadPosition:\r
5687       ReloadPosition(0);\r
5688       break;\r
5689 \r
5690     case IDM_SaveGame:\r
5691       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5692       f = OpenFileDialog(hwnd, "a", defName,\r
5693                          appData.oldSaveStyle ? "gam" : "pgn",\r
5694                          GAME_FILT,\r
5695                          "Save Game to File", NULL, fileTitle, NULL);\r
5696       if (f != NULL) {\r
5697         SaveGame(f, 0, "");\r
5698       }\r
5699       break;\r
5700 \r
5701     case IDM_SavePosition:\r
5702       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5703       f = OpenFileDialog(hwnd, "a", defName,\r
5704                          appData.oldSaveStyle ? "pos" : "fen",\r
5705                          POSITION_FILT,\r
5706                          "Save Position to File", NULL, fileTitle, NULL);\r
5707       if (f != NULL) {\r
5708         SavePosition(f, 0, "");\r
5709       }\r
5710       break;\r
5711 \r
5712     case IDM_SaveDiagram:\r
5713       defName = "diagram";\r
5714       f = OpenFileDialog(hwnd, "wb", defName,\r
5715                          "bmp",\r
5716                          DIAGRAM_FILT,\r
5717                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5718       if (f != NULL) {\r
5719         SaveDiagram(f);\r
5720       }\r
5721       break;\r
5722 \r
5723     case IDM_CopyGame:\r
5724       CopyGameToClipboard();\r
5725       break;\r
5726 \r
5727     case IDM_PasteGame:\r
5728       PasteGameFromClipboard();\r
5729       break;\r
5730 \r
5731     case IDM_CopyGameListToClipboard:\r
5732       CopyGameListToClipboard();\r
5733       break;\r
5734 \r
5735     /* [AS] Autodetect FEN or PGN data */\r
5736     case IDM_PasteAny:\r
5737       PasteGameOrFENFromClipboard();\r
5738       break;\r
5739 \r
5740     /* [AS] Move history */\r
5741     case IDM_ShowMoveHistory:\r
5742         if( MoveHistoryIsUp() ) {\r
5743             MoveHistoryPopDown();\r
5744         }\r
5745         else {\r
5746             MoveHistoryPopUp();\r
5747         }\r
5748         break;\r
5749 \r
5750     /* [AS] Eval graph */\r
5751     case IDM_ShowEvalGraph:\r
5752         if( EvalGraphIsUp() ) {\r
5753             EvalGraphPopDown();\r
5754         }\r
5755         else {\r
5756             EvalGraphPopUp();\r
5757             SetFocus(hwndMain);\r
5758         }\r
5759         break;\r
5760 \r
5761     /* [AS] Engine output */\r
5762     case IDM_ShowEngineOutput:\r
5763         if( EngineOutputIsUp() ) {\r
5764             EngineOutputPopDown();\r
5765         }\r
5766         else {\r
5767             EngineOutputPopUp();\r
5768         }\r
5769         break;\r
5770 \r
5771     /* [AS] User adjudication */\r
5772     case IDM_UserAdjudication_White:\r
5773         UserAdjudicationEvent( +1 );\r
5774         break;\r
5775 \r
5776     case IDM_UserAdjudication_Black:\r
5777         UserAdjudicationEvent( -1 );\r
5778         break;\r
5779 \r
5780     case IDM_UserAdjudication_Draw:\r
5781         UserAdjudicationEvent( 0 );\r
5782         break;\r
5783 \r
5784     /* [AS] Game list options dialog */\r
5785     case IDM_GameListOptions:\r
5786       GameListOptions();\r
5787       break;\r
5788 \r
5789     case IDM_NewChat:\r
5790       ChatPopUp();\r
5791       break;\r
5792 \r
5793     case IDM_CopyPosition:\r
5794       CopyFENToClipboard();\r
5795       break;\r
5796 \r
5797     case IDM_PastePosition:\r
5798       PasteFENFromClipboard();\r
5799       break;\r
5800 \r
5801     case IDM_MailMove:\r
5802       MailMoveEvent();\r
5803       break;\r
5804 \r
5805     case IDM_ReloadCMailMsg:\r
5806       Reset(TRUE, TRUE);\r
5807       ReloadCmailMsgEvent(FALSE);\r
5808       break;\r
5809 \r
5810     case IDM_Minimize:\r
5811       ShowWindow(hwnd, SW_MINIMIZE);\r
5812       break;\r
5813 \r
5814     case IDM_Exit:\r
5815       ExitEvent(0);\r
5816       break;\r
5817 \r
5818     case IDM_MachineWhite:\r
5819       MachineWhiteEvent();\r
5820       /*\r
5821        * refresh the tags dialog only if it's visible\r
5822        */\r
5823       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5824           char *tags;\r
5825           tags = PGNTags(&gameInfo);\r
5826           TagsPopUp(tags, CmailMsg());\r
5827           free(tags);\r
5828       }\r
5829       SAY("computer starts playing white");\r
5830       break;\r
5831 \r
5832     case IDM_MachineBlack:\r
5833       MachineBlackEvent();\r
5834       /*\r
5835        * refresh the tags dialog only if it's visible\r
5836        */\r
5837       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5838           char *tags;\r
5839           tags = PGNTags(&gameInfo);\r
5840           TagsPopUp(tags, CmailMsg());\r
5841           free(tags);\r
5842       }\r
5843       SAY("computer starts playing black");\r
5844       break;\r
5845 \r
5846     case IDM_TwoMachines:\r
5847       TwoMachinesEvent();\r
5848       /*\r
5849        * refresh the tags dialog only if it's visible\r
5850        */\r
5851       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5852           char *tags;\r
5853           tags = PGNTags(&gameInfo);\r
5854           TagsPopUp(tags, CmailMsg());\r
5855           free(tags);\r
5856       }\r
5857       SAY("programs start playing each other");\r
5858       break;\r
5859 \r
5860     case IDM_AnalysisMode:\r
5861       if (!first.analysisSupport) {\r
5862         sprintf(buf, "%s does not support analysis", first.tidy);\r
5863         DisplayError(buf, 0);\r
5864       } else {\r
5865         SAY("analyzing current position");\r
5866         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5867         if (appData.icsActive) {\r
5868                if (gameMode != IcsObserving) {\r
5869                        sprintf(buf, "You are not observing a game");\r
5870                        DisplayError(buf, 0);\r
5871                        /* secure check */\r
5872                        if (appData.icsEngineAnalyze) {\r
5873                                if (appData.debugMode) \r
5874                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5875                                ExitAnalyzeMode();\r
5876                                ModeHighlight();\r
5877                                break;\r
5878                        }\r
5879                        break;\r
5880                } else {\r
5881                        /* if enable, user want disable icsEngineAnalyze */\r
5882                        if (appData.icsEngineAnalyze) {\r
5883                                ExitAnalyzeMode();\r
5884                                ModeHighlight();\r
5885                                break;\r
5886                        }\r
5887                        appData.icsEngineAnalyze = TRUE;\r
5888                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5889                }\r
5890         } \r
5891         if (!appData.showThinking) ToggleShowThinking();\r
5892         AnalyzeModeEvent();\r
5893       }\r
5894       break;\r
5895 \r
5896     case IDM_AnalyzeFile:\r
5897       if (!first.analysisSupport) {\r
5898         char buf[MSG_SIZ];\r
5899         sprintf(buf, "%s does not support analysis", first.tidy);\r
5900         DisplayError(buf, 0);\r
5901       } else {\r
5902         if (!appData.showThinking) ToggleShowThinking();\r
5903         AnalyzeFileEvent();\r
5904         LoadGameDialog(hwnd, "Analyze Game from File");\r
5905         AnalysisPeriodicEvent(1);\r
5906       }\r
5907       break;\r
5908 \r
5909     case IDM_IcsClient:\r
5910       IcsClientEvent();\r
5911       break;\r
5912 \r
5913     case IDM_EditGame:\r
5914       EditGameEvent();\r
5915       SAY("edit game");\r
5916       break;\r
5917 \r
5918     case IDM_EditPosition:\r
5919       EditPositionEvent();\r
5920       SAY("to set up a position type a FEN");\r
5921       break;\r
5922 \r
5923     case IDM_Training:\r
5924       TrainingEvent();\r
5925       break;\r
5926 \r
5927     case IDM_ShowGameList:\r
5928       ShowGameListProc();\r
5929       break;\r
5930 \r
5931     case IDM_EditTags:\r
5932       EditTagsProc();\r
5933       break;\r
5934 \r
5935     case IDM_EditComment:\r
5936       if (commentDialogUp && editComment) {\r
5937         CommentPopDown();\r
5938       } else {\r
5939         EditCommentEvent();\r
5940       }\r
5941       break;\r
5942 \r
5943     case IDM_Pause:\r
5944       PauseEvent();\r
5945       break;\r
5946 \r
5947     case IDM_Accept:\r
5948       AcceptEvent();\r
5949       break;\r
5950 \r
5951     case IDM_Decline:\r
5952       DeclineEvent();\r
5953       break;\r
5954 \r
5955     case IDM_Rematch:\r
5956       RematchEvent();\r
5957       break;\r
5958 \r
5959     case IDM_CallFlag:\r
5960       CallFlagEvent();\r
5961       break;\r
5962 \r
5963     case IDM_Draw:\r
5964       DrawEvent();\r
5965       break;\r
5966 \r
5967     case IDM_Adjourn:\r
5968       AdjournEvent();\r
5969       break;\r
5970 \r
5971     case IDM_Abort:\r
5972       AbortEvent();\r
5973       break;\r
5974 \r
5975     case IDM_Resign:\r
5976       ResignEvent();\r
5977       break;\r
5978 \r
5979     case IDM_StopObserving:\r
5980       StopObservingEvent();\r
5981       break;\r
5982 \r
5983     case IDM_StopExamining:\r
5984       StopExaminingEvent();\r
5985       break;\r
5986 \r
5987     case IDM_TypeInMove:\r
5988       PopUpMoveDialog('\000');\r
5989       break;\r
5990 \r
5991     case IDM_TypeInName:\r
5992       PopUpNameDialog('\000');\r
5993       break;\r
5994 \r
5995     case IDM_Backward:\r
5996       BackwardEvent();\r
5997       SetFocus(hwndMain);\r
5998       break;\r
5999 \r
6000     JAWS_MENU_ITEMS\r
6001 \r
6002     case IDM_Forward:\r
6003       ForwardEvent();\r
6004       SetFocus(hwndMain);\r
6005       break;\r
6006 \r
6007     case IDM_ToStart:\r
6008       ToStartEvent();\r
6009       SetFocus(hwndMain);\r
6010       break;\r
6011 \r
6012     case IDM_ToEnd:\r
6013       ToEndEvent();\r
6014       SetFocus(hwndMain);\r
6015       break;\r
6016 \r
6017     case IDM_Revert:\r
6018       RevertEvent();\r
6019       break;\r
6020 \r
6021     case IDM_TruncateGame:\r
6022       TruncateGameEvent();\r
6023       break;\r
6024 \r
6025     case IDM_MoveNow:\r
6026       MoveNowEvent();\r
6027       break;\r
6028 \r
6029     case IDM_RetractMove:\r
6030       RetractMoveEvent();\r
6031       break;\r
6032 \r
6033     case IDM_FlipView:\r
6034       flipView = !flipView;\r
6035       DrawPosition(FALSE, NULL);\r
6036       break;\r
6037 \r
6038     case IDM_FlipClock:\r
6039       flipClock = !flipClock;\r
6040       DisplayBothClocks();\r
6041       DrawPosition(FALSE, NULL);\r
6042       break;\r
6043 \r
6044     case IDM_MuteSounds:\r
6045       mute = !mute; // [HGM] mute: keep track of global muting variable\r
6046       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
6047                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
6048       break;\r
6049 \r
6050     case IDM_GeneralOptions:\r
6051       GeneralOptionsPopup(hwnd);\r
6052       DrawPosition(TRUE, NULL);\r
6053       break;\r
6054 \r
6055     case IDM_BoardOptions:\r
6056       BoardOptionsPopup(hwnd);\r
6057       break;\r
6058 \r
6059     case IDM_EnginePlayOptions:\r
6060       EnginePlayOptionsPopup(hwnd);\r
6061       break;\r
6062 \r
6063     case IDM_Engine1Options:\r
6064       EngineOptionsPopup(hwnd, &first);\r
6065       break;\r
6066 \r
6067     case IDM_Engine2Options:\r
6068       EngineOptionsPopup(hwnd, &second);\r
6069       break;\r
6070 \r
6071     case IDM_OptionsUCI:\r
6072       UciOptionsPopup(hwnd);\r
6073       break;\r
6074 \r
6075     case IDM_IcsOptions:\r
6076       IcsOptionsPopup(hwnd);\r
6077       break;\r
6078 \r
6079     case IDM_Fonts:\r
6080       FontsOptionsPopup(hwnd);\r
6081       break;\r
6082 \r
6083     case IDM_Sounds:\r
6084       SoundOptionsPopup(hwnd);\r
6085       break;\r
6086 \r
6087     case IDM_CommPort:\r
6088       CommPortOptionsPopup(hwnd);\r
6089       break;\r
6090 \r
6091     case IDM_LoadOptions:\r
6092       LoadOptionsPopup(hwnd);\r
6093       break;\r
6094 \r
6095     case IDM_SaveOptions:\r
6096       SaveOptionsPopup(hwnd);\r
6097       break;\r
6098 \r
6099     case IDM_TimeControl:\r
6100       TimeControlOptionsPopup(hwnd);\r
6101       break;\r
6102 \r
6103     case IDM_SaveSettings:\r
6104       SaveSettings(settingsFileName);\r
6105       break;\r
6106 \r
6107     case IDM_SaveSettingsOnExit:\r
6108       saveSettingsOnExit = !saveSettingsOnExit;\r
6109       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6110                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6111                                          MF_CHECKED : MF_UNCHECKED));\r
6112       break;\r
6113 \r
6114     case IDM_Hint:\r
6115       HintEvent();\r
6116       break;\r
6117 \r
6118     case IDM_Book:\r
6119       BookEvent();\r
6120       break;\r
6121 \r
6122     case IDM_AboutGame:\r
6123       AboutGameEvent();\r
6124       break;\r
6125 \r
6126     case IDM_Debug:\r
6127       appData.debugMode = !appData.debugMode;\r
6128       if (appData.debugMode) {\r
6129         char dir[MSG_SIZ];\r
6130         GetCurrentDirectory(MSG_SIZ, dir);\r
6131         SetCurrentDirectory(installDir);\r
6132         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6133         SetCurrentDirectory(dir);\r
6134         setbuf(debugFP, NULL);\r
6135       } else {\r
6136         fclose(debugFP);\r
6137         debugFP = NULL;\r
6138       }\r
6139       break;\r
6140 \r
6141     case IDM_HELPCONTENTS:\r
6142       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6143           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6144           MessageBox (GetFocus(),\r
6145                     "Unable to activate help",\r
6146                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6147       }\r
6148       break;\r
6149 \r
6150     case IDM_HELPSEARCH:\r
6151         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6152             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6153         MessageBox (GetFocus(),\r
6154                     "Unable to activate help",\r
6155                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6156       }\r
6157       break;\r
6158 \r
6159     case IDM_HELPHELP:\r
6160       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6161         MessageBox (GetFocus(),\r
6162                     "Unable to activate help",\r
6163                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6164       }\r
6165       break;\r
6166 \r
6167     case IDM_ABOUT:\r
6168       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6169       DialogBox(hInst, \r
6170         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6171         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6172       FreeProcInstance(lpProc);\r
6173       break;\r
6174 \r
6175     case IDM_DirectCommand1:\r
6176       AskQuestionEvent("Direct Command",\r
6177                        "Send to chess program:", "", "1");\r
6178       break;\r
6179     case IDM_DirectCommand2:\r
6180       AskQuestionEvent("Direct Command",\r
6181                        "Send to second chess program:", "", "2");\r
6182       break;\r
6183 \r
6184     case EP_WhitePawn:\r
6185       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6186       fromX = fromY = -1;\r
6187       break;\r
6188 \r
6189     case EP_WhiteKnight:\r
6190       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6191       fromX = fromY = -1;\r
6192       break;\r
6193 \r
6194     case EP_WhiteBishop:\r
6195       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6196       fromX = fromY = -1;\r
6197       break;\r
6198 \r
6199     case EP_WhiteRook:\r
6200       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6201       fromX = fromY = -1;\r
6202       break;\r
6203 \r
6204     case EP_WhiteQueen:\r
6205       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6206       fromX = fromY = -1;\r
6207       break;\r
6208 \r
6209     case EP_WhiteFerz:\r
6210       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6211       fromX = fromY = -1;\r
6212       break;\r
6213 \r
6214     case EP_WhiteWazir:\r
6215       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6216       fromX = fromY = -1;\r
6217       break;\r
6218 \r
6219     case EP_WhiteAlfil:\r
6220       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6221       fromX = fromY = -1;\r
6222       break;\r
6223 \r
6224     case EP_WhiteCannon:\r
6225       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6226       fromX = fromY = -1;\r
6227       break;\r
6228 \r
6229     case EP_WhiteCardinal:\r
6230       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6231       fromX = fromY = -1;\r
6232       break;\r
6233 \r
6234     case EP_WhiteMarshall:\r
6235       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6236       fromX = fromY = -1;\r
6237       break;\r
6238 \r
6239     case EP_WhiteKing:\r
6240       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6241       fromX = fromY = -1;\r
6242       break;\r
6243 \r
6244     case EP_BlackPawn:\r
6245       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6246       fromX = fromY = -1;\r
6247       break;\r
6248 \r
6249     case EP_BlackKnight:\r
6250       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6251       fromX = fromY = -1;\r
6252       break;\r
6253 \r
6254     case EP_BlackBishop:\r
6255       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6256       fromX = fromY = -1;\r
6257       break;\r
6258 \r
6259     case EP_BlackRook:\r
6260       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6261       fromX = fromY = -1;\r
6262       break;\r
6263 \r
6264     case EP_BlackQueen:\r
6265       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6266       fromX = fromY = -1;\r
6267       break;\r
6268 \r
6269     case EP_BlackFerz:\r
6270       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6271       fromX = fromY = -1;\r
6272       break;\r
6273 \r
6274     case EP_BlackWazir:\r
6275       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6276       fromX = fromY = -1;\r
6277       break;\r
6278 \r
6279     case EP_BlackAlfil:\r
6280       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6281       fromX = fromY = -1;\r
6282       break;\r
6283 \r
6284     case EP_BlackCannon:\r
6285       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6286       fromX = fromY = -1;\r
6287       break;\r
6288 \r
6289     case EP_BlackCardinal:\r
6290       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6291       fromX = fromY = -1;\r
6292       break;\r
6293 \r
6294     case EP_BlackMarshall:\r
6295       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6296       fromX = fromY = -1;\r
6297       break;\r
6298 \r
6299     case EP_BlackKing:\r
6300       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6301       fromX = fromY = -1;\r
6302       break;\r
6303 \r
6304     case EP_EmptySquare:\r
6305       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6306       fromX = fromY = -1;\r
6307       break;\r
6308 \r
6309     case EP_ClearBoard:\r
6310       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6311       fromX = fromY = -1;\r
6312       break;\r
6313 \r
6314     case EP_White:\r
6315       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6316       fromX = fromY = -1;\r
6317       break;\r
6318 \r
6319     case EP_Black:\r
6320       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6321       fromX = fromY = -1;\r
6322       break;\r
6323 \r
6324     case EP_Promote:\r
6325       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6326       fromX = fromY = -1;\r
6327       break;\r
6328 \r
6329     case EP_Demote:\r
6330       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6331       fromX = fromY = -1;\r
6332       break;\r
6333 \r
6334     case DP_Pawn:\r
6335       DropMenuEvent(WhitePawn, fromX, fromY);\r
6336       fromX = fromY = -1;\r
6337       break;\r
6338 \r
6339     case DP_Knight:\r
6340       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6341       fromX = fromY = -1;\r
6342       break;\r
6343 \r
6344     case DP_Bishop:\r
6345       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6346       fromX = fromY = -1;\r
6347       break;\r
6348 \r
6349     case DP_Rook:\r
6350       DropMenuEvent(WhiteRook, fromX, fromY);\r
6351       fromX = fromY = -1;\r
6352       break;\r
6353 \r
6354     case DP_Queen:\r
6355       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6356       fromX = fromY = -1;\r
6357       break;\r
6358 \r
6359     default:\r
6360       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6361     }\r
6362     break;\r
6363 \r
6364   case WM_TIMER:\r
6365     switch (wParam) {\r
6366     case CLOCK_TIMER_ID:\r
6367       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6368       clockTimerEvent = 0;\r
6369       DecrementClocks(); /* call into back end */\r
6370       break;\r
6371     case LOAD_GAME_TIMER_ID:\r
6372       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6373       loadGameTimerEvent = 0;\r
6374       AutoPlayGameLoop(); /* call into back end */\r
6375       break;\r
6376     case ANALYSIS_TIMER_ID:\r
6377       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6378                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6379         AnalysisPeriodicEvent(0);\r
6380       } else {\r
6381         KillTimer(hwnd, analysisTimerEvent);\r
6382         analysisTimerEvent = 0;\r
6383       }\r
6384       break;\r
6385     case DELAYED_TIMER_ID:\r
6386       KillTimer(hwnd, delayedTimerEvent);\r
6387       delayedTimerEvent = 0;\r
6388       delayedTimerCallback();\r
6389       break;\r
6390     }\r
6391     break;\r
6392 \r
6393   case WM_USER_Input:\r
6394     InputEvent(hwnd, message, wParam, lParam);\r
6395     break;\r
6396 \r
6397   /* [AS] Also move "attached" child windows */\r
6398   case WM_WINDOWPOSCHANGING:\r
6399 \r
6400     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6401         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6402 \r
6403         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6404             /* Window is moving */\r
6405             RECT rcMain;\r
6406 \r
6407 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6408             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6409             rcMain.right  = boardX + winWidth;\r
6410             rcMain.top    = boardY;\r
6411             rcMain.bottom = boardY + winHeight;\r
6412             \r
6413             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6414             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6415             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6416             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6417             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6418             boardX = lpwp->x;\r
6419             boardY = lpwp->y;\r
6420         }\r
6421     }\r
6422     break;\r
6423 \r
6424   /* [AS] Snapping */\r
6425   case WM_ENTERSIZEMOVE:\r
6426     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6427     if (hwnd == hwndMain) {\r
6428       doingSizing = TRUE;\r
6429       lastSizing = 0;\r
6430     }\r
6431     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6432     break;\r
6433 \r
6434   case WM_SIZING:\r
6435     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6436     if (hwnd == hwndMain) {\r
6437       lastSizing = wParam;\r
6438     }\r
6439     break;\r
6440 \r
6441   case WM_MOVING:\r
6442     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6443       return OnMoving( &sd, hwnd, wParam, lParam );\r
6444 \r
6445   case WM_EXITSIZEMOVE:\r
6446     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6447     if (hwnd == hwndMain) {\r
6448       RECT client;\r
6449       doingSizing = FALSE;\r
6450       InvalidateRect(hwnd, &boardRect, FALSE);\r
6451       GetClientRect(hwnd, &client);\r
6452       ResizeBoard(client.right, client.bottom, lastSizing);\r
6453       lastSizing = 0;\r
6454       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6455     }\r
6456     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6457     break;\r
6458 \r
6459   case WM_DESTROY: /* message: window being destroyed */\r
6460     PostQuitMessage(0);\r
6461     break;\r
6462 \r
6463   case WM_CLOSE:\r
6464     if (hwnd == hwndMain) {\r
6465       ExitEvent(0);\r
6466     }\r
6467     break;\r
6468 \r
6469   default:      /* Passes it on if unprocessed */\r
6470     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6471   }\r
6472   return 0;\r
6473 }\r
6474 \r
6475 /*---------------------------------------------------------------------------*\\r
6476  *\r
6477  * Misc utility routines\r
6478  *\r
6479 \*---------------------------------------------------------------------------*/\r
6480 \r
6481 /*\r
6482  * Decent random number generator, at least not as bad as Windows\r
6483  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6484  */\r
6485 unsigned int randstate;\r
6486 \r
6487 int\r
6488 myrandom(void)\r
6489 {\r
6490   randstate = randstate * 1664525 + 1013904223;\r
6491   return (int) randstate & 0x7fffffff;\r
6492 }\r
6493 \r
6494 void\r
6495 mysrandom(unsigned int seed)\r
6496 {\r
6497   randstate = seed;\r
6498 }\r
6499 \r
6500 \r
6501 /* \r
6502  * returns TRUE if user selects a different color, FALSE otherwise \r
6503  */\r
6504 \r
6505 BOOL\r
6506 ChangeColor(HWND hwnd, COLORREF *which)\r
6507 {\r
6508   static BOOL firstTime = TRUE;\r
6509   static DWORD customColors[16];\r
6510   CHOOSECOLOR cc;\r
6511   COLORREF newcolor;\r
6512   int i;\r
6513   ColorClass ccl;\r
6514 \r
6515   if (firstTime) {\r
6516     /* Make initial colors in use available as custom colors */\r
6517     /* Should we put the compiled-in defaults here instead? */\r
6518     i = 0;\r
6519     customColors[i++] = lightSquareColor & 0xffffff;\r
6520     customColors[i++] = darkSquareColor & 0xffffff;\r
6521     customColors[i++] = whitePieceColor & 0xffffff;\r
6522     customColors[i++] = blackPieceColor & 0xffffff;\r
6523     customColors[i++] = highlightSquareColor & 0xffffff;\r
6524     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6525 \r
6526     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6527       customColors[i++] = textAttribs[ccl].color;\r
6528     }\r
6529     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6530     firstTime = FALSE;\r
6531   }\r
6532 \r
6533   cc.lStructSize = sizeof(cc);\r
6534   cc.hwndOwner = hwnd;\r
6535   cc.hInstance = NULL;\r
6536   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6537   cc.lpCustColors = (LPDWORD) customColors;\r
6538   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6539 \r
6540   if (!ChooseColor(&cc)) return FALSE;\r
6541 \r
6542   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6543   if (newcolor == *which) return FALSE;\r
6544   *which = newcolor;\r
6545   return TRUE;\r
6546 \r
6547   /*\r
6548   InitDrawingColors();\r
6549   InvalidateRect(hwnd, &boardRect, FALSE);\r
6550   */\r
6551 }\r
6552 \r
6553 BOOLEAN\r
6554 MyLoadSound(MySound *ms)\r
6555 {\r
6556   BOOL ok = FALSE;\r
6557   struct stat st;\r
6558   FILE *f;\r
6559 \r
6560   if (ms->data) free(ms->data);\r
6561   ms->data = NULL;\r
6562 \r
6563   switch (ms->name[0]) {\r
6564   case NULLCHAR:\r
6565     /* Silence */\r
6566     ok = TRUE;\r
6567     break;\r
6568   case '$':\r
6569     /* System sound from Control Panel.  Don't preload here. */\r
6570     ok = TRUE;\r
6571     break;\r
6572   case '!':\r
6573     if (ms->name[1] == NULLCHAR) {\r
6574       /* "!" alone = silence */\r
6575       ok = TRUE;\r
6576     } else {\r
6577       /* Builtin wave resource.  Error if not found. */\r
6578       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6579       if (h == NULL) break;\r
6580       ms->data = (void *)LoadResource(hInst, h);\r
6581       if (h == NULL) break;\r
6582       ok = TRUE;\r
6583     }\r
6584     break;\r
6585   default:\r
6586     /* .wav file.  Error if not found. */\r
6587     f = fopen(ms->name, "rb");\r
6588     if (f == NULL) break;\r
6589     if (fstat(fileno(f), &st) < 0) break;\r
6590     ms->data = malloc(st.st_size);\r
6591     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6592     fclose(f);\r
6593     ok = TRUE;\r
6594     break;\r
6595   }\r
6596   if (!ok) {\r
6597     char buf[MSG_SIZ];\r
6598     sprintf(buf, "Error loading sound %s", ms->name);\r
6599     DisplayError(buf, GetLastError());\r
6600   }\r
6601   return ok;\r
6602 }\r
6603 \r
6604 BOOLEAN\r
6605 MyPlaySound(MySound *ms)\r
6606 {\r
6607   BOOLEAN ok = FALSE;\r
6608 \r
6609   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6610   switch (ms->name[0]) {\r
6611   case NULLCHAR:\r
6612         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6613     /* Silence */\r
6614     ok = TRUE;\r
6615     break;\r
6616   case '$':\r
6617     /* System sound from Control Panel (deprecated feature).\r
6618        "$" alone or an unset sound name gets default beep (still in use). */\r
6619     if (ms->name[1]) {\r
6620       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6621     }\r
6622     if (!ok) ok = MessageBeep(MB_OK);\r
6623     break; \r
6624   case '!':\r
6625     /* Builtin wave resource, or "!" alone for silence */\r
6626     if (ms->name[1]) {\r
6627       if (ms->data == NULL) return FALSE;\r
6628       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6629     } else {\r
6630       ok = TRUE;\r
6631     }\r
6632     break;\r
6633   default:\r
6634     /* .wav file.  Error if not found. */\r
6635     if (ms->data == NULL) return FALSE;\r
6636     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6637     break;\r
6638   }\r
6639   /* Don't print an error: this can happen innocently if the sound driver\r
6640      is busy; for instance, if another instance of WinBoard is playing\r
6641      a sound at about the same time. */\r
6642   return ok;\r
6643 }\r
6644 \r
6645 \r
6646 LRESULT CALLBACK\r
6647 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6648 {\r
6649   BOOL ok;\r
6650   OPENFILENAME *ofn;\r
6651   static UINT *number; /* gross that this is static */\r
6652 \r
6653   switch (message) {\r
6654   case WM_INITDIALOG: /* message: initialize dialog box */\r
6655     /* Center the dialog over the application window */\r
6656     ofn = (OPENFILENAME *) lParam;\r
6657     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6658       number = (UINT *) ofn->lCustData;\r
6659       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6660     } else {\r
6661       number = NULL;\r
6662     }\r
6663     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6664     return FALSE;  /* Allow for further processing */\r
6665 \r
6666   case WM_COMMAND:\r
6667     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6668       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6669     }\r
6670     return FALSE;  /* Allow for further processing */\r
6671   }\r
6672   return FALSE;\r
6673 }\r
6674 \r
6675 UINT APIENTRY\r
6676 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6677 {\r
6678   static UINT *number;\r
6679   OPENFILENAME *ofname;\r
6680   OFNOTIFY *ofnot;\r
6681   switch (uiMsg) {\r
6682   case WM_INITDIALOG:\r
6683     ofname = (OPENFILENAME *)lParam;\r
6684     number = (UINT *)(ofname->lCustData);\r
6685     break;\r
6686   case WM_NOTIFY:\r
6687     ofnot = (OFNOTIFY *)lParam;\r
6688     if (ofnot->hdr.code == CDN_FILEOK) {\r
6689       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6690     }\r
6691     break;\r
6692   }\r
6693   return 0;\r
6694 }\r
6695 \r
6696 \r
6697 FILE *\r
6698 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6699                char *nameFilt, char *dlgTitle, UINT *number,\r
6700                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6701 {\r
6702   OPENFILENAME openFileName;\r
6703   char buf1[MSG_SIZ];\r
6704   FILE *f;\r
6705 \r
6706   if (fileName == NULL) fileName = buf1;\r
6707   if (defName == NULL) {\r
6708     strcpy(fileName, "*.");\r
6709     strcat(fileName, defExt);\r
6710   } else {\r
6711     strcpy(fileName, defName);\r
6712   }\r
6713   if (fileTitle) strcpy(fileTitle, "");\r
6714   if (number) *number = 0;\r
6715 \r
6716   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6717   openFileName.hwndOwner         = hwnd;\r
6718   openFileName.hInstance         = (HANDLE) hInst;\r
6719   openFileName.lpstrFilter       = nameFilt;\r
6720   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6721   openFileName.nMaxCustFilter    = 0L;\r
6722   openFileName.nFilterIndex      = 1L;\r
6723   openFileName.lpstrFile         = fileName;\r
6724   openFileName.nMaxFile          = MSG_SIZ;\r
6725   openFileName.lpstrFileTitle    = fileTitle;\r
6726   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6727   openFileName.lpstrInitialDir   = NULL;\r
6728   openFileName.lpstrTitle        = dlgTitle;\r
6729   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6730     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6731     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6732     | (oldDialog ? 0 : OFN_EXPLORER);\r
6733   openFileName.nFileOffset       = 0;\r
6734   openFileName.nFileExtension    = 0;\r
6735   openFileName.lpstrDefExt       = defExt;\r
6736   openFileName.lCustData         = (LONG) number;\r
6737   openFileName.lpfnHook          = oldDialog ?\r
6738     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6739   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6740 \r
6741   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6742                         GetOpenFileName(&openFileName)) {\r
6743     /* open the file */\r
6744     f = fopen(openFileName.lpstrFile, write);\r
6745     if (f == NULL) {\r
6746       MessageBox(hwnd, "File open failed", NULL,\r
6747                  MB_OK|MB_ICONEXCLAMATION);\r
6748       return NULL;\r
6749     }\r
6750   } else {\r
6751     int err = CommDlgExtendedError();\r
6752     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6753     return FALSE;\r
6754   }\r
6755   return f;\r
6756 }\r
6757 \r
6758 \r
6759 \r
6760 VOID APIENTRY\r
6761 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6762 {\r
6763   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6764 \r
6765   /*\r
6766    * Get the first pop-up menu in the menu template. This is the\r
6767    * menu that TrackPopupMenu displays.\r
6768    */\r
6769   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6770 \r
6771   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6772 \r
6773   /*\r
6774    * TrackPopup uses screen coordinates, so convert the\r
6775    * coordinates of the mouse click to screen coordinates.\r
6776    */\r
6777   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6778 \r
6779   /* Draw and track the floating pop-up menu. */\r
6780   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6781                  pt.x, pt.y, 0, hwnd, NULL);\r
6782 \r
6783   /* Destroy the menu.*/\r
6784   DestroyMenu(hmenu);\r
6785 }\r
6786    \r
6787 typedef struct {\r
6788   HWND hDlg, hText;\r
6789   int sizeX, sizeY, newSizeX, newSizeY;\r
6790   HDWP hdwp;\r
6791 } ResizeEditPlusButtonsClosure;\r
6792 \r
6793 BOOL CALLBACK\r
6794 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6795 {\r
6796   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6797   RECT rect;\r
6798   POINT pt;\r
6799 \r
6800   if (hChild == cl->hText) return TRUE;\r
6801   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6802   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6803   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6804   ScreenToClient(cl->hDlg, &pt);\r
6805   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6806     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6807   return TRUE;\r
6808 }\r
6809 \r
6810 /* Resize a dialog that has a (rich) edit field filling most of\r
6811    the top, with a row of buttons below */\r
6812 VOID\r
6813 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6814 {\r
6815   RECT rectText;\r
6816   int newTextHeight, newTextWidth;\r
6817   ResizeEditPlusButtonsClosure cl;\r
6818   \r
6819   /*if (IsIconic(hDlg)) return;*/\r
6820   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6821   \r
6822   cl.hdwp = BeginDeferWindowPos(8);\r
6823 \r
6824   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6825   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6826   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6827   if (newTextHeight < 0) {\r
6828     newSizeY += -newTextHeight;\r
6829     newTextHeight = 0;\r
6830   }\r
6831   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6832     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6833 \r
6834   cl.hDlg = hDlg;\r
6835   cl.hText = hText;\r
6836   cl.sizeX = sizeX;\r
6837   cl.sizeY = sizeY;\r
6838   cl.newSizeX = newSizeX;\r
6839   cl.newSizeY = newSizeY;\r
6840   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6841 \r
6842   EndDeferWindowPos(cl.hdwp);\r
6843 }\r
6844 \r
6845 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6846 {\r
6847     RECT    rChild, rParent;\r
6848     int     wChild, hChild, wParent, hParent;\r
6849     int     wScreen, hScreen, xNew, yNew;\r
6850     HDC     hdc;\r
6851 \r
6852     /* Get the Height and Width of the child window */\r
6853     GetWindowRect (hwndChild, &rChild);\r
6854     wChild = rChild.right - rChild.left;\r
6855     hChild = rChild.bottom - rChild.top;\r
6856 \r
6857     /* Get the Height and Width of the parent window */\r
6858     GetWindowRect (hwndParent, &rParent);\r
6859     wParent = rParent.right - rParent.left;\r
6860     hParent = rParent.bottom - rParent.top;\r
6861 \r
6862     /* Get the display limits */\r
6863     hdc = GetDC (hwndChild);\r
6864     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6865     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6866     ReleaseDC(hwndChild, hdc);\r
6867 \r
6868     /* Calculate new X position, then adjust for screen */\r
6869     xNew = rParent.left + ((wParent - wChild) /2);\r
6870     if (xNew < 0) {\r
6871         xNew = 0;\r
6872     } else if ((xNew+wChild) > wScreen) {\r
6873         xNew = wScreen - wChild;\r
6874     }\r
6875 \r
6876     /* Calculate new Y position, then adjust for screen */\r
6877     if( mode == 0 ) {\r
6878         yNew = rParent.top  + ((hParent - hChild) /2);\r
6879     }\r
6880     else {\r
6881         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6882     }\r
6883 \r
6884     if (yNew < 0) {\r
6885         yNew = 0;\r
6886     } else if ((yNew+hChild) > hScreen) {\r
6887         yNew = hScreen - hChild;\r
6888     }\r
6889 \r
6890     /* Set it, and return */\r
6891     return SetWindowPos (hwndChild, NULL,\r
6892                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6893 }\r
6894 \r
6895 /* Center one window over another */\r
6896 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6897 {\r
6898     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6899 }\r
6900 \r
6901 /*---------------------------------------------------------------------------*\\r
6902  *\r
6903  * Startup Dialog functions\r
6904  *\r
6905 \*---------------------------------------------------------------------------*/\r
6906 void\r
6907 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6908 {\r
6909   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6910 \r
6911   while (*cd != NULL) {\r
6912     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6913     cd++;\r
6914   }\r
6915 }\r
6916 \r
6917 void\r
6918 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6919 {\r
6920   char buf1[ARG_MAX];\r
6921   int len;\r
6922 \r
6923   if (str[0] == '@') {\r
6924     FILE* f = fopen(str + 1, "r");\r
6925     if (f == NULL) {\r
6926       DisplayFatalError(str + 1, errno, 2);\r
6927       return;\r
6928     }\r
6929     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6930     fclose(f);\r
6931     buf1[len] = NULLCHAR;\r
6932     str = buf1;\r
6933   }\r
6934 \r
6935   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6936 \r
6937   for (;;) {\r
6938     char buf[MSG_SIZ];\r
6939     char *end = strchr(str, '\n');\r
6940     if (end == NULL) return;\r
6941     memcpy(buf, str, end - str);\r
6942     buf[end - str] = NULLCHAR;\r
6943     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6944     str = end + 1;\r
6945   }\r
6946 }\r
6947 \r
6948 void\r
6949 SetStartupDialogEnables(HWND hDlg)\r
6950 {\r
6951   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6952     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6953     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6954   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6955     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6956   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6957     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6958   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6959     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6960   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6961     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6962     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6963     IsDlgButtonChecked(hDlg, OPT_View));\r
6964 }\r
6965 \r
6966 char *\r
6967 QuoteForFilename(char *filename)\r
6968 {\r
6969   int dquote, space;\r
6970   dquote = strchr(filename, '"') != NULL;\r
6971   space = strchr(filename, ' ') != NULL;\r
6972   if (dquote || space) {\r
6973     if (dquote) {\r
6974       return "'";\r
6975     } else {\r
6976       return "\"";\r
6977     }\r
6978   } else {\r
6979     return "";\r
6980   }\r
6981 }\r
6982 \r
6983 VOID\r
6984 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6985 {\r
6986   char buf[MSG_SIZ];\r
6987   char *q;\r
6988 \r
6989   InitComboStringsFromOption(hwndCombo, nthnames);\r
6990   q = QuoteForFilename(nthcp);\r
6991   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6992   if (*nthdir != NULLCHAR) {\r
6993     q = QuoteForFilename(nthdir);\r
6994     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6995   }\r
6996   if (*nthcp == NULLCHAR) {\r
6997     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6998   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6999     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7000     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7001   }\r
7002 }\r
7003 \r
7004 LRESULT CALLBACK\r
7005 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7006 {\r
7007   char buf[MSG_SIZ];\r
7008   HANDLE hwndCombo;\r
7009   char *p;\r
7010 \r
7011   switch (message) {\r
7012   case WM_INITDIALOG:\r
7013     /* Center the dialog */\r
7014     CenterWindow (hDlg, GetDesktopWindow());\r
7015     /* Initialize the dialog items */\r
7016     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7017                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7018                   firstChessProgramNames);\r
7019     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7020                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7021                   secondChessProgramNames);\r
7022     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7023     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7024     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7025     if (*appData.icsHelper != NULLCHAR) {\r
7026       char *q = QuoteForFilename(appData.icsHelper);\r
7027       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7028     }\r
7029     if (*appData.icsHost == NULLCHAR) {\r
7030       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7031       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7032     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7033       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7034       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7035     }\r
7036 \r
7037     if (appData.icsActive) {\r
7038       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7039     }\r
7040     else if (appData.noChessProgram) {\r
7041       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7042     }\r
7043     else {\r
7044       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7045     }\r
7046 \r
7047     SetStartupDialogEnables(hDlg);\r
7048     return TRUE;\r
7049 \r
7050   case WM_COMMAND:\r
7051     switch (LOWORD(wParam)) {\r
7052     case IDOK:\r
7053       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7054         strcpy(buf, "/fcp=");\r
7055         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7056         p = buf;\r
7057         ParseArgs(StringGet, &p);\r
7058         strcpy(buf, "/scp=");\r
7059         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7060         p = buf;\r
7061         ParseArgs(StringGet, &p);\r
7062         appData.noChessProgram = FALSE;\r
7063         appData.icsActive = FALSE;\r
7064       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7065         strcpy(buf, "/ics /icshost=");\r
7066         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7067         p = buf;\r
7068         ParseArgs(StringGet, &p);\r
7069         if (appData.zippyPlay) {\r
7070           strcpy(buf, "/fcp=");\r
7071           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7072           p = buf;\r
7073           ParseArgs(StringGet, &p);\r
7074         }\r
7075       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7076         appData.noChessProgram = TRUE;\r
7077         appData.icsActive = FALSE;\r
7078       } else {\r
7079         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7080                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7081         return TRUE;\r
7082       }\r
7083       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7084         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7085         p = buf;\r
7086         ParseArgs(StringGet, &p);\r
7087       }\r
7088       EndDialog(hDlg, TRUE);\r
7089       return TRUE;\r
7090 \r
7091     case IDCANCEL:\r
7092       ExitEvent(0);\r
7093       return TRUE;\r
7094 \r
7095     case IDM_HELPCONTENTS:\r
7096       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7097         MessageBox (GetFocus(),\r
7098                     "Unable to activate help",\r
7099                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7100       }\r
7101       break;\r
7102 \r
7103     default:\r
7104       SetStartupDialogEnables(hDlg);\r
7105       break;\r
7106     }\r
7107     break;\r
7108   }\r
7109   return FALSE;\r
7110 }\r
7111 \r
7112 /*---------------------------------------------------------------------------*\\r
7113  *\r
7114  * About box dialog functions\r
7115  *\r
7116 \*---------------------------------------------------------------------------*/\r
7117 \r
7118 /* Process messages for "About" dialog box */\r
7119 LRESULT CALLBACK\r
7120 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7121 {\r
7122   switch (message) {\r
7123   case WM_INITDIALOG: /* message: initialize dialog box */\r
7124     /* Center the dialog over the application window */\r
7125     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7126     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7127     JAWS_COPYRIGHT\r
7128     return (TRUE);\r
7129 \r
7130   case WM_COMMAND: /* message: received a command */\r
7131     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7132         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7133       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7134       return (TRUE);\r
7135     }\r
7136     break;\r
7137   }\r
7138   return (FALSE);\r
7139 }\r
7140 \r
7141 /*---------------------------------------------------------------------------*\\r
7142  *\r
7143  * Comment Dialog functions\r
7144  *\r
7145 \*---------------------------------------------------------------------------*/\r
7146 \r
7147 LRESULT CALLBACK\r
7148 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7149 {\r
7150   static HANDLE hwndText = NULL;\r
7151   int len, newSizeX, newSizeY, flags;\r
7152   static int sizeX, sizeY;\r
7153   char *str;\r
7154   RECT rect;\r
7155   MINMAXINFO *mmi;\r
7156 \r
7157   switch (message) {\r
7158   case WM_INITDIALOG: /* message: initialize dialog box */\r
7159     /* Initialize the dialog items */\r
7160     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7161     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7162     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7163     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7164     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7165     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7166     SetWindowText(hDlg, commentTitle);\r
7167     if (editComment) {\r
7168       SetFocus(hwndText);\r
7169     } else {\r
7170       SetFocus(GetDlgItem(hDlg, IDOK));\r
7171     }\r
7172     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7173                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7174                 MAKELPARAM(FALSE, 0));\r
7175     /* Size and position the dialog */\r
7176     if (!commentDialog) {\r
7177       commentDialog = hDlg;\r
7178       flags = SWP_NOZORDER;\r
7179       GetClientRect(hDlg, &rect);\r
7180       sizeX = rect.right;\r
7181       sizeY = rect.bottom;\r
7182       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7183           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7184         WINDOWPLACEMENT wp;\r
7185         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7186         wp.length = sizeof(WINDOWPLACEMENT);\r
7187         wp.flags = 0;\r
7188         wp.showCmd = SW_SHOW;\r
7189         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7190         wp.rcNormalPosition.left = commentX;\r
7191         wp.rcNormalPosition.right = commentX + commentW;\r
7192         wp.rcNormalPosition.top = commentY;\r
7193         wp.rcNormalPosition.bottom = commentY + commentH;\r
7194         SetWindowPlacement(hDlg, &wp);\r
7195 \r
7196         GetClientRect(hDlg, &rect);\r
7197         newSizeX = rect.right;\r
7198         newSizeY = rect.bottom;\r
7199         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7200                               newSizeX, newSizeY);\r
7201         sizeX = newSizeX;\r
7202         sizeY = newSizeY;\r
7203       }\r
7204     }\r
7205     return FALSE;\r
7206 \r
7207   case WM_COMMAND: /* message: received a command */\r
7208     switch (LOWORD(wParam)) {\r
7209     case IDOK:\r
7210       if (editComment) {\r
7211         char *p, *q;\r
7212         /* Read changed options from the dialog box */\r
7213         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7214         len = GetWindowTextLength(hwndText);\r
7215         str = (char *) malloc(len + 1);\r
7216         GetWindowText(hwndText, str, len + 1);\r
7217         p = q = str;\r
7218         while (*q) {\r
7219           if (*q == '\r')\r
7220             q++;\r
7221           else\r
7222             *p++ = *q++;\r
7223         }\r
7224         *p = NULLCHAR;\r
7225         ReplaceComment(commentIndex, str);\r
7226         free(str);\r
7227       }\r
7228       CommentPopDown();\r
7229       return TRUE;\r
7230 \r
7231     case IDCANCEL:\r
7232     case OPT_CancelComment:\r
7233       CommentPopDown();\r
7234       return TRUE;\r
7235 \r
7236     case OPT_ClearComment:\r
7237       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7238       break;\r
7239 \r
7240     case OPT_EditComment:\r
7241       EditCommentEvent();\r
7242       return TRUE;\r
7243 \r
7244     default:\r
7245       break;\r
7246     }\r
7247     break;\r
7248 \r
7249   case WM_SIZE:\r
7250     newSizeX = LOWORD(lParam);\r
7251     newSizeY = HIWORD(lParam);\r
7252     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7253     sizeX = newSizeX;\r
7254     sizeY = newSizeY;\r
7255     break;\r
7256 \r
7257   case WM_GETMINMAXINFO:\r
7258     /* Prevent resizing window too small */\r
7259     mmi = (MINMAXINFO *) lParam;\r
7260     mmi->ptMinTrackSize.x = 100;\r
7261     mmi->ptMinTrackSize.y = 100;\r
7262     break;\r
7263   }\r
7264   return FALSE;\r
7265 }\r
7266 \r
7267 VOID\r
7268 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7269 {\r
7270   FARPROC lpProc;\r
7271   char *p, *q;\r
7272 \r
7273   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7274 \r
7275   if (str == NULL) str = "";\r
7276   p = (char *) malloc(2 * strlen(str) + 2);\r
7277   q = p;\r
7278   while (*str) {\r
7279     if (*str == '\n') *q++ = '\r';\r
7280     *q++ = *str++;\r
7281   }\r
7282   *q = NULLCHAR;\r
7283   if (commentText != NULL) free(commentText);\r
7284 \r
7285   commentIndex = index;\r
7286   commentTitle = title;\r
7287   commentText = p;\r
7288   editComment = edit;\r
7289 \r
7290   if (commentDialog) {\r
7291     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7292     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7293   } else {\r
7294     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7295     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7296                  hwndMain, (DLGPROC)lpProc);\r
7297     FreeProcInstance(lpProc);\r
7298   }\r
7299   commentDialogUp = TRUE;\r
7300 }\r
7301 \r
7302 \r
7303 /*---------------------------------------------------------------------------*\\r
7304  *\r
7305  * Type-in move dialog functions\r
7306  * \r
7307 \*---------------------------------------------------------------------------*/\r
7308 \r
7309 LRESULT CALLBACK\r
7310 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7311 {\r
7312   char move[MSG_SIZ];\r
7313   HWND hInput;\r
7314   ChessMove moveType;\r
7315   int fromX, fromY, toX, toY;\r
7316   char promoChar;\r
7317 \r
7318   switch (message) {\r
7319   case WM_INITDIALOG:\r
7320     move[0] = (char) lParam;\r
7321     move[1] = NULLCHAR;\r
7322     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7323     hInput = GetDlgItem(hDlg, OPT_Move);\r
7324     SetWindowText(hInput, move);\r
7325     SetFocus(hInput);\r
7326     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7327     return FALSE;\r
7328 \r
7329   case WM_COMMAND:\r
7330     switch (LOWORD(wParam)) {\r
7331     case IDOK:\r
7332       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7333       { int n; Board board;\r
7334         // [HGM] FENedit\r
7335         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7336                 EditPositionPasteFEN(move);\r
7337                 EndDialog(hDlg, TRUE);\r
7338                 return TRUE;\r
7339         }\r
7340         // [HGM] movenum: allow move number to be typed in any mode\r
7341         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7342           currentMove = 2*n-1;\r
7343           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7344           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7345           EndDialog(hDlg, TRUE);\r
7346           DrawPosition(TRUE, boards[currentMove]);\r
7347           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7348           else DisplayMessage("", "");\r
7349           return TRUE;\r
7350         }\r
7351       }\r
7352       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7353         gameMode != Training) {\r
7354         DisplayMoveError("Displayed move is not current");\r
7355       } else {\r
7356 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7357         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7358           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7359         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7360         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7361           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7362           if (gameMode != Training)\r
7363               forwardMostMove = currentMove;\r
7364           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7365         } else {\r
7366           DisplayMoveError("Could not parse move");\r
7367         }\r
7368       }\r
7369       EndDialog(hDlg, TRUE);\r
7370       return TRUE;\r
7371     case IDCANCEL:\r
7372       EndDialog(hDlg, FALSE);\r
7373       return TRUE;\r
7374     default:\r
7375       break;\r
7376     }\r
7377     break;\r
7378   }\r
7379   return FALSE;\r
7380 }\r
7381 \r
7382 VOID\r
7383 PopUpMoveDialog(char firstchar)\r
7384 {\r
7385     FARPROC lpProc;\r
7386     \r
7387     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7388         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7389         gameMode == AnalyzeMode || gameMode == EditGame || \r
7390         gameMode == EditPosition || gameMode == IcsExamining ||\r
7391         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7392         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7393                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7394                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7395         gameMode == Training) {\r
7396       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7397       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7398         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7399       FreeProcInstance(lpProc);\r
7400     }\r
7401 }\r
7402 \r
7403 /*---------------------------------------------------------------------------*\\r
7404  *\r
7405  * Type-in name dialog functions\r
7406  * \r
7407 \*---------------------------------------------------------------------------*/\r
7408 \r
7409 LRESULT CALLBACK\r
7410 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7411 {\r
7412   char move[MSG_SIZ];\r
7413   HWND hInput;\r
7414 \r
7415   switch (message) {\r
7416   case WM_INITDIALOG:\r
7417     move[0] = (char) lParam;\r
7418     move[1] = NULLCHAR;\r
7419     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7420     hInput = GetDlgItem(hDlg, OPT_Name);\r
7421     SetWindowText(hInput, move);\r
7422     SetFocus(hInput);\r
7423     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7424     return FALSE;\r
7425 \r
7426   case WM_COMMAND:\r
7427     switch (LOWORD(wParam)) {\r
7428     case IDOK:\r
7429       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7430       appData.userName = strdup(move);\r
7431       SetUserLogo();\r
7432 \r
7433       EndDialog(hDlg, TRUE);\r
7434       return TRUE;\r
7435     case IDCANCEL:\r
7436       EndDialog(hDlg, FALSE);\r
7437       return TRUE;\r
7438     default:\r
7439       break;\r
7440     }\r
7441     break;\r
7442   }\r
7443   return FALSE;\r
7444 }\r
7445 \r
7446 VOID\r
7447 PopUpNameDialog(char firstchar)\r
7448 {\r
7449     FARPROC lpProc;\r
7450     \r
7451       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7452       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7453         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7454       FreeProcInstance(lpProc);\r
7455 }\r
7456 \r
7457 /*---------------------------------------------------------------------------*\\r
7458  *\r
7459  *  Error dialogs\r
7460  * \r
7461 \*---------------------------------------------------------------------------*/\r
7462 \r
7463 /* Nonmodal error box */\r
7464 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7465                              WPARAM wParam, LPARAM lParam);\r
7466 \r
7467 VOID\r
7468 ErrorPopUp(char *title, char *content)\r
7469 {\r
7470   FARPROC lpProc;\r
7471   char *p, *q;\r
7472   BOOLEAN modal = hwndMain == NULL;\r
7473 \r
7474   p = content;\r
7475   q = errorMessage;\r
7476   while (*p) {\r
7477     if (*p == '\n') {\r
7478       if (modal) {\r
7479         *q++ = ' ';\r
7480         p++;\r
7481       } else {\r
7482         *q++ = '\r';\r
7483         *q++ = *p++;\r
7484       }\r
7485     } else {\r
7486       *q++ = *p++;\r
7487     }\r
7488   }\r
7489   *q = NULLCHAR;\r
7490   strncpy(errorTitle, title, sizeof(errorTitle));\r
7491   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7492   \r
7493   if (modal) {\r
7494     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7495   } else {\r
7496     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7497     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7498                  hwndMain, (DLGPROC)lpProc);\r
7499     FreeProcInstance(lpProc);\r
7500   }\r
7501 }\r
7502 \r
7503 VOID\r
7504 ErrorPopDown()\r
7505 {\r
7506   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7507   if (errorDialog == NULL) return;\r
7508   DestroyWindow(errorDialog);\r
7509   errorDialog = NULL;\r
7510 }\r
7511 \r
7512 LRESULT CALLBACK\r
7513 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7514 {\r
7515   HANDLE hwndText;\r
7516   RECT rChild;\r
7517 \r
7518   switch (message) {\r
7519   case WM_INITDIALOG:\r
7520     GetWindowRect(hDlg, &rChild);\r
7521 \r
7522     /*\r
7523     SetWindowPos(hDlg, NULL, rChild.left,\r
7524       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7525       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7526     */\r
7527 \r
7528     /* \r
7529         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7530         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7531         and it doesn't work when you resize the dialog.\r
7532         For now, just give it a default position.\r
7533     */\r
7534     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7535 \r
7536     errorDialog = hDlg;\r
7537     SetWindowText(hDlg, errorTitle);\r
7538     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7539     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7540     return FALSE;\r
7541 \r
7542   case WM_COMMAND:\r
7543     switch (LOWORD(wParam)) {\r
7544     case IDOK:\r
7545     case IDCANCEL:\r
7546       if (errorDialog == hDlg) errorDialog = NULL;\r
7547       DestroyWindow(hDlg);\r
7548       return TRUE;\r
7549 \r
7550     default:\r
7551       break;\r
7552     }\r
7553     break;\r
7554   }\r
7555   return FALSE;\r
7556 }\r
7557 \r
7558 #ifdef GOTHIC\r
7559 HWND gothicDialog = NULL;\r
7560 \r
7561 LRESULT CALLBACK\r
7562 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7563 {\r
7564   HANDLE hwndText;\r
7565   RECT rChild;\r
7566   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7567 \r
7568   switch (message) {\r
7569   case WM_INITDIALOG:\r
7570     GetWindowRect(hDlg, &rChild);\r
7571 \r
7572     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7573                                                              SWP_NOZORDER);\r
7574 \r
7575     /* \r
7576         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7577         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7578         and it doesn't work when you resize the dialog.\r
7579         For now, just give it a default position.\r
7580     */\r
7581     gothicDialog = hDlg;\r
7582     SetWindowText(hDlg, errorTitle);\r
7583     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7584     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7585     return FALSE;\r
7586 \r
7587   case WM_COMMAND:\r
7588     switch (LOWORD(wParam)) {\r
7589     case IDOK:\r
7590     case IDCANCEL:\r
7591       if (errorDialog == hDlg) errorDialog = NULL;\r
7592       DestroyWindow(hDlg);\r
7593       return TRUE;\r
7594 \r
7595     default:\r
7596       break;\r
7597     }\r
7598     break;\r
7599   }\r
7600   return FALSE;\r
7601 }\r
7602 \r
7603 VOID\r
7604 GothicPopUp(char *title, VariantClass variant)\r
7605 {\r
7606   FARPROC lpProc;\r
7607   static char *lastTitle;\r
7608 \r
7609   strncpy(errorTitle, title, sizeof(errorTitle));\r
7610   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7611 \r
7612   if(lastTitle != title && gothicDialog != NULL) {\r
7613     DestroyWindow(gothicDialog);\r
7614     gothicDialog = NULL;\r
7615   }\r
7616   if(variant != VariantNormal && gothicDialog == NULL) {\r
7617     title = lastTitle;\r
7618     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7619     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7620                  hwndMain, (DLGPROC)lpProc);\r
7621     FreeProcInstance(lpProc);\r
7622   }\r
7623 }\r
7624 #endif\r
7625 \r
7626 /*---------------------------------------------------------------------------*\\r
7627  *\r
7628  *  Ics Interaction console functions\r
7629  *\r
7630 \*---------------------------------------------------------------------------*/\r
7631 \r
7632 #define HISTORY_SIZE 64\r
7633 static char *history[HISTORY_SIZE];\r
7634 int histIn = 0, histP = 0;\r
7635 \r
7636 VOID\r
7637 SaveInHistory(char *cmd)\r
7638 {\r
7639   if (history[histIn] != NULL) {\r
7640     free(history[histIn]);\r
7641     history[histIn] = NULL;\r
7642   }\r
7643   if (*cmd == NULLCHAR) return;\r
7644   history[histIn] = StrSave(cmd);\r
7645   histIn = (histIn + 1) % HISTORY_SIZE;\r
7646   if (history[histIn] != NULL) {\r
7647     free(history[histIn]);\r
7648     history[histIn] = NULL;\r
7649   }\r
7650   histP = histIn;\r
7651 }\r
7652 \r
7653 char *\r
7654 PrevInHistory(char *cmd)\r
7655 {\r
7656   int newhp;\r
7657   if (histP == histIn) {\r
7658     if (history[histIn] != NULL) free(history[histIn]);\r
7659     history[histIn] = StrSave(cmd);\r
7660   }\r
7661   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7662   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7663   histP = newhp;\r
7664   return history[histP];\r
7665 }\r
7666 \r
7667 char *\r
7668 NextInHistory()\r
7669 {\r
7670   if (histP == histIn) return NULL;\r
7671   histP = (histP + 1) % HISTORY_SIZE;\r
7672   return history[histP];\r
7673 }\r
7674 \r
7675 typedef struct {\r
7676   char *item;\r
7677   char *command;\r
7678   BOOLEAN getname;\r
7679   BOOLEAN immediate;\r
7680 } IcsTextMenuEntry;\r
7681 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7682 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7683 \r
7684 void\r
7685 ParseIcsTextMenu(char *icsTextMenuString)\r
7686 {\r
7687 //  int flags = 0;\r
7688   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7689   char *p = icsTextMenuString;\r
7690   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7691     free(e->item);\r
7692     e->item = NULL;\r
7693     if (e->command != NULL) {\r
7694       free(e->command);\r
7695       e->command = NULL;\r
7696     }\r
7697     e++;\r
7698   }\r
7699   e = icsTextMenuEntry;\r
7700   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7701     if (*p == ';' || *p == '\n') {\r
7702       e->item = strdup("-");\r
7703       e->command = NULL;\r
7704       p++;\r
7705     } else if (*p == '-') {\r
7706       e->item = strdup("-");\r
7707       e->command = NULL;\r
7708       p++;\r
7709       if (*p) p++;\r
7710     } else {\r
7711       char *q, *r, *s, *t;\r
7712       char c;\r
7713       q = strchr(p, ',');\r
7714       if (q == NULL) break;\r
7715       *q = NULLCHAR;\r
7716       r = strchr(q + 1, ',');\r
7717       if (r == NULL) break;\r
7718       *r = NULLCHAR;\r
7719       s = strchr(r + 1, ',');\r
7720       if (s == NULL) break;\r
7721       *s = NULLCHAR;\r
7722       c = ';';\r
7723       t = strchr(s + 1, c);\r
7724       if (t == NULL) {\r
7725         c = '\n';\r
7726         t = strchr(s + 1, c);\r
7727       }\r
7728       if (t != NULL) *t = NULLCHAR;\r
7729       e->item = strdup(p);\r
7730       e->command = strdup(q + 1);\r
7731       e->getname = *(r + 1) != '0';\r
7732       e->immediate = *(s + 1) != '0';\r
7733       *q = ',';\r
7734       *r = ',';\r
7735       *s = ',';\r
7736       if (t == NULL) break;\r
7737       *t = c;\r
7738       p = t + 1;\r
7739     }\r
7740     e++;\r
7741   } \r
7742 }\r
7743 \r
7744 HMENU\r
7745 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7746 {\r
7747   HMENU hmenu, h;\r
7748   int i = 0;\r
7749   hmenu = LoadMenu(hInst, "TextMenu");\r
7750   h = GetSubMenu(hmenu, 0);\r
7751   while (e->item) {\r
7752     if (strcmp(e->item, "-") == 0) {\r
7753       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7754     } else {\r
7755       if (e->item[0] == '|') {\r
7756         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7757                    IDM_CommandX + i, &e->item[1]);\r
7758       } else {\r
7759         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7760       }\r
7761     }\r
7762     e++;\r
7763     i++;\r
7764   } \r
7765   return hmenu;\r
7766 }\r
7767 \r
7768 WNDPROC consoleTextWindowProc;\r
7769 \r
7770 void\r
7771 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7772 {\r
7773   char buf[MSG_SIZ], name[MSG_SIZ];\r
7774   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7775   CHARRANGE sel;\r
7776 \r
7777   if (!getname) {\r
7778     SetWindowText(hInput, command);\r
7779     if (immediate) {\r
7780       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7781     } else {\r
7782       sel.cpMin = 999999;\r
7783       sel.cpMax = 999999;\r
7784       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7785       SetFocus(hInput);\r
7786     }\r
7787     return;\r
7788   }    \r
7789   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7790   if (sel.cpMin == sel.cpMax) {\r
7791     /* Expand to surrounding word */\r
7792     TEXTRANGE tr;\r
7793     do {\r
7794       tr.chrg.cpMax = sel.cpMin;\r
7795       tr.chrg.cpMin = --sel.cpMin;\r
7796       if (sel.cpMin < 0) break;\r
7797       tr.lpstrText = name;\r
7798       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7799     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7800     sel.cpMin++;\r
7801 \r
7802     do {\r
7803       tr.chrg.cpMin = sel.cpMax;\r
7804       tr.chrg.cpMax = ++sel.cpMax;\r
7805       tr.lpstrText = name;\r
7806       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7807     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7808     sel.cpMax--;\r
7809 \r
7810     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7811       MessageBeep(MB_ICONEXCLAMATION);\r
7812       return;\r
7813     }\r
7814     tr.chrg = sel;\r
7815     tr.lpstrText = name;\r
7816     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7817   } else {\r
7818     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7819       MessageBeep(MB_ICONEXCLAMATION);\r
7820       return;\r
7821     }\r
7822     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7823   }\r
7824   if (immediate) {\r
7825     sprintf(buf, "%s %s", command, name);\r
7826     SetWindowText(hInput, buf);\r
7827     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7828   } else {\r
7829     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7830     SetWindowText(hInput, buf);\r
7831     sel.cpMin = 999999;\r
7832     sel.cpMax = 999999;\r
7833     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7834     SetFocus(hInput);\r
7835   }\r
7836 }\r
7837 \r
7838 LRESULT CALLBACK \r
7839 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7840 {\r
7841   HWND hInput;\r
7842   CHARRANGE sel;\r
7843 \r
7844   switch (message) {\r
7845   case WM_KEYDOWN:\r
7846     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7847     switch (wParam) {\r
7848     case VK_PRIOR:\r
7849       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7850       return 0;\r
7851     case VK_NEXT:\r
7852       sel.cpMin = 999999;\r
7853       sel.cpMax = 999999;\r
7854       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7855       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7856       return 0;\r
7857     }\r
7858     break;\r
7859   case WM_CHAR:\r
7860    if(wParam != '\022') {\r
7861     if (wParam == '\t') {\r
7862       if (GetKeyState(VK_SHIFT) < 0) {\r
7863         /* shifted */\r
7864         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7865         if (buttonDesc[0].hwnd) {\r
7866           SetFocus(buttonDesc[0].hwnd);\r
7867         } else {\r
7868           SetFocus(hwndMain);\r
7869         }\r
7870       } else {\r
7871         /* unshifted */\r
7872         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7873       }\r
7874     } else {\r
7875       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7876       JAWS_DELETE( SetFocus(hInput); )\r
7877       SendMessage(hInput, message, wParam, lParam);\r
7878     }\r
7879     return 0;\r
7880    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7881   case WM_RBUTTONUP:\r
7882     if (GetKeyState(VK_SHIFT) & ~1) {\r
7883       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7884         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7885     } else {\r
7886       POINT pt;\r
7887       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7888       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7889       if (sel.cpMin == sel.cpMax) {\r
7890         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7891         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7892       }\r
7893       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7894         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7895       }\r
7896       pt.x = LOWORD(lParam);\r
7897       pt.y = HIWORD(lParam);\r
7898       MenuPopup(hwnd, pt, hmenu, -1);\r
7899     }\r
7900     return 0;\r
7901   case WM_PASTE:\r
7902     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7903     SetFocus(hInput);\r
7904     return SendMessage(hInput, message, wParam, lParam);\r
7905   case WM_MBUTTONDOWN:\r
7906     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7907   case WM_RBUTTONDOWN:\r
7908     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7909       /* Move selection here if it was empty */\r
7910       POINT pt;\r
7911       pt.x = LOWORD(lParam);\r
7912       pt.y = HIWORD(lParam);\r
7913       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7914       if (sel.cpMin == sel.cpMax) {\r
7915         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7916         sel.cpMax = sel.cpMin;\r
7917         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7918       }\r
7919       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7920     }\r
7921     return 0;\r
7922   case WM_COMMAND:\r
7923     switch (LOWORD(wParam)) {\r
7924     case IDM_QuickPaste:\r
7925       {\r
7926         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7927         if (sel.cpMin == sel.cpMax) {\r
7928           MessageBeep(MB_ICONEXCLAMATION);\r
7929           return 0;\r
7930         }\r
7931         SendMessage(hwnd, WM_COPY, 0, 0);\r
7932         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7933         SendMessage(hInput, WM_PASTE, 0, 0);\r
7934         SetFocus(hInput);\r
7935         return 0;\r
7936       }\r
7937     case IDM_Cut:\r
7938       SendMessage(hwnd, WM_CUT, 0, 0);\r
7939       return 0;\r
7940     case IDM_Paste:\r
7941       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7942       return 0;\r
7943     case IDM_Copy:\r
7944       SendMessage(hwnd, WM_COPY, 0, 0);\r
7945       return 0;\r
7946     default:\r
7947       {\r
7948         int i = LOWORD(wParam) - IDM_CommandX;\r
7949         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7950             icsTextMenuEntry[i].command != NULL) {\r
7951           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7952                    icsTextMenuEntry[i].getname,\r
7953                    icsTextMenuEntry[i].immediate);\r
7954           return 0;\r
7955         }\r
7956       }\r
7957       break;\r
7958     }\r
7959     break;\r
7960   }\r
7961   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7962 }\r
7963 \r
7964 WNDPROC consoleInputWindowProc;\r
7965 \r
7966 LRESULT CALLBACK\r
7967 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7968 {\r
7969   char buf[MSG_SIZ];\r
7970   char *p;\r
7971   static BOOL sendNextChar = FALSE;\r
7972   static BOOL quoteNextChar = FALSE;\r
7973   InputSource *is = consoleInputSource;\r
7974   CHARFORMAT cf;\r
7975   CHARRANGE sel;\r
7976 \r
7977   switch (message) {\r
7978   case WM_CHAR:\r
7979     if (!appData.localLineEditing || sendNextChar) {\r
7980       is->buf[0] = (CHAR) wParam;\r
7981       is->count = 1;\r
7982       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7983       sendNextChar = FALSE;\r
7984       return 0;\r
7985     }\r
7986     if (quoteNextChar) {\r
7987       buf[0] = (char) wParam;\r
7988       buf[1] = NULLCHAR;\r
7989       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7990       quoteNextChar = FALSE;\r
7991       return 0;\r
7992     }\r
7993     switch (wParam) {\r
7994     case '\r':   /* Enter key */\r
7995       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7996       if (consoleEcho) SaveInHistory(is->buf);\r
7997       is->buf[is->count++] = '\n';\r
7998       is->buf[is->count] = NULLCHAR;\r
7999       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8000       if (consoleEcho) {\r
8001         ConsoleOutput(is->buf, is->count, TRUE);\r
8002       } else if (appData.localLineEditing) {\r
8003         ConsoleOutput("\n", 1, TRUE);\r
8004       }\r
8005       /* fall thru */\r
8006     case '\033': /* Escape key */\r
8007       SetWindowText(hwnd, "");\r
8008       cf.cbSize = sizeof(CHARFORMAT);\r
8009       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8010       if (consoleEcho) {\r
8011         cf.crTextColor = textAttribs[ColorNormal].color;\r
8012       } else {\r
8013         cf.crTextColor = COLOR_ECHOOFF;\r
8014       }\r
8015       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8016       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8017       return 0;\r
8018     case '\t':   /* Tab key */\r
8019       if (GetKeyState(VK_SHIFT) < 0) {\r
8020         /* shifted */\r
8021         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8022       } else {\r
8023         /* unshifted */\r
8024         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8025         if (buttonDesc[0].hwnd) {\r
8026           SetFocus(buttonDesc[0].hwnd);\r
8027         } else {\r
8028           SetFocus(hwndMain);\r
8029         }\r
8030       }\r
8031       return 0;\r
8032     case '\023': /* Ctrl+S */\r
8033       sendNextChar = TRUE;\r
8034       return 0;\r
8035     case '\021': /* Ctrl+Q */\r
8036       quoteNextChar = TRUE;\r
8037       return 0;\r
8038     JAWS_REPLAY\r
8039     default:\r
8040       break;\r
8041     }\r
8042     break;\r
8043   case WM_KEYDOWN:\r
8044     switch (wParam) {\r
8045     case VK_UP:\r
8046       GetWindowText(hwnd, buf, MSG_SIZ);\r
8047       p = PrevInHistory(buf);\r
8048       if (p != NULL) {\r
8049         SetWindowText(hwnd, p);\r
8050         sel.cpMin = 999999;\r
8051         sel.cpMax = 999999;\r
8052         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8053         return 0;\r
8054       }\r
8055       break;\r
8056     case VK_DOWN:\r
8057       p = NextInHistory();\r
8058       if (p != NULL) {\r
8059         SetWindowText(hwnd, p);\r
8060         sel.cpMin = 999999;\r
8061         sel.cpMax = 999999;\r
8062         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8063         return 0;\r
8064       }\r
8065       break;\r
8066     case VK_HOME:\r
8067     case VK_END:\r
8068       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8069       /* fall thru */\r
8070     case VK_PRIOR:\r
8071     case VK_NEXT:\r
8072       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8073       return 0;\r
8074     }\r
8075     break;\r
8076   case WM_MBUTTONDOWN:\r
8077     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8078       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8079     break;\r
8080   case WM_RBUTTONUP:\r
8081     if (GetKeyState(VK_SHIFT) & ~1) {\r
8082       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8083         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8084     } else {\r
8085       POINT pt;\r
8086       HMENU hmenu;\r
8087       hmenu = LoadMenu(hInst, "InputMenu");\r
8088       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8089       if (sel.cpMin == sel.cpMax) {\r
8090         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8091         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8092       }\r
8093       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8094         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8095       }\r
8096       pt.x = LOWORD(lParam);\r
8097       pt.y = HIWORD(lParam);\r
8098       MenuPopup(hwnd, pt, hmenu, -1);\r
8099     }\r
8100     return 0;\r
8101   case WM_COMMAND:\r
8102     switch (LOWORD(wParam)) { \r
8103     case IDM_Undo:\r
8104       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8105       return 0;\r
8106     case IDM_SelectAll:\r
8107       sel.cpMin = 0;\r
8108       sel.cpMax = -1; /*999999?*/\r
8109       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8110       return 0;\r
8111     case IDM_Cut:\r
8112       SendMessage(hwnd, WM_CUT, 0, 0);\r
8113       return 0;\r
8114     case IDM_Paste:\r
8115       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8116       return 0;\r
8117     case IDM_Copy:\r
8118       SendMessage(hwnd, WM_COPY, 0, 0);\r
8119       return 0;\r
8120     }\r
8121     break;\r
8122   }\r
8123   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8124 }\r
8125 \r
8126 #define CO_MAX  100000\r
8127 #define CO_TRIM   1000\r
8128 \r
8129 LRESULT CALLBACK\r
8130 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8131 {\r
8132   static SnapData sd;\r
8133   HWND hText, hInput;\r
8134   RECT rect;\r
8135   static int sizeX, sizeY;\r
8136   int newSizeX, newSizeY;\r
8137   MINMAXINFO *mmi;\r
8138   WORD wMask;\r
8139 \r
8140   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8141   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8142 \r
8143   switch (message) {\r
8144   case WM_NOTIFY:\r
8145     if (((NMHDR*)lParam)->code == EN_LINK)\r
8146     {\r
8147       ENLINK *pLink = (ENLINK*)lParam;\r
8148       if (pLink->msg == WM_LBUTTONUP)\r
8149       {\r
8150         TEXTRANGE tr;\r
8151 \r
8152         tr.chrg = pLink->chrg;\r
8153         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
8154         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
8155         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
8156         free(tr.lpstrText);\r
8157       }\r
8158     }\r
8159     break;\r
8160   case WM_INITDIALOG: /* message: initialize dialog box */\r
8161     hwndConsole = hDlg;\r
8162     SetFocus(hInput);\r
8163     consoleTextWindowProc = (WNDPROC)\r
8164       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8165     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8166     consoleInputWindowProc = (WNDPROC)\r
8167       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8168     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8169     Colorize(ColorNormal, TRUE);\r
8170     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8171     ChangedConsoleFont();\r
8172     GetClientRect(hDlg, &rect);\r
8173     sizeX = rect.right;\r
8174     sizeY = rect.bottom;\r
8175     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8176         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8177       WINDOWPLACEMENT wp;\r
8178       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8179       wp.length = sizeof(WINDOWPLACEMENT);\r
8180       wp.flags = 0;\r
8181       wp.showCmd = SW_SHOW;\r
8182       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8183       wp.rcNormalPosition.left = wpConsole.x;\r
8184       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8185       wp.rcNormalPosition.top = wpConsole.y;\r
8186       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8187       SetWindowPlacement(hDlg, &wp);\r
8188     }\r
8189 \r
8190    // [HGM] Chessknight's change 2004-07-13\r
8191    else { /* Determine Defaults */\r
8192        WINDOWPLACEMENT wp;\r
8193        wpConsole.x = winWidth + 1;\r
8194        wpConsole.y = boardY;\r
8195        wpConsole.width = screenWidth -  winWidth;\r
8196        wpConsole.height = winHeight;\r
8197        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8198        wp.length = sizeof(WINDOWPLACEMENT);\r
8199        wp.flags = 0;\r
8200        wp.showCmd = SW_SHOW;\r
8201        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8202        wp.rcNormalPosition.left = wpConsole.x;\r
8203        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8204        wp.rcNormalPosition.top = wpConsole.y;\r
8205        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8206        SetWindowPlacement(hDlg, &wp);\r
8207     }\r
8208 \r
8209    // Allow hText to highlight URLs and send notifications on them\r
8210    wMask = SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
8211    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
8212    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
8213    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
8214 \r
8215     return FALSE;\r
8216 \r
8217   case WM_SETFOCUS:\r
8218     SetFocus(hInput);\r
8219     return 0;\r
8220 \r
8221   case WM_CLOSE:\r
8222     ExitEvent(0);\r
8223     /* not reached */\r
8224     break;\r
8225 \r
8226   case WM_SIZE:\r
8227     if (IsIconic(hDlg)) break;\r
8228     newSizeX = LOWORD(lParam);\r
8229     newSizeY = HIWORD(lParam);\r
8230     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8231       RECT rectText, rectInput;\r
8232       POINT pt;\r
8233       int newTextHeight, newTextWidth;\r
8234       GetWindowRect(hText, &rectText);\r
8235       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8236       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8237       if (newTextHeight < 0) {\r
8238         newSizeY += -newTextHeight;\r
8239         newTextHeight = 0;\r
8240       }\r
8241       SetWindowPos(hText, NULL, 0, 0,\r
8242         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8243       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8244       pt.x = rectInput.left;\r
8245       pt.y = rectInput.top + newSizeY - sizeY;\r
8246       ScreenToClient(hDlg, &pt);\r
8247       SetWindowPos(hInput, NULL, \r
8248         pt.x, pt.y, /* needs client coords */   \r
8249         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8250         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8251     }\r
8252     sizeX = newSizeX;\r
8253     sizeY = newSizeY;\r
8254     break;\r
8255 \r
8256   case WM_GETMINMAXINFO:\r
8257     /* Prevent resizing window too small */\r
8258     mmi = (MINMAXINFO *) lParam;\r
8259     mmi->ptMinTrackSize.x = 100;\r
8260     mmi->ptMinTrackSize.y = 100;\r
8261     break;\r
8262 \r
8263   /* [AS] Snapping */\r
8264   case WM_ENTERSIZEMOVE:\r
8265     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8266 \r
8267   case WM_SIZING:\r
8268     return OnSizing( &sd, hDlg, wParam, lParam );\r
8269 \r
8270   case WM_MOVING:\r
8271     return OnMoving( &sd, hDlg, wParam, lParam );\r
8272 \r
8273   case WM_EXITSIZEMOVE:\r
8274         UpdateICSWidth(hText);\r
8275     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8276   }\r
8277 \r
8278   return DefWindowProc(hDlg, message, wParam, lParam);\r
8279 }\r
8280 \r
8281 \r
8282 VOID\r
8283 ConsoleCreate()\r
8284 {\r
8285   HWND hCons;\r
8286   if (hwndConsole) return;\r
8287   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8288   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8289 }\r
8290 \r
8291 \r
8292 VOID\r
8293 ConsoleOutput(char* data, int length, int forceVisible)\r
8294 {\r
8295   HWND hText;\r
8296   int trim, exlen;\r
8297   char *p, *q;\r
8298   char buf[CO_MAX+1];\r
8299   POINT pEnd;\r
8300   RECT rect;\r
8301   static int delayLF = 0;\r
8302   CHARRANGE savesel, sel;\r
8303 \r
8304   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8305   p = data;\r
8306   q = buf;\r
8307   if (delayLF) {\r
8308     *q++ = '\r';\r
8309     *q++ = '\n';\r
8310     delayLF = 0;\r
8311   }\r
8312   while (length--) {\r
8313     if (*p == '\n') {\r
8314       if (*++p) {\r
8315         *q++ = '\r';\r
8316         *q++ = '\n';\r
8317       } else {\r
8318         delayLF = 1;\r
8319       }\r
8320     } else if (*p == '\007') {\r
8321        MyPlaySound(&sounds[(int)SoundBell]);\r
8322        p++;\r
8323     } else {\r
8324       *q++ = *p++;\r
8325     }\r
8326   }\r
8327   *q = NULLCHAR;\r
8328   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8329   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8330   /* Save current selection */\r
8331   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8332   exlen = GetWindowTextLength(hText);\r
8333   /* Find out whether current end of text is visible */\r
8334   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8335   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8336   /* Trim existing text if it's too long */\r
8337   if (exlen + (q - buf) > CO_MAX) {\r
8338     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8339     sel.cpMin = 0;\r
8340     sel.cpMax = trim;\r
8341     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8342     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8343     exlen -= trim;\r
8344     savesel.cpMin -= trim;\r
8345     savesel.cpMax -= trim;\r
8346     if (exlen < 0) exlen = 0;\r
8347     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8348     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8349   }\r
8350   /* Append the new text */\r
8351   sel.cpMin = exlen;\r
8352   sel.cpMax = exlen;\r
8353   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8354   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8355   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8356   if (forceVisible || exlen == 0 ||\r
8357       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8358        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8359     /* Scroll to make new end of text visible if old end of text\r
8360        was visible or new text is an echo of user typein */\r
8361     sel.cpMin = 9999999;\r
8362     sel.cpMax = 9999999;\r
8363     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8364     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8365     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8366     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8367   }\r
8368   if (savesel.cpMax == exlen || forceVisible) {\r
8369     /* Move insert point to new end of text if it was at the old\r
8370        end of text or if the new text is an echo of user typein */\r
8371     sel.cpMin = 9999999;\r
8372     sel.cpMax = 9999999;\r
8373     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8374   } else {\r
8375     /* Restore previous selection */\r
8376     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8377   }\r
8378   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8379 }\r
8380 \r
8381 /*---------*/\r
8382 \r
8383 \r
8384 void\r
8385 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8386 {\r
8387   char buf[100];\r
8388   char *str;\r
8389   COLORREF oldFg, oldBg;\r
8390   HFONT oldFont;\r
8391   RECT rect;\r
8392 \r
8393   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8394 \r
8395   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8396   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8397   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8398 \r
8399   rect.left = x;\r
8400   rect.right = x + squareSize;\r
8401   rect.top  = y;\r
8402   rect.bottom = y + squareSize;\r
8403   str = buf;\r
8404 \r
8405   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8406                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8407              y, ETO_CLIPPED|ETO_OPAQUE,\r
8408              &rect, str, strlen(str), NULL);\r
8409 \r
8410   (void) SetTextColor(hdc, oldFg);\r
8411   (void) SetBkColor(hdc, oldBg);\r
8412   (void) SelectObject(hdc, oldFont);\r
8413 }\r
8414 \r
8415 void\r
8416 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8417               RECT *rect, char *color, char *flagFell)\r
8418 {\r
8419   char buf[100];\r
8420   char *str;\r
8421   COLORREF oldFg, oldBg;\r
8422   HFONT oldFont;\r
8423 \r
8424   if (appData.clockMode) {\r
8425     if (tinyLayout)\r
8426       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8427     else\r
8428       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8429     str = buf;\r
8430   } else {\r
8431     str = color;\r
8432   }\r
8433 \r
8434   if (highlight) {\r
8435     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8436     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8437   } else {\r
8438     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8439     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8440   }\r
8441   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8442 \r
8443   JAWS_SILENCE\r
8444 \r
8445   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8446              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8447              rect, str, strlen(str), NULL);\r
8448   if(logoHeight > 0 && appData.clockMode) {\r
8449       RECT r;\r
8450       sprintf(buf, "%s %s", buf+7, flagFell);\r
8451       r.top = rect->top + logoHeight/2;\r
8452       r.left = rect->left;\r
8453       r.right = rect->right;\r
8454       r.bottom = rect->bottom;\r
8455       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8456                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8457                  &r, str, strlen(str), NULL);\r
8458   }\r
8459   (void) SetTextColor(hdc, oldFg);\r
8460   (void) SetBkColor(hdc, oldBg);\r
8461   (void) SelectObject(hdc, oldFont);\r
8462 }\r
8463 \r
8464 \r
8465 int\r
8466 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8467            OVERLAPPED *ovl)\r
8468 {\r
8469   int ok, err;\r
8470 \r
8471   /* [AS]  */\r
8472   if( count <= 0 ) {\r
8473     if (appData.debugMode) {\r
8474       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8475     }\r
8476 \r
8477     return ERROR_INVALID_USER_BUFFER;\r
8478   }\r
8479 \r
8480   ResetEvent(ovl->hEvent);\r
8481   ovl->Offset = ovl->OffsetHigh = 0;\r
8482   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8483   if (ok) {\r
8484     err = NO_ERROR;\r
8485   } else {\r
8486     err = GetLastError();\r
8487     if (err == ERROR_IO_PENDING) {\r
8488       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8489       if (ok)\r
8490         err = NO_ERROR;\r
8491       else\r
8492         err = GetLastError();\r
8493     }\r
8494   }\r
8495   return err;\r
8496 }\r
8497 \r
8498 int\r
8499 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8500             OVERLAPPED *ovl)\r
8501 {\r
8502   int ok, err;\r
8503 \r
8504   ResetEvent(ovl->hEvent);\r
8505   ovl->Offset = ovl->OffsetHigh = 0;\r
8506   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8507   if (ok) {\r
8508     err = NO_ERROR;\r
8509   } else {\r
8510     err = GetLastError();\r
8511     if (err == ERROR_IO_PENDING) {\r
8512       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8513       if (ok)\r
8514         err = NO_ERROR;\r
8515       else\r
8516         err = GetLastError();\r
8517     }\r
8518   }\r
8519   return err;\r
8520 }\r
8521 \r
8522 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8523 void CheckForInputBufferFull( InputSource * is )\r
8524 {\r
8525     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8526         /* Look for end of line */\r
8527         char * p = is->buf;\r
8528         \r
8529         while( p < is->next && *p != '\n' ) {\r
8530             p++;\r
8531         }\r
8532 \r
8533         if( p >= is->next ) {\r
8534             if (appData.debugMode) {\r
8535                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8536             }\r
8537 \r
8538             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8539             is->count = (DWORD) -1;\r
8540             is->next = is->buf;\r
8541         }\r
8542     }\r
8543 }\r
8544 \r
8545 DWORD\r
8546 InputThread(LPVOID arg)\r
8547 {\r
8548   InputSource *is;\r
8549   OVERLAPPED ovl;\r
8550 \r
8551   is = (InputSource *) arg;\r
8552   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8553   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8554   while (is->hThread != NULL) {\r
8555     is->error = DoReadFile(is->hFile, is->next,\r
8556                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8557                            &is->count, &ovl);\r
8558     if (is->error == NO_ERROR) {\r
8559       is->next += is->count;\r
8560     } else {\r
8561       if (is->error == ERROR_BROKEN_PIPE) {\r
8562         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8563         is->count = 0;\r
8564       } else {\r
8565         is->count = (DWORD) -1;\r
8566         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8567         break; \r
8568       }\r
8569     }\r
8570 \r
8571     CheckForInputBufferFull( is );\r
8572 \r
8573     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8574 \r
8575     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8576 \r
8577     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8578   }\r
8579 \r
8580   CloseHandle(ovl.hEvent);\r
8581   CloseHandle(is->hFile);\r
8582 \r
8583   if (appData.debugMode) {\r
8584     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8585   }\r
8586 \r
8587   return 0;\r
8588 }\r
8589 \r
8590 \r
8591 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8592 DWORD\r
8593 NonOvlInputThread(LPVOID arg)\r
8594 {\r
8595   InputSource *is;\r
8596   char *p, *q;\r
8597   int i;\r
8598   char prev;\r
8599 \r
8600   is = (InputSource *) arg;\r
8601   while (is->hThread != NULL) {\r
8602     is->error = ReadFile(is->hFile, is->next,\r
8603                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8604                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8605     if (is->error == NO_ERROR) {\r
8606       /* Change CRLF to LF */\r
8607       if (is->next > is->buf) {\r
8608         p = is->next - 1;\r
8609         i = is->count + 1;\r
8610       } else {\r
8611         p = is->next;\r
8612         i = is->count;\r
8613       }\r
8614       q = p;\r
8615       prev = NULLCHAR;\r
8616       while (i > 0) {\r
8617         if (prev == '\r' && *p == '\n') {\r
8618           *(q-1) = '\n';\r
8619           is->count--;\r
8620         } else { \r
8621           *q++ = *p;\r
8622         }\r
8623         prev = *p++;\r
8624         i--;\r
8625       }\r
8626       *q = NULLCHAR;\r
8627       is->next = q;\r
8628     } else {\r
8629       if (is->error == ERROR_BROKEN_PIPE) {\r
8630         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8631         is->count = 0; \r
8632       } else {\r
8633         is->count = (DWORD) -1;\r
8634       }\r
8635     }\r
8636 \r
8637     CheckForInputBufferFull( is );\r
8638 \r
8639     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8640 \r
8641     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8642 \r
8643     if (is->count < 0) break;  /* Quit on error */\r
8644   }\r
8645   CloseHandle(is->hFile);\r
8646   return 0;\r
8647 }\r
8648 \r
8649 DWORD\r
8650 SocketInputThread(LPVOID arg)\r
8651 {\r
8652   InputSource *is;\r
8653 \r
8654   is = (InputSource *) arg;\r
8655   while (is->hThread != NULL) {\r
8656     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8657     if ((int)is->count == SOCKET_ERROR) {\r
8658       is->count = (DWORD) -1;\r
8659       is->error = WSAGetLastError();\r
8660     } else {\r
8661       is->error = NO_ERROR;\r
8662       is->next += is->count;\r
8663       if (is->count == 0 && is->second == is) {\r
8664         /* End of file on stderr; quit with no message */\r
8665         break;\r
8666       }\r
8667     }\r
8668     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8669 \r
8670     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8671 \r
8672     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8673   }\r
8674   return 0;\r
8675 }\r
8676 \r
8677 VOID\r
8678 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8679 {\r
8680   InputSource *is;\r
8681 \r
8682   is = (InputSource *) lParam;\r
8683   if (is->lineByLine) {\r
8684     /* Feed in lines one by one */\r
8685     char *p = is->buf;\r
8686     char *q = p;\r
8687     while (q < is->next) {\r
8688       if (*q++ == '\n') {\r
8689         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8690         p = q;\r
8691       }\r
8692     }\r
8693     \r
8694     /* Move any partial line to the start of the buffer */\r
8695     q = is->buf;\r
8696     while (p < is->next) {\r
8697       *q++ = *p++;\r
8698     }\r
8699     is->next = q;\r
8700 \r
8701     if (is->error != NO_ERROR || is->count == 0) {\r
8702       /* Notify backend of the error.  Note: If there was a partial\r
8703          line at the end, it is not flushed through. */\r
8704       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8705     }\r
8706   } else {\r
8707     /* Feed in the whole chunk of input at once */\r
8708     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8709     is->next = is->buf;\r
8710   }\r
8711 }\r
8712 \r
8713 /*---------------------------------------------------------------------------*\\r
8714  *\r
8715  *  Menu enables. Used when setting various modes.\r
8716  *\r
8717 \*---------------------------------------------------------------------------*/\r
8718 \r
8719 typedef struct {\r
8720   int item;\r
8721   int flags;\r
8722 } Enables;\r
8723 \r
8724 VOID\r
8725 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8726 {\r
8727   while (enab->item > 0) {\r
8728     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8729     enab++;\r
8730   }\r
8731 }\r
8732 \r
8733 Enables gnuEnables[] = {\r
8734   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8735   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8736   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8737   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8738   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8739   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8740   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8741   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8742   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8743   { IDM_Revert, 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_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8754   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8755   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8756   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8757   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8758   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8759   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8760   { -1, -1 }\r
8761 };\r
8762 \r
8763 #ifdef ZIPPY\r
8764 Enables zippyEnables[] = {\r
8765   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8766   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8767   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8768   { -1, -1 }\r
8769 };\r
8770 #endif\r
8771 \r
8772 Enables ncpEnables[] = {\r
8773   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8774   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8775   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8776   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8777   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8778   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8779   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8780   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8781   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8782   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8783   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8784   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8785   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8786   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8787   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8788   { -1, -1 }\r
8789 };\r
8790 \r
8791 Enables trainingOnEnables[] = {\r
8792   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8793   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8794   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8795   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8796   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8797   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8798   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8799   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8800   { -1, -1 }\r
8801 };\r
8802 \r
8803 Enables trainingOffEnables[] = {\r
8804   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8805   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8806   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8807   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8808   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8809   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8810   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8811   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8812   { -1, -1 }\r
8813 };\r
8814 \r
8815 /* These modify either ncpEnables or gnuEnables */\r
8816 Enables cmailEnables[] = {\r
8817   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8818   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8819   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8820   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8821   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8822   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8823   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8824   { -1, -1 }\r
8825 };\r
8826 \r
8827 Enables machineThinkingEnables[] = {\r
8828   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8829   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8830   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8831   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8832   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8833   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8834   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8835   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8836   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8837   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8838   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8839   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8840   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8841   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8842   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8843   { -1, -1 }\r
8844 };\r
8845 \r
8846 Enables userThinkingEnables[] = {\r
8847   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8848   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8849   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8850   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8851   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8852   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8853   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8854   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8855   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8856   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8857   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8858   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8859   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8860   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8861   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8862   { -1, -1 }\r
8863 };\r
8864 \r
8865 /*---------------------------------------------------------------------------*\\r
8866  *\r
8867  *  Front-end interface functions exported by XBoard.\r
8868  *  Functions appear in same order as prototypes in frontend.h.\r
8869  * \r
8870 \*---------------------------------------------------------------------------*/\r
8871 VOID\r
8872 ModeHighlight()\r
8873 {\r
8874   static UINT prevChecked = 0;\r
8875   static int prevPausing = 0;\r
8876   UINT nowChecked;\r
8877 \r
8878   if (pausing != prevPausing) {\r
8879     prevPausing = pausing;\r
8880     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8881                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8882     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8883   }\r
8884 \r
8885   switch (gameMode) {\r
8886   case BeginningOfGame:\r
8887     if (appData.icsActive)\r
8888       nowChecked = IDM_IcsClient;\r
8889     else if (appData.noChessProgram)\r
8890       nowChecked = IDM_EditGame;\r
8891     else\r
8892       nowChecked = IDM_MachineBlack;\r
8893     break;\r
8894   case MachinePlaysBlack:\r
8895     nowChecked = IDM_MachineBlack;\r
8896     break;\r
8897   case MachinePlaysWhite:\r
8898     nowChecked = IDM_MachineWhite;\r
8899     break;\r
8900   case TwoMachinesPlay:\r
8901     nowChecked = IDM_TwoMachines;\r
8902     break;\r
8903   case AnalyzeMode:\r
8904     nowChecked = IDM_AnalysisMode;\r
8905     break;\r
8906   case AnalyzeFile:\r
8907     nowChecked = IDM_AnalyzeFile;\r
8908     break;\r
8909   case EditGame:\r
8910     nowChecked = IDM_EditGame;\r
8911     break;\r
8912   case PlayFromGameFile:\r
8913     nowChecked = IDM_LoadGame;\r
8914     break;\r
8915   case EditPosition:\r
8916     nowChecked = IDM_EditPosition;\r
8917     break;\r
8918   case Training:\r
8919     nowChecked = IDM_Training;\r
8920     break;\r
8921   case IcsPlayingWhite:\r
8922   case IcsPlayingBlack:\r
8923   case IcsObserving:\r
8924   case IcsIdle:\r
8925     nowChecked = IDM_IcsClient;\r
8926     break;\r
8927   default:\r
8928   case EndOfGame:\r
8929     nowChecked = 0;\r
8930     break;\r
8931   }\r
8932   if (prevChecked != 0)\r
8933     (void) CheckMenuItem(GetMenu(hwndMain),\r
8934                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8935   if (nowChecked != 0)\r
8936     (void) CheckMenuItem(GetMenu(hwndMain),\r
8937                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8938 \r
8939   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8940     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8941                           MF_BYCOMMAND|MF_ENABLED);\r
8942   } else {\r
8943     (void) EnableMenuItem(GetMenu(hwndMain), \r
8944                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8945   }\r
8946 \r
8947   prevChecked = nowChecked;\r
8948 \r
8949   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8950   if (appData.icsActive) {\r
8951        if (appData.icsEngineAnalyze) {\r
8952                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8953                        MF_BYCOMMAND|MF_CHECKED);\r
8954        } else {\r
8955                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8956                        MF_BYCOMMAND|MF_UNCHECKED);\r
8957        }\r
8958   }\r
8959 }\r
8960 \r
8961 VOID\r
8962 SetICSMode()\r
8963 {\r
8964   HMENU hmenu = GetMenu(hwndMain);\r
8965   SetMenuEnables(hmenu, icsEnables);\r
8966   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8967     MF_BYPOSITION|MF_ENABLED);\r
8968 #ifdef ZIPPY\r
8969   if (appData.zippyPlay) {\r
8970     SetMenuEnables(hmenu, zippyEnables);\r
8971     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8972          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8973           MF_BYCOMMAND|MF_ENABLED);\r
8974   }\r
8975 #endif\r
8976 }\r
8977 \r
8978 VOID\r
8979 SetGNUMode()\r
8980 {\r
8981   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8982 }\r
8983 \r
8984 VOID\r
8985 SetNCPMode()\r
8986 {\r
8987   HMENU hmenu = GetMenu(hwndMain);\r
8988   SetMenuEnables(hmenu, ncpEnables);\r
8989   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8990     MF_BYPOSITION|MF_GRAYED);\r
8991     DrawMenuBar(hwndMain);\r
8992 }\r
8993 \r
8994 VOID\r
8995 SetCmailMode()\r
8996 {\r
8997   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8998 }\r
8999 \r
9000 VOID \r
9001 SetTrainingModeOn()\r
9002 {\r
9003   int i;\r
9004   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9005   for (i = 0; i < N_BUTTONS; i++) {\r
9006     if (buttonDesc[i].hwnd != NULL)\r
9007       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9008   }\r
9009   CommentPopDown();\r
9010 }\r
9011 \r
9012 VOID SetTrainingModeOff()\r
9013 {\r
9014   int i;\r
9015   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9016   for (i = 0; i < N_BUTTONS; i++) {\r
9017     if (buttonDesc[i].hwnd != NULL)\r
9018       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9019   }\r
9020 }\r
9021 \r
9022 \r
9023 VOID\r
9024 SetUserThinkingEnables()\r
9025 {\r
9026   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9027 }\r
9028 \r
9029 VOID\r
9030 SetMachineThinkingEnables()\r
9031 {\r
9032   HMENU hMenu = GetMenu(hwndMain);\r
9033   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9034 \r
9035   SetMenuEnables(hMenu, machineThinkingEnables);\r
9036 \r
9037   if (gameMode == MachinePlaysBlack) {\r
9038     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9039   } else if (gameMode == MachinePlaysWhite) {\r
9040     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9041   } else if (gameMode == TwoMachinesPlay) {\r
9042     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9043   }\r
9044 }\r
9045 \r
9046 \r
9047 VOID\r
9048 DisplayTitle(char *str)\r
9049 {\r
9050   char title[MSG_SIZ], *host;\r
9051   if (str[0] != NULLCHAR) {\r
9052     strcpy(title, str);\r
9053   } else if (appData.icsActive) {\r
9054     if (appData.icsCommPort[0] != NULLCHAR)\r
9055       host = "ICS";\r
9056     else \r
9057       host = appData.icsHost;\r
9058     sprintf(title, "%s: %s", szTitle, host);\r
9059   } else if (appData.noChessProgram) {\r
9060     strcpy(title, szTitle);\r
9061   } else {\r
9062     strcpy(title, szTitle);\r
9063     strcat(title, ": ");\r
9064     strcat(title, first.tidy);\r
9065   }\r
9066   SetWindowText(hwndMain, title);\r
9067 }\r
9068 \r
9069 \r
9070 VOID\r
9071 DisplayMessage(char *str1, char *str2)\r
9072 {\r
9073   HDC hdc;\r
9074   HFONT oldFont;\r
9075   int remain = MESSAGE_TEXT_MAX - 1;\r
9076   int len;\r
9077 \r
9078   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9079   messageText[0] = NULLCHAR;\r
9080   if (*str1) {\r
9081     len = strlen(str1);\r
9082     if (len > remain) len = remain;\r
9083     strncpy(messageText, str1, len);\r
9084     messageText[len] = NULLCHAR;\r
9085     remain -= len;\r
9086   }\r
9087   if (*str2 && remain >= 2) {\r
9088     if (*str1) {\r
9089       strcat(messageText, "  ");\r
9090       remain -= 2;\r
9091     }\r
9092     len = strlen(str2);\r
9093     if (len > remain) len = remain;\r
9094     strncat(messageText, str2, len);\r
9095   }\r
9096   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9097 \r
9098   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9099 \r
9100   SAYMACHINEMOVE();\r
9101 \r
9102   hdc = GetDC(hwndMain);\r
9103   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9104   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9105              &messageRect, messageText, strlen(messageText), NULL);\r
9106   (void) SelectObject(hdc, oldFont);\r
9107   (void) ReleaseDC(hwndMain, hdc);\r
9108 }\r
9109 \r
9110 VOID\r
9111 DisplayError(char *str, int error)\r
9112 {\r
9113   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9114   int len;\r
9115 \r
9116   if (error == 0) {\r
9117     strcpy(buf, str);\r
9118   } else {\r
9119     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9120                         NULL, error, LANG_NEUTRAL,\r
9121                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9122     if (len > 0) {\r
9123       sprintf(buf, "%s:\n%s", str, buf2);\r
9124     } else {\r
9125       ErrorMap *em = errmap;\r
9126       while (em->err != 0 && em->err != error) em++;\r
9127       if (em->err != 0) {\r
9128         sprintf(buf, "%s:\n%s", str, em->msg);\r
9129       } else {\r
9130         sprintf(buf, "%s:\nError code %d", str, error);\r
9131       }\r
9132     }\r
9133   }\r
9134   \r
9135   ErrorPopUp("Error", buf);\r
9136 }\r
9137 \r
9138 \r
9139 VOID\r
9140 DisplayMoveError(char *str)\r
9141 {\r
9142   fromX = fromY = -1;\r
9143   ClearHighlights();\r
9144   DrawPosition(FALSE, NULL);\r
9145   if (appData.popupMoveErrors) {\r
9146     ErrorPopUp("Error", str);\r
9147   } else {\r
9148     DisplayMessage(str, "");\r
9149     moveErrorMessageUp = TRUE;\r
9150   }\r
9151 }\r
9152 \r
9153 VOID\r
9154 DisplayFatalError(char *str, int error, int exitStatus)\r
9155 {\r
9156   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9157   int len;\r
9158   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9159 \r
9160   if (error != 0) {\r
9161     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9162                         NULL, error, LANG_NEUTRAL,\r
9163                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9164     if (len > 0) {\r
9165       sprintf(buf, "%s:\n%s", str, buf2);\r
9166     } else {\r
9167       ErrorMap *em = errmap;\r
9168       while (em->err != 0 && em->err != error) em++;\r
9169       if (em->err != 0) {\r
9170         sprintf(buf, "%s:\n%s", str, em->msg);\r
9171       } else {\r
9172         sprintf(buf, "%s:\nError code %d", str, error);\r
9173       }\r
9174     }\r
9175     str = buf;\r
9176   }\r
9177   if (appData.debugMode) {\r
9178     fprintf(debugFP, "%s: %s\n", label, str);\r
9179   }\r
9180   if (appData.popupExitMessage) {\r
9181     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9182                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9183   }\r
9184   ExitEvent(exitStatus);\r
9185 }\r
9186 \r
9187 \r
9188 VOID\r
9189 DisplayInformation(char *str)\r
9190 {\r
9191   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9192 }\r
9193 \r
9194 \r
9195 VOID\r
9196 DisplayNote(char *str)\r
9197 {\r
9198   ErrorPopUp("Note", str);\r
9199 }\r
9200 \r
9201 \r
9202 typedef struct {\r
9203   char *title, *question, *replyPrefix;\r
9204   ProcRef pr;\r
9205 } QuestionParams;\r
9206 \r
9207 LRESULT CALLBACK\r
9208 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9209 {\r
9210   static QuestionParams *qp;\r
9211   char reply[MSG_SIZ];\r
9212   int len, err;\r
9213 \r
9214   switch (message) {\r
9215   case WM_INITDIALOG:\r
9216     qp = (QuestionParams *) lParam;\r
9217     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9218     SetWindowText(hDlg, qp->title);\r
9219     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9220     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9221     return FALSE;\r
9222 \r
9223   case WM_COMMAND:\r
9224     switch (LOWORD(wParam)) {\r
9225     case IDOK:\r
9226       strcpy(reply, qp->replyPrefix);\r
9227       if (*reply) strcat(reply, " ");\r
9228       len = strlen(reply);\r
9229       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9230       strcat(reply, "\n");\r
9231       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9232       EndDialog(hDlg, TRUE);\r
9233       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9234       return TRUE;\r
9235     case IDCANCEL:\r
9236       EndDialog(hDlg, FALSE);\r
9237       return TRUE;\r
9238     default:\r
9239       break;\r
9240     }\r
9241     break;\r
9242   }\r
9243   return FALSE;\r
9244 }\r
9245 \r
9246 VOID\r
9247 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9248 {\r
9249     QuestionParams qp;\r
9250     FARPROC lpProc;\r
9251     \r
9252     qp.title = title;\r
9253     qp.question = question;\r
9254     qp.replyPrefix = replyPrefix;\r
9255     qp.pr = pr;\r
9256     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9257     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9258       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9259     FreeProcInstance(lpProc);\r
9260 }\r
9261 \r
9262 /* [AS] Pick FRC position */\r
9263 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9264 {\r
9265     static int * lpIndexFRC;\r
9266     BOOL index_is_ok;\r
9267     char buf[16];\r
9268 \r
9269     switch( message )\r
9270     {\r
9271     case WM_INITDIALOG:\r
9272         lpIndexFRC = (int *) lParam;\r
9273 \r
9274         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9275 \r
9276         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9277         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9278         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9279         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9280 \r
9281         break;\r
9282 \r
9283     case WM_COMMAND:\r
9284         switch( LOWORD(wParam) ) {\r
9285         case IDOK:\r
9286             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9287             EndDialog( hDlg, 0 );\r
9288             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9289             return TRUE;\r
9290         case IDCANCEL:\r
9291             EndDialog( hDlg, 1 );   \r
9292             return TRUE;\r
9293         case IDC_NFG_Edit:\r
9294             if( HIWORD(wParam) == EN_CHANGE ) {\r
9295                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9296 \r
9297                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9298             }\r
9299             return TRUE;\r
9300         case IDC_NFG_Random:\r
9301             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9302             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9303             return TRUE;\r
9304         }\r
9305 \r
9306         break;\r
9307     }\r
9308 \r
9309     return FALSE;\r
9310 }\r
9311 \r
9312 int NewGameFRC()\r
9313 {\r
9314     int result;\r
9315     int index = appData.defaultFrcPosition;\r
9316     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9317 \r
9318     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9319 \r
9320     if( result == 0 ) {\r
9321         appData.defaultFrcPosition = index;\r
9322     }\r
9323 \r
9324     return result;\r
9325 }\r
9326 \r
9327 /* [AS] Game list options */\r
9328 typedef struct {\r
9329     char id;\r
9330     char * name;\r
9331 } GLT_Item;\r
9332 \r
9333 static GLT_Item GLT_ItemInfo[] = {\r
9334     { GLT_EVENT,      "Event" },\r
9335     { GLT_SITE,       "Site" },\r
9336     { GLT_DATE,       "Date" },\r
9337     { GLT_ROUND,      "Round" },\r
9338     { GLT_PLAYERS,    "Players" },\r
9339     { GLT_RESULT,     "Result" },\r
9340     { GLT_WHITE_ELO,  "White Rating" },\r
9341     { GLT_BLACK_ELO,  "Black Rating" },\r
9342     { GLT_TIME_CONTROL,"Time Control" },\r
9343     { GLT_VARIANT,    "Variant" },\r
9344     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9345     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9346     { 0, 0 }\r
9347 };\r
9348 \r
9349 const char * GLT_FindItem( char id )\r
9350 {\r
9351     const char * result = 0;\r
9352 \r
9353     GLT_Item * list = GLT_ItemInfo;\r
9354 \r
9355     while( list->id != 0 ) {\r
9356         if( list->id == id ) {\r
9357             result = list->name;\r
9358             break;\r
9359         }\r
9360 \r
9361         list++;\r
9362     }\r
9363 \r
9364     return result;\r
9365 }\r
9366 \r
9367 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9368 {\r
9369     const char * name = GLT_FindItem( id );\r
9370 \r
9371     if( name != 0 ) {\r
9372         if( index >= 0 ) {\r
9373             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9374         }\r
9375         else {\r
9376             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9377         }\r
9378     }\r
9379 }\r
9380 \r
9381 void GLT_TagsToList( HWND hDlg, char * tags )\r
9382 {\r
9383     char * pc = tags;\r
9384 \r
9385     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9386 \r
9387     while( *pc ) {\r
9388         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9389         pc++;\r
9390     }\r
9391 \r
9392     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9393 \r
9394     pc = GLT_ALL_TAGS;\r
9395 \r
9396     while( *pc ) {\r
9397         if( strchr( tags, *pc ) == 0 ) {\r
9398             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9399         }\r
9400         pc++;\r
9401     }\r
9402 \r
9403     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9404 }\r
9405 \r
9406 char GLT_ListItemToTag( HWND hDlg, int index )\r
9407 {\r
9408     char result = '\0';\r
9409     char name[128];\r
9410 \r
9411     GLT_Item * list = GLT_ItemInfo;\r
9412 \r
9413     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9414         while( list->id != 0 ) {\r
9415             if( strcmp( list->name, name ) == 0 ) {\r
9416                 result = list->id;\r
9417                 break;\r
9418             }\r
9419 \r
9420             list++;\r
9421         }\r
9422     }\r
9423 \r
9424     return result;\r
9425 }\r
9426 \r
9427 void GLT_MoveSelection( HWND hDlg, int delta )\r
9428 {\r
9429     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9430     int idx2 = idx1 + delta;\r
9431     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9432 \r
9433     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9434         char buf[128];\r
9435 \r
9436         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9437         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9438         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9439         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9440     }\r
9441 }\r
9442 \r
9443 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9444 {\r
9445     static char glt[64];\r
9446     static char * lpUserGLT;\r
9447 \r
9448     switch( message )\r
9449     {\r
9450     case WM_INITDIALOG:\r
9451         lpUserGLT = (char *) lParam;\r
9452         \r
9453         strcpy( glt, lpUserGLT );\r
9454 \r
9455         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9456 \r
9457         /* Initialize list */\r
9458         GLT_TagsToList( hDlg, glt );\r
9459 \r
9460         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9461 \r
9462         break;\r
9463 \r
9464     case WM_COMMAND:\r
9465         switch( LOWORD(wParam) ) {\r
9466         case IDOK:\r
9467             {\r
9468                 char * pc = lpUserGLT;\r
9469                 int idx = 0;\r
9470 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9471                 char id;\r
9472 \r
9473                 do {\r
9474                     id = GLT_ListItemToTag( hDlg, idx );\r
9475 \r
9476                     *pc++ = id;\r
9477                     idx++;\r
9478                 } while( id != '\0' );\r
9479             }\r
9480             EndDialog( hDlg, 0 );\r
9481             return TRUE;\r
9482         case IDCANCEL:\r
9483             EndDialog( hDlg, 1 );\r
9484             return TRUE;\r
9485 \r
9486         case IDC_GLT_Default:\r
9487             strcpy( glt, GLT_DEFAULT_TAGS );\r
9488             GLT_TagsToList( hDlg, glt );\r
9489             return TRUE;\r
9490 \r
9491         case IDC_GLT_Restore:\r
9492             strcpy( glt, lpUserGLT );\r
9493             GLT_TagsToList( hDlg, glt );\r
9494             return TRUE;\r
9495 \r
9496         case IDC_GLT_Up:\r
9497             GLT_MoveSelection( hDlg, -1 );\r
9498             return TRUE;\r
9499 \r
9500         case IDC_GLT_Down:\r
9501             GLT_MoveSelection( hDlg, +1 );\r
9502             return TRUE;\r
9503         }\r
9504 \r
9505         break;\r
9506     }\r
9507 \r
9508     return FALSE;\r
9509 }\r
9510 \r
9511 int GameListOptions()\r
9512 {\r
9513     char glt[64];\r
9514     int result;\r
9515     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9516 \r
9517     strcpy( glt, appData.gameListTags );\r
9518 \r
9519     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9520 \r
9521     if( result == 0 ) {\r
9522         /* [AS] Memory leak here! */\r
9523         appData.gameListTags = strdup( glt ); \r
9524     }\r
9525 \r
9526     return result;\r
9527 }\r
9528 \r
9529 \r
9530 VOID\r
9531 DisplayIcsInteractionTitle(char *str)\r
9532 {\r
9533   char consoleTitle[MSG_SIZ];\r
9534 \r
9535   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9536   SetWindowText(hwndConsole, consoleTitle);\r
9537 }\r
9538 \r
9539 void\r
9540 DrawPosition(int fullRedraw, Board board)\r
9541 {\r
9542   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9543 }\r
9544 \r
9545 void NotifyFrontendLogin()\r
9546 {\r
9547         if (hwndConsole)\r
9548                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
9549 }\r
9550 \r
9551 VOID\r
9552 ResetFrontEnd()\r
9553 {\r
9554   fromX = fromY = -1;\r
9555   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9556     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9557     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9558     dragInfo.lastpos = dragInfo.pos;\r
9559     dragInfo.start.x = dragInfo.start.y = -1;\r
9560     dragInfo.from = dragInfo.start;\r
9561     ReleaseCapture();\r
9562     DrawPosition(TRUE, NULL);\r
9563   }\r
9564 }\r
9565 \r
9566 \r
9567 VOID\r
9568 CommentPopUp(char *title, char *str)\r
9569 {\r
9570   HWND hwnd = GetActiveWindow();\r
9571   EitherCommentPopUp(0, title, str, FALSE);\r
9572   SAY(str);\r
9573   SetActiveWindow(hwnd);\r
9574 }\r
9575 \r
9576 VOID\r
9577 CommentPopDown(void)\r
9578 {\r
9579   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9580   if (commentDialog) {\r
9581     ShowWindow(commentDialog, SW_HIDE);\r
9582   }\r
9583   commentDialogUp = FALSE;\r
9584 }\r
9585 \r
9586 VOID\r
9587 EditCommentPopUp(int index, char *title, char *str)\r
9588 {\r
9589   EitherCommentPopUp(index, title, str, TRUE);\r
9590 }\r
9591 \r
9592 \r
9593 VOID\r
9594 RingBell()\r
9595 {\r
9596   MyPlaySound(&sounds[(int)SoundMove]);\r
9597 }\r
9598 \r
9599 VOID PlayIcsWinSound()\r
9600 {\r
9601   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9602 }\r
9603 \r
9604 VOID PlayIcsLossSound()\r
9605 {\r
9606   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9607 }\r
9608 \r
9609 VOID PlayIcsDrawSound()\r
9610 {\r
9611   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9612 }\r
9613 \r
9614 VOID PlayIcsUnfinishedSound()\r
9615 {\r
9616   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9617 }\r
9618 \r
9619 VOID\r
9620 PlayAlarmSound()\r
9621 {\r
9622   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9623 }\r
9624 \r
9625 \r
9626 VOID\r
9627 EchoOn()\r
9628 {\r
9629   HWND hInput;\r
9630   consoleEcho = TRUE;\r
9631   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9632   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9633   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9634 }\r
9635 \r
9636 \r
9637 VOID\r
9638 EchoOff()\r
9639 {\r
9640   CHARFORMAT cf;\r
9641   HWND hInput;\r
9642   consoleEcho = FALSE;\r
9643   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9644   /* This works OK: set text and background both to the same color */\r
9645   cf = consoleCF;\r
9646   cf.crTextColor = COLOR_ECHOOFF;\r
9647   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9648   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9649 }\r
9650 \r
9651 /* No Raw()...? */\r
9652 \r
9653 void Colorize(ColorClass cc, int continuation)\r
9654 {\r
9655   currentColorClass = cc;\r
9656   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9657   consoleCF.crTextColor = textAttribs[cc].color;\r
9658   consoleCF.dwEffects = textAttribs[cc].effects;\r
9659   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9660 }\r
9661 \r
9662 char *\r
9663 UserName()\r
9664 {\r
9665   static char buf[MSG_SIZ];\r
9666   DWORD bufsiz = MSG_SIZ;\r
9667 \r
9668   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9669         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9670   }\r
9671   if (!GetUserName(buf, &bufsiz)) {\r
9672     /*DisplayError("Error getting user name", GetLastError());*/\r
9673     strcpy(buf, "User");\r
9674   }\r
9675   return buf;\r
9676 }\r
9677 \r
9678 char *\r
9679 HostName()\r
9680 {\r
9681   static char buf[MSG_SIZ];\r
9682   DWORD bufsiz = MSG_SIZ;\r
9683 \r
9684   if (!GetComputerName(buf, &bufsiz)) {\r
9685     /*DisplayError("Error getting host name", GetLastError());*/\r
9686     strcpy(buf, "Unknown");\r
9687   }\r
9688   return buf;\r
9689 }\r
9690 \r
9691 \r
9692 int\r
9693 ClockTimerRunning()\r
9694 {\r
9695   return clockTimerEvent != 0;\r
9696 }\r
9697 \r
9698 int\r
9699 StopClockTimer()\r
9700 {\r
9701   if (clockTimerEvent == 0) return FALSE;\r
9702   KillTimer(hwndMain, clockTimerEvent);\r
9703   clockTimerEvent = 0;\r
9704   return TRUE;\r
9705 }\r
9706 \r
9707 void\r
9708 StartClockTimer(long millisec)\r
9709 {\r
9710   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9711                              (UINT) millisec, NULL);\r
9712 }\r
9713 \r
9714 void\r
9715 DisplayWhiteClock(long timeRemaining, int highlight)\r
9716 {\r
9717   HDC hdc;\r
9718   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9719 \r
9720   if(appData.noGUI) return;\r
9721   hdc = GetDC(hwndMain);\r
9722   if (!IsIconic(hwndMain)) {\r
9723     DisplayAClock(hdc, timeRemaining, highlight, \r
9724                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9725   }\r
9726   if (highlight && iconCurrent == iconBlack) {\r
9727     iconCurrent = iconWhite;\r
9728     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9729     if (IsIconic(hwndMain)) {\r
9730       DrawIcon(hdc, 2, 2, iconCurrent);\r
9731     }\r
9732   }\r
9733   (void) ReleaseDC(hwndMain, hdc);\r
9734   if (hwndConsole)\r
9735     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9736 }\r
9737 \r
9738 void\r
9739 DisplayBlackClock(long timeRemaining, int highlight)\r
9740 {\r
9741   HDC hdc;\r
9742   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9743 \r
9744   if(appData.noGUI) return;\r
9745   hdc = GetDC(hwndMain);\r
9746   if (!IsIconic(hwndMain)) {\r
9747     DisplayAClock(hdc, timeRemaining, highlight, \r
9748                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9749   }\r
9750   if (highlight && iconCurrent == iconWhite) {\r
9751     iconCurrent = iconBlack;\r
9752     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9753     if (IsIconic(hwndMain)) {\r
9754       DrawIcon(hdc, 2, 2, iconCurrent);\r
9755     }\r
9756   }\r
9757   (void) ReleaseDC(hwndMain, hdc);\r
9758   if (hwndConsole)\r
9759     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9760 }\r
9761 \r
9762 \r
9763 int\r
9764 LoadGameTimerRunning()\r
9765 {\r
9766   return loadGameTimerEvent != 0;\r
9767 }\r
9768 \r
9769 int\r
9770 StopLoadGameTimer()\r
9771 {\r
9772   if (loadGameTimerEvent == 0) return FALSE;\r
9773   KillTimer(hwndMain, loadGameTimerEvent);\r
9774   loadGameTimerEvent = 0;\r
9775   return TRUE;\r
9776 }\r
9777 \r
9778 void\r
9779 StartLoadGameTimer(long millisec)\r
9780 {\r
9781   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9782                                 (UINT) millisec, NULL);\r
9783 }\r
9784 \r
9785 void\r
9786 AutoSaveGame()\r
9787 {\r
9788   char *defName;\r
9789   FILE *f;\r
9790   char fileTitle[MSG_SIZ];\r
9791 \r
9792   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9793   f = OpenFileDialog(hwndMain, "a", defName,\r
9794                      appData.oldSaveStyle ? "gam" : "pgn",\r
9795                      GAME_FILT, \r
9796                      "Save Game to File", NULL, fileTitle, NULL);\r
9797   if (f != NULL) {\r
9798     SaveGame(f, 0, "");\r
9799     fclose(f);\r
9800   }\r
9801 }\r
9802 \r
9803 \r
9804 void\r
9805 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9806 {\r
9807   if (delayedTimerEvent != 0) {\r
9808     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9809       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9810     }\r
9811     KillTimer(hwndMain, delayedTimerEvent);\r
9812     delayedTimerEvent = 0;\r
9813     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9814     delayedTimerCallback();\r
9815   }\r
9816   delayedTimerCallback = cb;\r
9817   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9818                                 (UINT) millisec, NULL);\r
9819 }\r
9820 \r
9821 DelayedEventCallback\r
9822 GetDelayedEvent()\r
9823 {\r
9824   if (delayedTimerEvent) {\r
9825     return delayedTimerCallback;\r
9826   } else {\r
9827     return NULL;\r
9828   }\r
9829 }\r
9830 \r
9831 void\r
9832 CancelDelayedEvent()\r
9833 {\r
9834   if (delayedTimerEvent) {\r
9835     KillTimer(hwndMain, delayedTimerEvent);\r
9836     delayedTimerEvent = 0;\r
9837   }\r
9838 }\r
9839 \r
9840 DWORD GetWin32Priority(int nice)\r
9841 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9842 /*\r
9843 REALTIME_PRIORITY_CLASS     0x00000100\r
9844 HIGH_PRIORITY_CLASS         0x00000080\r
9845 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9846 NORMAL_PRIORITY_CLASS       0x00000020\r
9847 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9848 IDLE_PRIORITY_CLASS         0x00000040\r
9849 */\r
9850         if (nice < -15) return 0x00000080;\r
9851         if (nice < 0)   return 0x00008000;\r
9852         if (nice == 0)  return 0x00000020;\r
9853         if (nice < 15)  return 0x00004000;\r
9854         return 0x00000040;\r
9855 }\r
9856 \r
9857 /* Start a child process running the given program.\r
9858    The process's standard output can be read from "from", and its\r
9859    standard input can be written to "to".\r
9860    Exit with fatal error if anything goes wrong.\r
9861    Returns an opaque pointer that can be used to destroy the process\r
9862    later.\r
9863 */\r
9864 int\r
9865 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9866 {\r
9867 #define BUFSIZE 4096\r
9868 \r
9869   HANDLE hChildStdinRd, hChildStdinWr,\r
9870     hChildStdoutRd, hChildStdoutWr;\r
9871   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9872   SECURITY_ATTRIBUTES saAttr;\r
9873   BOOL fSuccess;\r
9874   PROCESS_INFORMATION piProcInfo;\r
9875   STARTUPINFO siStartInfo;\r
9876   ChildProc *cp;\r
9877   char buf[MSG_SIZ];\r
9878   DWORD err;\r
9879 \r
9880   if (appData.debugMode) {\r
9881     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9882   }\r
9883 \r
9884   *pr = NoProc;\r
9885 \r
9886   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9887   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9888   saAttr.bInheritHandle = TRUE;\r
9889   saAttr.lpSecurityDescriptor = NULL;\r
9890 \r
9891   /*\r
9892    * The steps for redirecting child's STDOUT:\r
9893    *     1. Create anonymous pipe to be STDOUT for child.\r
9894    *     2. Create a noninheritable duplicate of read handle,\r
9895    *         and close the inheritable read handle.\r
9896    */\r
9897 \r
9898   /* Create a pipe for the child's STDOUT. */\r
9899   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9900     return GetLastError();\r
9901   }\r
9902 \r
9903   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9904   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9905                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9906                              FALSE,     /* not inherited */\r
9907                              DUPLICATE_SAME_ACCESS);\r
9908   if (! fSuccess) {\r
9909     return GetLastError();\r
9910   }\r
9911   CloseHandle(hChildStdoutRd);\r
9912 \r
9913   /*\r
9914    * The steps for redirecting child's STDIN:\r
9915    *     1. Create anonymous pipe to be STDIN for child.\r
9916    *     2. Create a noninheritable duplicate of write handle,\r
9917    *         and close the inheritable write handle.\r
9918    */\r
9919 \r
9920   /* Create a pipe for the child's STDIN. */\r
9921   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9922     return GetLastError();\r
9923   }\r
9924 \r
9925   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9926   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9927                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9928                              FALSE,     /* not inherited */\r
9929                              DUPLICATE_SAME_ACCESS);\r
9930   if (! fSuccess) {\r
9931     return GetLastError();\r
9932   }\r
9933   CloseHandle(hChildStdinWr);\r
9934 \r
9935   /* Arrange to (1) look in dir for the child .exe file, and\r
9936    * (2) have dir be the child's working directory.  Interpret\r
9937    * dir relative to the directory WinBoard loaded from. */\r
9938   GetCurrentDirectory(MSG_SIZ, buf);\r
9939   SetCurrentDirectory(installDir);\r
9940   SetCurrentDirectory(dir);\r
9941 \r
9942   /* Now create the child process. */\r
9943 \r
9944   siStartInfo.cb = sizeof(STARTUPINFO);\r
9945   siStartInfo.lpReserved = NULL;\r
9946   siStartInfo.lpDesktop = NULL;\r
9947   siStartInfo.lpTitle = NULL;\r
9948   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9949   siStartInfo.cbReserved2 = 0;\r
9950   siStartInfo.lpReserved2 = NULL;\r
9951   siStartInfo.hStdInput = hChildStdinRd;\r
9952   siStartInfo.hStdOutput = hChildStdoutWr;\r
9953   siStartInfo.hStdError = hChildStdoutWr;\r
9954 \r
9955   fSuccess = CreateProcess(NULL,\r
9956                            cmdLine,        /* command line */\r
9957                            NULL,           /* process security attributes */\r
9958                            NULL,           /* primary thread security attrs */\r
9959                            TRUE,           /* handles are inherited */\r
9960                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9961                            NULL,           /* use parent's environment */\r
9962                            NULL,\r
9963                            &siStartInfo, /* STARTUPINFO pointer */\r
9964                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9965 \r
9966   err = GetLastError();\r
9967   SetCurrentDirectory(buf); /* return to prev directory */\r
9968   if (! fSuccess) {\r
9969     return err;\r
9970   }\r
9971 \r
9972   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9973     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9974     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9975   }\r
9976 \r
9977   /* Close the handles we don't need in the parent */\r
9978   CloseHandle(piProcInfo.hThread);\r
9979   CloseHandle(hChildStdinRd);\r
9980   CloseHandle(hChildStdoutWr);\r
9981 \r
9982   /* Prepare return value */\r
9983   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9984   cp->kind = CPReal;\r
9985   cp->hProcess = piProcInfo.hProcess;\r
9986   cp->pid = piProcInfo.dwProcessId;\r
9987   cp->hFrom = hChildStdoutRdDup;\r
9988   cp->hTo = hChildStdinWrDup;\r
9989 \r
9990   *pr = (void *) cp;\r
9991 \r
9992   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9993      2000 where engines sometimes don't see the initial command(s)\r
9994      from WinBoard and hang.  I don't understand how that can happen,\r
9995      but the Sleep is harmless, so I've put it in.  Others have also\r
9996      reported what may be the same problem, so hopefully this will fix\r
9997      it for them too.  */\r
9998   Sleep(500);\r
9999 \r
10000   return NO_ERROR;\r
10001 }\r
10002 \r
10003 \r
10004 void\r
10005 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10006 {\r
10007   ChildProc *cp; int result;\r
10008 \r
10009   cp = (ChildProc *) pr;\r
10010   if (cp == NULL) return;\r
10011 \r
10012   switch (cp->kind) {\r
10013   case CPReal:\r
10014     /* TerminateProcess is considered harmful, so... */\r
10015     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10016     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10017     /* The following doesn't work because the chess program\r
10018        doesn't "have the same console" as WinBoard.  Maybe\r
10019        we could arrange for this even though neither WinBoard\r
10020        nor the chess program uses a console for stdio? */\r
10021     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10022 \r
10023     /* [AS] Special termination modes for misbehaving programs... */\r
10024     if( signal == 9 ) { \r
10025         result = TerminateProcess( cp->hProcess, 0 );\r
10026 \r
10027         if ( appData.debugMode) {\r
10028             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10029         }\r
10030     }\r
10031     else if( signal == 10 ) {\r
10032         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10033 \r
10034         if( dw != WAIT_OBJECT_0 ) {\r
10035             result = TerminateProcess( cp->hProcess, 0 );\r
10036 \r
10037             if ( appData.debugMode) {\r
10038                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10039             }\r
10040 \r
10041         }\r
10042     }\r
10043 \r
10044     CloseHandle(cp->hProcess);\r
10045     break;\r
10046 \r
10047   case CPComm:\r
10048     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10049     break;\r
10050 \r
10051   case CPSock:\r
10052     closesocket(cp->sock);\r
10053     WSACleanup();\r
10054     break;\r
10055 \r
10056   case CPRcmd:\r
10057     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10058     closesocket(cp->sock);\r
10059     closesocket(cp->sock2);\r
10060     WSACleanup();\r
10061     break;\r
10062   }\r
10063   free(cp);\r
10064 }\r
10065 \r
10066 void\r
10067 InterruptChildProcess(ProcRef pr)\r
10068 {\r
10069   ChildProc *cp;\r
10070 \r
10071   cp = (ChildProc *) pr;\r
10072   if (cp == NULL) return;\r
10073   switch (cp->kind) {\r
10074   case CPReal:\r
10075     /* The following doesn't work because the chess program\r
10076        doesn't "have the same console" as WinBoard.  Maybe\r
10077        we could arrange for this even though neither WinBoard\r
10078        nor the chess program uses a console for stdio */\r
10079     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10080     break;\r
10081 \r
10082   case CPComm:\r
10083   case CPSock:\r
10084     /* Can't interrupt */\r
10085     break;\r
10086 \r
10087   case CPRcmd:\r
10088     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10089     break;\r
10090   }\r
10091 }\r
10092 \r
10093 \r
10094 int\r
10095 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10096 {\r
10097   char cmdLine[MSG_SIZ];\r
10098 \r
10099   if (port[0] == NULLCHAR) {\r
10100     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10101   } else {\r
10102     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10103   }\r
10104   return StartChildProcess(cmdLine, "", pr);\r
10105 }\r
10106 \r
10107 \r
10108 /* Code to open TCP sockets */\r
10109 \r
10110 int\r
10111 OpenTCP(char *host, char *port, ProcRef *pr)\r
10112 {\r
10113   ChildProc *cp;\r
10114   int err;\r
10115   SOCKET s;\r
10116   struct sockaddr_in sa, mysa;\r
10117   struct hostent FAR *hp;\r
10118   unsigned short uport;\r
10119   WORD wVersionRequested;\r
10120   WSADATA wsaData;\r
10121 \r
10122   /* Initialize socket DLL */\r
10123   wVersionRequested = MAKEWORD(1, 1);\r
10124   err = WSAStartup(wVersionRequested, &wsaData);\r
10125   if (err != 0) return err;\r
10126 \r
10127   /* Make socket */\r
10128   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10129     err = WSAGetLastError();\r
10130     WSACleanup();\r
10131     return err;\r
10132   }\r
10133 \r
10134   /* Bind local address using (mostly) don't-care values.\r
10135    */\r
10136   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10137   mysa.sin_family = AF_INET;\r
10138   mysa.sin_addr.s_addr = INADDR_ANY;\r
10139   uport = (unsigned short) 0;\r
10140   mysa.sin_port = htons(uport);\r
10141   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10142       == SOCKET_ERROR) {\r
10143     err = WSAGetLastError();\r
10144     WSACleanup();\r
10145     return err;\r
10146   }\r
10147 \r
10148   /* Resolve remote host name */\r
10149   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10150   if (!(hp = gethostbyname(host))) {\r
10151     unsigned int b0, b1, b2, b3;\r
10152 \r
10153     err = WSAGetLastError();\r
10154 \r
10155     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10156       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10157       hp->h_addrtype = AF_INET;\r
10158       hp->h_length = 4;\r
10159       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10160       hp->h_addr_list[0] = (char *) malloc(4);\r
10161       hp->h_addr_list[0][0] = (char) b0;\r
10162       hp->h_addr_list[0][1] = (char) b1;\r
10163       hp->h_addr_list[0][2] = (char) b2;\r
10164       hp->h_addr_list[0][3] = (char) b3;\r
10165     } else {\r
10166       WSACleanup();\r
10167       return err;\r
10168     }\r
10169   }\r
10170   sa.sin_family = hp->h_addrtype;\r
10171   uport = (unsigned short) atoi(port);\r
10172   sa.sin_port = htons(uport);\r
10173   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10174 \r
10175   /* Make connection */\r
10176   if (connect(s, (struct sockaddr *) &sa,\r
10177               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10178     err = WSAGetLastError();\r
10179     WSACleanup();\r
10180     return err;\r
10181   }\r
10182 \r
10183   /* Prepare return value */\r
10184   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10185   cp->kind = CPSock;\r
10186   cp->sock = s;\r
10187   *pr = (ProcRef *) cp;\r
10188 \r
10189   return NO_ERROR;\r
10190 }\r
10191 \r
10192 int\r
10193 OpenCommPort(char *name, ProcRef *pr)\r
10194 {\r
10195   HANDLE h;\r
10196   COMMTIMEOUTS ct;\r
10197   ChildProc *cp;\r
10198   char fullname[MSG_SIZ];\r
10199 \r
10200   if (*name != '\\')\r
10201     sprintf(fullname, "\\\\.\\%s", name);\r
10202   else\r
10203     strcpy(fullname, name);\r
10204 \r
10205   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10206                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10207   if (h == (HANDLE) -1) {\r
10208     return GetLastError();\r
10209   }\r
10210   hCommPort = h;\r
10211 \r
10212   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10213 \r
10214   /* Accumulate characters until a 100ms pause, then parse */\r
10215   ct.ReadIntervalTimeout = 100;\r
10216   ct.ReadTotalTimeoutMultiplier = 0;\r
10217   ct.ReadTotalTimeoutConstant = 0;\r
10218   ct.WriteTotalTimeoutMultiplier = 0;\r
10219   ct.WriteTotalTimeoutConstant = 0;\r
10220   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10221 \r
10222   /* Prepare return value */\r
10223   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10224   cp->kind = CPComm;\r
10225   cp->hFrom = h;\r
10226   cp->hTo = h;\r
10227   *pr = (ProcRef *) cp;\r
10228 \r
10229   return NO_ERROR;\r
10230 }\r
10231 \r
10232 int\r
10233 OpenLoopback(ProcRef *pr)\r
10234 {\r
10235   DisplayFatalError("Not implemented", 0, 1);\r
10236   return NO_ERROR;\r
10237 }\r
10238 \r
10239 \r
10240 int\r
10241 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10242 {\r
10243   ChildProc *cp;\r
10244   int err;\r
10245   SOCKET s, s2, s3;\r
10246   struct sockaddr_in sa, mysa;\r
10247   struct hostent FAR *hp;\r
10248   unsigned short uport;\r
10249   WORD wVersionRequested;\r
10250   WSADATA wsaData;\r
10251   int fromPort;\r
10252   char stderrPortStr[MSG_SIZ];\r
10253 \r
10254   /* Initialize socket DLL */\r
10255   wVersionRequested = MAKEWORD(1, 1);\r
10256   err = WSAStartup(wVersionRequested, &wsaData);\r
10257   if (err != 0) return err;\r
10258 \r
10259   /* Resolve remote host name */\r
10260   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10261   if (!(hp = gethostbyname(host))) {\r
10262     unsigned int b0, b1, b2, b3;\r
10263 \r
10264     err = WSAGetLastError();\r
10265 \r
10266     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10267       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10268       hp->h_addrtype = AF_INET;\r
10269       hp->h_length = 4;\r
10270       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10271       hp->h_addr_list[0] = (char *) malloc(4);\r
10272       hp->h_addr_list[0][0] = (char) b0;\r
10273       hp->h_addr_list[0][1] = (char) b1;\r
10274       hp->h_addr_list[0][2] = (char) b2;\r
10275       hp->h_addr_list[0][3] = (char) b3;\r
10276     } else {\r
10277       WSACleanup();\r
10278       return err;\r
10279     }\r
10280   }\r
10281   sa.sin_family = hp->h_addrtype;\r
10282   uport = (unsigned short) 514;\r
10283   sa.sin_port = htons(uport);\r
10284   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10285 \r
10286   /* Bind local socket to unused "privileged" port address\r
10287    */\r
10288   s = INVALID_SOCKET;\r
10289   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10290   mysa.sin_family = AF_INET;\r
10291   mysa.sin_addr.s_addr = INADDR_ANY;\r
10292   for (fromPort = 1023;; fromPort--) {\r
10293     if (fromPort < 0) {\r
10294       WSACleanup();\r
10295       return WSAEADDRINUSE;\r
10296     }\r
10297     if (s == INVALID_SOCKET) {\r
10298       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10299         err = WSAGetLastError();\r
10300         WSACleanup();\r
10301         return err;\r
10302       }\r
10303     }\r
10304     uport = (unsigned short) fromPort;\r
10305     mysa.sin_port = htons(uport);\r
10306     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10307         == SOCKET_ERROR) {\r
10308       err = WSAGetLastError();\r
10309       if (err == WSAEADDRINUSE) continue;\r
10310       WSACleanup();\r
10311       return err;\r
10312     }\r
10313     if (connect(s, (struct sockaddr *) &sa,\r
10314       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10315       err = WSAGetLastError();\r
10316       if (err == WSAEADDRINUSE) {\r
10317         closesocket(s);\r
10318         s = -1;\r
10319         continue;\r
10320       }\r
10321       WSACleanup();\r
10322       return err;\r
10323     }\r
10324     break;\r
10325   }\r
10326 \r
10327   /* Bind stderr local socket to unused "privileged" port address\r
10328    */\r
10329   s2 = INVALID_SOCKET;\r
10330   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10331   mysa.sin_family = AF_INET;\r
10332   mysa.sin_addr.s_addr = INADDR_ANY;\r
10333   for (fromPort = 1023;; fromPort--) {\r
10334     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10335     if (fromPort < 0) {\r
10336       (void) closesocket(s);\r
10337       WSACleanup();\r
10338       return WSAEADDRINUSE;\r
10339     }\r
10340     if (s2 == INVALID_SOCKET) {\r
10341       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10342         err = WSAGetLastError();\r
10343         closesocket(s);\r
10344         WSACleanup();\r
10345         return err;\r
10346       }\r
10347     }\r
10348     uport = (unsigned short) fromPort;\r
10349     mysa.sin_port = htons(uport);\r
10350     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10351         == SOCKET_ERROR) {\r
10352       err = WSAGetLastError();\r
10353       if (err == WSAEADDRINUSE) continue;\r
10354       (void) closesocket(s);\r
10355       WSACleanup();\r
10356       return err;\r
10357     }\r
10358     if (listen(s2, 1) == SOCKET_ERROR) {\r
10359       err = WSAGetLastError();\r
10360       if (err == WSAEADDRINUSE) {\r
10361         closesocket(s2);\r
10362         s2 = INVALID_SOCKET;\r
10363         continue;\r
10364       }\r
10365       (void) closesocket(s);\r
10366       (void) closesocket(s2);\r
10367       WSACleanup();\r
10368       return err;\r
10369     }\r
10370     break;\r
10371   }\r
10372   prevStderrPort = fromPort; // remember port used\r
10373   sprintf(stderrPortStr, "%d", fromPort);\r
10374 \r
10375   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10376     err = WSAGetLastError();\r
10377     (void) closesocket(s);\r
10378     (void) closesocket(s2);\r
10379     WSACleanup();\r
10380     return err;\r
10381   }\r
10382 \r
10383   if (send(s, UserName(), strlen(UserName()) + 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   if (*user == NULLCHAR) user = UserName();\r
10391   if (send(s, user, strlen(user) + 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 (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10399     err = WSAGetLastError();\r
10400     (void) closesocket(s);\r
10401     (void) closesocket(s2);\r
10402     WSACleanup();\r
10403     return err;\r
10404   }\r
10405 \r
10406   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\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   (void) closesocket(s2);  /* Stop listening */\r
10414 \r
10415   /* Prepare return value */\r
10416   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10417   cp->kind = CPRcmd;\r
10418   cp->sock = s;\r
10419   cp->sock2 = s3;\r
10420   *pr = (ProcRef *) cp;\r
10421 \r
10422   return NO_ERROR;\r
10423 }\r
10424 \r
10425 \r
10426 InputSourceRef\r
10427 AddInputSource(ProcRef pr, int lineByLine,\r
10428                InputCallback func, VOIDSTAR closure)\r
10429 {\r
10430   InputSource *is, *is2 = NULL;\r
10431   ChildProc *cp = (ChildProc *) pr;\r
10432 \r
10433   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10434   is->lineByLine = lineByLine;\r
10435   is->func = func;\r
10436   is->closure = closure;\r
10437   is->second = NULL;\r
10438   is->next = is->buf;\r
10439   if (pr == NoProc) {\r
10440     is->kind = CPReal;\r
10441     consoleInputSource = is;\r
10442   } else {\r
10443     is->kind = cp->kind;\r
10444     /* \r
10445         [AS] Try to avoid a race condition if the thread is given control too early:\r
10446         we create all threads suspended so that the is->hThread variable can be\r
10447         safely assigned, then let the threads start with ResumeThread.\r
10448     */\r
10449     switch (cp->kind) {\r
10450     case CPReal:\r
10451       is->hFile = cp->hFrom;\r
10452       cp->hFrom = NULL; /* now owned by InputThread */\r
10453       is->hThread =\r
10454         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10455                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10456       break;\r
10457 \r
10458     case CPComm:\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) InputThread,\r
10463                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10464       break;\r
10465 \r
10466     case CPSock:\r
10467       is->sock = cp->sock;\r
10468       is->hThread =\r
10469         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10470                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10471       break;\r
10472 \r
10473     case CPRcmd:\r
10474       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10475       *is2 = *is;\r
10476       is->sock = cp->sock;\r
10477       is->second = is2;\r
10478       is2->sock = cp->sock2;\r
10479       is2->second = is2;\r
10480       is->hThread =\r
10481         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10482                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10483       is2->hThread =\r
10484         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10485                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10486       break;\r
10487     }\r
10488 \r
10489     if( is->hThread != NULL ) {\r
10490         ResumeThread( is->hThread );\r
10491     }\r
10492 \r
10493     if( is2 != NULL && is2->hThread != NULL ) {\r
10494         ResumeThread( is2->hThread );\r
10495     }\r
10496   }\r
10497 \r
10498   return (InputSourceRef) is;\r
10499 }\r
10500 \r
10501 void\r
10502 RemoveInputSource(InputSourceRef isr)\r
10503 {\r
10504   InputSource *is;\r
10505 \r
10506   is = (InputSource *) isr;\r
10507   is->hThread = NULL;  /* tell thread to stop */\r
10508   CloseHandle(is->hThread);\r
10509   if (is->second != NULL) {\r
10510     is->second->hThread = NULL;\r
10511     CloseHandle(is->second->hThread);\r
10512   }\r
10513 }\r
10514 \r
10515 \r
10516 int\r
10517 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10518 {\r
10519   DWORD dOutCount;\r
10520   int outCount = SOCKET_ERROR;\r
10521   ChildProc *cp = (ChildProc *) pr;\r
10522   static OVERLAPPED ovl;\r
10523 \r
10524   if (pr == NoProc) {\r
10525     ConsoleOutput(message, count, FALSE);\r
10526     return count;\r
10527   } \r
10528 \r
10529   if (ovl.hEvent == NULL) {\r
10530     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10531   }\r
10532   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10533 \r
10534   switch (cp->kind) {\r
10535   case CPSock:\r
10536   case CPRcmd:\r
10537     outCount = send(cp->sock, message, count, 0);\r
10538     if (outCount == SOCKET_ERROR) {\r
10539       *outError = WSAGetLastError();\r
10540     } else {\r
10541       *outError = NO_ERROR;\r
10542     }\r
10543     break;\r
10544 \r
10545   case CPReal:\r
10546     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10547                   &dOutCount, NULL)) {\r
10548       *outError = NO_ERROR;\r
10549       outCount = (int) dOutCount;\r
10550     } else {\r
10551       *outError = GetLastError();\r
10552     }\r
10553     break;\r
10554 \r
10555   case CPComm:\r
10556     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10557                             &dOutCount, &ovl);\r
10558     if (*outError == NO_ERROR) {\r
10559       outCount = (int) dOutCount;\r
10560     }\r
10561     break;\r
10562   }\r
10563   return outCount;\r
10564 }\r
10565 \r
10566 int\r
10567 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10568                        long msdelay)\r
10569 {\r
10570   /* Ignore delay, not implemented for WinBoard */\r
10571   return OutputToProcess(pr, message, count, outError);\r
10572 }\r
10573 \r
10574 \r
10575 void\r
10576 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10577                         char *buf, int count, int error)\r
10578 {\r
10579   DisplayFatalError("Not implemented", 0, 1);\r
10580 }\r
10581 \r
10582 /* see wgamelist.c for Game List functions */\r
10583 /* see wedittags.c for Edit Tags functions */\r
10584 \r
10585 \r
10586 VOID\r
10587 ICSInitScript()\r
10588 {\r
10589   FILE *f;\r
10590   char buf[MSG_SIZ];\r
10591   char *dummy;\r
10592 \r
10593   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10594     f = fopen(buf, "r");\r
10595     if (f != NULL) {\r
10596       ProcessICSInitScript(f);\r
10597       fclose(f);\r
10598     }\r
10599   }\r
10600 }\r
10601 \r
10602 \r
10603 VOID\r
10604 StartAnalysisClock()\r
10605 {\r
10606   if (analysisTimerEvent) return;\r
10607   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10608                                         (UINT) 2000, NULL);\r
10609 }\r
10610 \r
10611 LRESULT CALLBACK\r
10612 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10613 {\r
10614   static HANDLE hwndText;\r
10615   RECT rect;\r
10616   static int sizeX, sizeY;\r
10617   int newSizeX, newSizeY, flags;\r
10618   MINMAXINFO *mmi;\r
10619 \r
10620   switch (message) {\r
10621   case WM_INITDIALOG: /* message: initialize dialog box */\r
10622     /* Initialize the dialog items */\r
10623     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10624     SetWindowText(hDlg, analysisTitle);\r
10625     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10626     /* Size and position the dialog */\r
10627     if (!analysisDialog) {\r
10628       analysisDialog = hDlg;\r
10629       flags = SWP_NOZORDER;\r
10630       GetClientRect(hDlg, &rect);\r
10631       sizeX = rect.right;\r
10632       sizeY = rect.bottom;\r
10633       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10634           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10635         WINDOWPLACEMENT wp;\r
10636         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10637         wp.length = sizeof(WINDOWPLACEMENT);\r
10638         wp.flags = 0;\r
10639         wp.showCmd = SW_SHOW;\r
10640         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10641         wp.rcNormalPosition.left = analysisX;\r
10642         wp.rcNormalPosition.right = analysisX + analysisW;\r
10643         wp.rcNormalPosition.top = analysisY;\r
10644         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10645         SetWindowPlacement(hDlg, &wp);\r
10646 \r
10647         GetClientRect(hDlg, &rect);\r
10648         newSizeX = rect.right;\r
10649         newSizeY = rect.bottom;\r
10650         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10651                               newSizeX, newSizeY);\r
10652         sizeX = newSizeX;\r
10653         sizeY = newSizeY;\r
10654       }\r
10655     }\r
10656     return FALSE;\r
10657 \r
10658   case WM_COMMAND: /* message: received a command */\r
10659     switch (LOWORD(wParam)) {\r
10660     case IDCANCEL:\r
10661       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10662           ExitAnalyzeMode();\r
10663           ModeHighlight();\r
10664           return TRUE;\r
10665       }\r
10666       EditGameEvent();\r
10667       return TRUE;\r
10668     default:\r
10669       break;\r
10670     }\r
10671     break;\r
10672 \r
10673   case WM_SIZE:\r
10674     newSizeX = LOWORD(lParam);\r
10675     newSizeY = HIWORD(lParam);\r
10676     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10677     sizeX = newSizeX;\r
10678     sizeY = newSizeY;\r
10679     break;\r
10680 \r
10681   case WM_GETMINMAXINFO:\r
10682     /* Prevent resizing window too small */\r
10683     mmi = (MINMAXINFO *) lParam;\r
10684     mmi->ptMinTrackSize.x = 100;\r
10685     mmi->ptMinTrackSize.y = 100;\r
10686     break;\r
10687   }\r
10688   return FALSE;\r
10689 }\r
10690 \r
10691 VOID\r
10692 AnalysisPopUp(char* title, char* str)\r
10693 {\r
10694   FARPROC lpProc;\r
10695   char *p, *q;\r
10696 \r
10697   /* [AS] */\r
10698   EngineOutputPopUp();\r
10699   return;\r
10700 \r
10701   if (str == NULL) str = "";\r
10702   p = (char *) malloc(2 * strlen(str) + 2);\r
10703   q = p;\r
10704   while (*str) {\r
10705     if (*str == '\n') *q++ = '\r';\r
10706     *q++ = *str++;\r
10707   }\r
10708   *q = NULLCHAR;\r
10709   if (analysisText != NULL) free(analysisText);\r
10710   analysisText = p;\r
10711 \r
10712   if (analysisDialog) {\r
10713     SetWindowText(analysisDialog, title);\r
10714     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10715     ShowWindow(analysisDialog, SW_SHOW);\r
10716   } else {\r
10717     analysisTitle = title;\r
10718     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10719     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10720                  hwndMain, (DLGPROC)lpProc);\r
10721     FreeProcInstance(lpProc);\r
10722   }\r
10723   analysisDialogUp = TRUE;  \r
10724 }\r
10725 \r
10726 VOID\r
10727 AnalysisPopDown()\r
10728 {\r
10729   if (analysisDialog) {\r
10730     ShowWindow(analysisDialog, SW_HIDE);\r
10731   }\r
10732   analysisDialogUp = FALSE;  \r
10733 }\r
10734 \r
10735 \r
10736 VOID\r
10737 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10738 {\r
10739   highlightInfo.sq[0].x = fromX;\r
10740   highlightInfo.sq[0].y = fromY;\r
10741   highlightInfo.sq[1].x = toX;\r
10742   highlightInfo.sq[1].y = toY;\r
10743 }\r
10744 \r
10745 VOID\r
10746 ClearHighlights()\r
10747 {\r
10748   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10749     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10750 }\r
10751 \r
10752 VOID\r
10753 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10754 {\r
10755   premoveHighlightInfo.sq[0].x = fromX;\r
10756   premoveHighlightInfo.sq[0].y = fromY;\r
10757   premoveHighlightInfo.sq[1].x = toX;\r
10758   premoveHighlightInfo.sq[1].y = toY;\r
10759 }\r
10760 \r
10761 VOID\r
10762 ClearPremoveHighlights()\r
10763 {\r
10764   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10765     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10766 }\r
10767 \r
10768 VOID\r
10769 ShutDownFrontEnd()\r
10770 {\r
10771   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10772   DeleteClipboardTempFiles();\r
10773 }\r
10774 \r
10775 void\r
10776 BoardToTop()\r
10777 {\r
10778     if (IsIconic(hwndMain))\r
10779       ShowWindow(hwndMain, SW_RESTORE);\r
10780 \r
10781     SetActiveWindow(hwndMain);\r
10782 }\r
10783 \r
10784 /*\r
10785  * Prototypes for animation support routines\r
10786  */\r
10787 static void ScreenSquare(int column, int row, POINT * pt);\r
10788 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10789      POINT frames[], int * nFrames);\r
10790 \r
10791 \r
10792 void\r
10793 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10794 {       // [HGM] atomic: animate blast wave\r
10795         int i;\r
10796 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10797         explodeInfo.fromX = fromX;\r
10798         explodeInfo.fromY = fromY;\r
10799         explodeInfo.toX = toX;\r
10800         explodeInfo.toY = toY;\r
10801         for(i=1; i<nFrames; i++) {\r
10802             explodeInfo.radius = (i*180)/(nFrames-1);\r
10803             DrawPosition(FALSE, NULL);\r
10804             Sleep(appData.animSpeed);\r
10805         }\r
10806         explodeInfo.radius = 0;\r
10807         DrawPosition(TRUE, NULL);\r
10808 }\r
10809 \r
10810 #define kFactor 4\r
10811 \r
10812 void\r
10813 AnimateMove(board, fromX, fromY, toX, toY)\r
10814      Board board;\r
10815      int fromX;\r
10816      int fromY;\r
10817      int toX;\r
10818      int toY;\r
10819 {\r
10820   ChessSquare piece;\r
10821   POINT start, finish, mid;\r
10822   POINT frames[kFactor * 2 + 1];\r
10823   int nFrames, n;\r
10824 \r
10825   if (!appData.animate) return;\r
10826   if (doingSizing) return;\r
10827   if (fromY < 0 || fromX < 0) return;\r
10828   piece = board[fromY][fromX];\r
10829   if (piece >= EmptySquare) return;\r
10830 \r
10831   ScreenSquare(fromX, fromY, &start);\r
10832   ScreenSquare(toX, toY, &finish);\r
10833 \r
10834   /* All pieces except knights move in straight line */\r
10835   if (piece != WhiteKnight && piece != BlackKnight) {\r
10836     mid.x = start.x + (finish.x - start.x) / 2;\r
10837     mid.y = start.y + (finish.y - start.y) / 2;\r
10838   } else {\r
10839     /* Knight: make diagonal movement then straight */\r
10840     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10841        mid.x = start.x + (finish.x - start.x) / 2;\r
10842        mid.y = finish.y;\r
10843      } else {\r
10844        mid.x = finish.x;\r
10845        mid.y = start.y + (finish.y - start.y) / 2;\r
10846      }\r
10847   }\r
10848   \r
10849   /* Don't use as many frames for very short moves */\r
10850   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10851     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10852   else\r
10853     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10854 \r
10855   animInfo.from.x = fromX;\r
10856   animInfo.from.y = fromY;\r
10857   animInfo.to.x = toX;\r
10858   animInfo.to.y = toY;\r
10859   animInfo.lastpos = start;\r
10860   animInfo.piece = piece;\r
10861   for (n = 0; n < nFrames; n++) {\r
10862     animInfo.pos = frames[n];\r
10863     DrawPosition(FALSE, NULL);\r
10864     animInfo.lastpos = animInfo.pos;\r
10865     Sleep(appData.animSpeed);\r
10866   }\r
10867   animInfo.pos = finish;\r
10868   DrawPosition(FALSE, NULL);\r
10869   animInfo.piece = EmptySquare;\r
10870   if(gameInfo.variant == VariantAtomic && \r
10871      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10872         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10873 }\r
10874 \r
10875 /*      Convert board position to corner of screen rect and color       */\r
10876 \r
10877 static void\r
10878 ScreenSquare(column, row, pt)\r
10879      int column; int row; POINT * pt;\r
10880 {\r
10881   if (flipView) {\r
10882     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10883     pt->y = lineGap + row * (squareSize + lineGap);\r
10884   } else {\r
10885     pt->x = lineGap + column * (squareSize + lineGap);\r
10886     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10887   }\r
10888 }\r
10889 \r
10890 /*      Generate a series of frame coords from start->mid->finish.\r
10891         The movement rate doubles until the half way point is\r
10892         reached, then halves back down to the final destination,\r
10893         which gives a nice slow in/out effect. The algorithmn\r
10894         may seem to generate too many intermediates for short\r
10895         moves, but remember that the purpose is to attract the\r
10896         viewers attention to the piece about to be moved and\r
10897         then to where it ends up. Too few frames would be less\r
10898         noticeable.                                             */\r
10899 \r
10900 static void\r
10901 Tween(start, mid, finish, factor, frames, nFrames)\r
10902      POINT * start; POINT * mid;\r
10903      POINT * finish; int factor;\r
10904      POINT frames[]; int * nFrames;\r
10905 {\r
10906   int n, fraction = 1, count = 0;\r
10907 \r
10908   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10909   for (n = 0; n < factor; n++)\r
10910     fraction *= 2;\r
10911   for (n = 0; n < factor; n++) {\r
10912     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10913     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10914     count ++;\r
10915     fraction = fraction / 2;\r
10916   }\r
10917   \r
10918   /* Midpoint */\r
10919   frames[count] = *mid;\r
10920   count ++;\r
10921   \r
10922   /* Slow out, stepping 1/2, then 1/4, ... */\r
10923   fraction = 2;\r
10924   for (n = 0; n < factor; n++) {\r
10925     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10926     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10927     count ++;\r
10928     fraction = fraction * 2;\r
10929   }\r
10930   *nFrames = count;\r
10931 }\r
10932 \r
10933 void\r
10934 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10935 {\r
10936     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10937 \r
10938     EvalGraphSet( first, last, current, pvInfoList );\r
10939 }\r
10940 \r
10941 void SetProgramStats( FrontEndProgramStats * stats )\r
10942 {\r
10943     EngineOutputUpdate( stats );\r
10944 }\r