Added a command-line option -keepLineBreaksICS true/false to control line joining.
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "winboard.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "wgamelist.h"\r
89 #include "wedittags.h"\r
90 #include "woptions.h"\r
91 #include "wsockerr.h"\r
92 #include "defaults.h"\r
93 #include "help.h"\r
94 #include "wsnap.h"\r
95 \r
96 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
97 \r
98   int myrandom(void);\r
99   void mysrandom(unsigned int seed);\r
100 \r
101 extern int whiteFlag, blackFlag;\r
102 Boolean flipClock = FALSE;\r
103 extern HANDLE chatHandle[];\r
104 extern int ics_type;\r
105 \r
106 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
107 VOID NewVariantPopup(HWND hwnd);\r
108 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
109                    /*char*/int promoChar));\r
110 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
111 void DisplayMove P((int moveNumber));\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P(());\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129 } DragInfo;\r
130 \r
131 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
132 \r
133 typedef struct {\r
134   POINT sq[2];    /* board coordinates of from, to squares */\r
135 } HighlightInfo;\r
136 \r
137 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 \r
140 typedef struct { // [HGM] atomic\r
141   int fromX, fromY, toX, toY, radius;\r
142 } ExplodeInfo;\r
143 \r
144 static ExplodeInfo explodeInfo;\r
145 \r
146 /* Window class names */\r
147 char szAppName[] = "WinBoard";\r
148 char szConsoleName[] = "WBConsole";\r
149 \r
150 /* Title bar text */\r
151 char szTitle[] = "WinBoard";\r
152 char szConsoleTitle[] = "I C S Interaction";\r
153 \r
154 char *programName;\r
155 char *settingsFileName;\r
156 BOOLEAN saveSettingsOnExit;\r
157 char installDir[MSG_SIZ];\r
158 \r
159 BoardSize boardSize;\r
160 BOOLEAN chessProgram;\r
161 static int boardX, boardY;\r
162 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
163 static int squareSize, lineGap, minorSize;\r
164 static int winWidth, winHeight, winW, winH;\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
166 static int logoHeight = 0;\r
167 static char messageText[MESSAGE_TEXT_MAX];\r
168 static int clockTimerEvent = 0;\r
169 static int loadGameTimerEvent = 0;\r
170 static int analysisTimerEvent = 0;\r
171 static DelayedEventCallback delayedTimerCallback;\r
172 static int delayedTimerEvent = 0;\r
173 static int buttonCount = 2;\r
174 char *icsTextMenuString;\r
175 char *icsNames;\r
176 char *firstChessProgramNames;\r
177 char *secondChessProgramNames;\r
178 \r
179 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
180 \r
181 #define PALETTESIZE 256\r
182 \r
183 HINSTANCE hInst;          /* current instance */\r
184 HWND hwndMain = NULL;        /* root window*/\r
185 HWND hwndConsole = NULL;\r
186 BOOLEAN alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 HWND hCommPort = NULL;    /* currently open comm port */\r
194 static HWND hwndPause;    /* pause button */\r
195 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
196 static HBRUSH lightSquareBrush, darkSquareBrush,\r
197   blackSquareBrush, /* [HGM] for band between board and holdings */\r
198   explodeBrush,     /* [HGM] atomic */\r
199   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
200 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
201 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
202 static HPEN gridPen = NULL;\r
203 static HPEN highlightPen = NULL;\r
204 static HPEN premovePen = NULL;\r
205 static NPLOGPALETTE pLogPal;\r
206 static BOOL paletteChanged = FALSE;\r
207 static HICON iconWhite, iconBlack, iconCurrent;\r
208 static int doingSizing = FALSE;\r
209 static int lastSizing = 0;\r
210 static int prevStderrPort;\r
211 static HBITMAP userLogo;\r
212 \r
213 /* [AS] Support for background textures */\r
214 #define BACK_TEXTURE_MODE_DISABLED      0\r
215 #define BACK_TEXTURE_MODE_PLAIN         1\r
216 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 #if defined(_winmajor)\r
229 #define oldDialog (_winmajor < 4)\r
230 #else\r
231 #define oldDialog 0\r
232 #endif\r
233 #endif\r
234 \r
235 char *defaultTextAttribs[] = \r
236 {\r
237   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
238   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
239   COLOR_NONE\r
240 };\r
241 \r
242 typedef struct {\r
243   char *name;\r
244   int squareSize;\r
245   int lineGap;\r
246   int smallLayout;\r
247   int tinyLayout;\r
248   int cliWidth, cliHeight;\r
249 } SizeInfo;\r
250 \r
251 SizeInfo sizeInfo[] = \r
252 {\r
253   { "tiny",     21, 0, 1, 1, 0, 0 },\r
254   { "teeny",    25, 1, 1, 1, 0, 0 },\r
255   { "dinky",    29, 1, 1, 1, 0, 0 },\r
256   { "petite",   33, 1, 1, 1, 0, 0 },\r
257   { "slim",     37, 2, 1, 0, 0, 0 },\r
258   { "small",    40, 2, 1, 0, 0, 0 },\r
259   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
260   { "middling", 49, 2, 0, 0, 0, 0 },\r
261   { "average",  54, 2, 0, 0, 0, 0 },\r
262   { "moderate", 58, 3, 0, 0, 0, 0 },\r
263   { "medium",   64, 3, 0, 0, 0, 0 },\r
264   { "bulky",    72, 3, 0, 0, 0, 0 },\r
265   { "large",    80, 3, 0, 0, 0, 0 },\r
266   { "big",      87, 3, 0, 0, 0, 0 },\r
267   { "huge",     95, 3, 0, 0, 0, 0 },\r
268   { "giant",    108, 3, 0, 0, 0, 0 },\r
269   { "colossal", 116, 4, 0, 0, 0, 0 },\r
270   { "titanic",  129, 4, 0, 0, 0, 0 },\r
271   { NULL, 0, 0, 0, 0, 0, 0 }\r
272 };\r
273 \r
274 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
275 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
276 {\r
277   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
278   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
279   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
280   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
281   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
282   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
283   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
284   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
285   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
286   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
287   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
288   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
289   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
290   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
291   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
292   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
293   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
294   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
295 };\r
296 \r
297 MyFont *font[NUM_SIZES][NUM_FONTS];\r
298 \r
299 typedef struct {\r
300   char *label;\r
301   int id;\r
302   HWND hwnd;\r
303   WNDPROC wndproc;\r
304 } MyButtonDesc;\r
305 \r
306 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
307 #define N_BUTTONS 5\r
308 \r
309 MyButtonDesc buttonDesc[N_BUTTONS] =\r
310 {\r
311   {"<<", IDM_ToStart, NULL, NULL},\r
312   {"<", IDM_Backward, NULL, NULL},\r
313   {"P", IDM_Pause, NULL, NULL},\r
314   {">", IDM_Forward, NULL, NULL},\r
315   {">>", IDM_ToEnd, NULL, NULL},\r
316 };\r
317 \r
318 int tinyLayout = 0, smallLayout = 0;\r
319 #define MENU_BAR_ITEMS 7\r
320 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
321   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
322   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
323 };\r
324 \r
325 \r
326 MySound sounds[(int)NSoundClasses];\r
327 MyTextAttribs textAttribs[(int)NColorClasses];\r
328 \r
329 MyColorizeAttribs colorizeAttribs[] = {\r
330   { (COLORREF)0, 0, "Shout Text" },\r
331   { (COLORREF)0, 0, "SShout/CShout" },\r
332   { (COLORREF)0, 0, "Channel 1 Text" },\r
333   { (COLORREF)0, 0, "Channel Text" },\r
334   { (COLORREF)0, 0, "Kibitz Text" },\r
335   { (COLORREF)0, 0, "Tell Text" },\r
336   { (COLORREF)0, 0, "Challenge Text" },\r
337   { (COLORREF)0, 0, "Request Text" },\r
338   { (COLORREF)0, 0, "Seek Text" },\r
339   { (COLORREF)0, 0, "Normal Text" },\r
340   { (COLORREF)0, 0, "None" }\r
341 };\r
342 \r
343 \r
344 \r
345 static char *commentTitle;\r
346 static char *commentText;\r
347 static int commentIndex;\r
348 static Boolean editComment = FALSE;\r
349 HWND commentDialog = NULL;\r
350 BOOLEAN commentDialogUp = FALSE;\r
351 static int commentX, commentY, commentH, commentW;\r
352 \r
353 static char *analysisTitle;\r
354 static char *analysisText;\r
355 HWND analysisDialog = NULL;\r
356 BOOLEAN analysisDialogUp = FALSE;\r
357 static int analysisX, analysisY, analysisH, analysisW;\r
358 \r
359 char errorTitle[MSG_SIZ];\r
360 char errorMessage[2*MSG_SIZ];\r
361 HWND errorDialog = NULL;\r
362 BOOLEAN moveErrorMessageUp = FALSE;\r
363 BOOLEAN consoleEcho = TRUE;\r
364 CHARFORMAT consoleCF;\r
365 COLORREF consoleBackgroundColor;\r
366 \r
367 char *programVersion;\r
368 \r
369 #define CPReal 1\r
370 #define CPComm 2\r
371 #define CPSock 3\r
372 #define CPRcmd 4\r
373 typedef int CPKind;\r
374 \r
375 typedef struct {\r
376   CPKind kind;\r
377   HANDLE hProcess;\r
378   DWORD pid;\r
379   HANDLE hTo;\r
380   HANDLE hFrom;\r
381   SOCKET sock;\r
382   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
383 } ChildProc;\r
384 \r
385 #define INPUT_SOURCE_BUF_SIZE 4096\r
386 \r
387 typedef struct _InputSource {\r
388   CPKind kind;\r
389   HANDLE hFile;\r
390   SOCKET sock;\r
391   int lineByLine;\r
392   HANDLE hThread;\r
393   DWORD id;\r
394   char buf[INPUT_SOURCE_BUF_SIZE];\r
395   char *next;\r
396   DWORD count;\r
397   int error;\r
398   InputCallback func;\r
399   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
400   VOIDSTAR closure;\r
401 } InputSource;\r
402 \r
403 InputSource *consoleInputSource;\r
404 \r
405 DCB dcb;\r
406 \r
407 /* forward */\r
408 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
409 VOID ConsoleCreate();\r
410 LRESULT CALLBACK\r
411   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
412 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
413 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
414 VOID ParseCommSettings(char *arg, DCB *dcb);\r
415 LRESULT CALLBACK\r
416   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
417 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
418 void ParseIcsTextMenu(char *icsTextMenuString);\r
419 VOID PopUpMoveDialog(char firstchar);\r
420 VOID PopUpNameDialog(char firstchar);\r
421 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
422 \r
423 /* [AS] */\r
424 int NewGameFRC();\r
425 int GameListOptions();\r
426 \r
427 HWND moveHistoryDialog = NULL;\r
428 BOOLEAN moveHistoryDialogUp = FALSE;\r
429 \r
430 WindowPlacement wpMoveHistory;\r
431 \r
432 HWND evalGraphDialog = NULL;\r
433 BOOLEAN evalGraphDialogUp = FALSE;\r
434 \r
435 WindowPlacement wpEvalGraph;\r
436 \r
437 HWND engineOutputDialog = NULL;\r
438 BOOLEAN engineOutputDialogUp = FALSE;\r
439 \r
440 WindowPlacement wpEngineOutput;\r
441 WindowPlacement wpGameList;\r
442 WindowPlacement wpConsole;\r
443 \r
444 VOID MoveHistoryPopUp();\r
445 VOID MoveHistoryPopDown();\r
446 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
447 BOOL MoveHistoryIsUp();\r
448 \r
449 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
450 VOID EvalGraphPopUp();\r
451 VOID EvalGraphPopDown();\r
452 BOOL EvalGraphIsUp();\r
453 \r
454 VOID EngineOutputPopUp();\r
455 VOID EngineOutputPopDown();\r
456 BOOL EngineOutputIsUp();\r
457 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
458 \r
459 VOID EngineOptionsPopup(); // [HGM] settings\r
460 \r
461 VOID GothicPopUp(char *title, VariantClass variant);\r
462 /*\r
463  * Setting "frozen" should disable all user input other than deleting\r
464  * the window.  We do this while engines are initializing themselves.\r
465  */\r
466 static int frozen = 0;\r
467 static int oldMenuItemState[MENU_BAR_ITEMS];\r
468 void FreezeUI()\r
469 {\r
470   HMENU hmenu;\r
471   int i;\r
472 \r
473   if (frozen) return;\r
474   frozen = 1;\r
475   hmenu = GetMenu(hwndMain);\r
476   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
477     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
478   }\r
479   DrawMenuBar(hwndMain);\r
480 }\r
481 \r
482 /* Undo a FreezeUI */\r
483 void ThawUI()\r
484 {\r
485   HMENU hmenu;\r
486   int i;\r
487 \r
488   if (!frozen) return;\r
489   frozen = 0;\r
490   hmenu = GetMenu(hwndMain);\r
491   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
492     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
493   }\r
494   DrawMenuBar(hwndMain);\r
495 }\r
496 \r
497 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
498 \r
499 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
500 #ifdef JAWS\r
501 #include "jaws.c"\r
502 #else\r
503 #define JAWS_INIT\r
504 #define JAWS_ARGS\r
505 #define JAWS_ALT_INTERCEPT\r
506 #define JAWS_KB_NAVIGATION\r
507 #define JAWS_MENU_ITEMS\r
508 #define JAWS_SILENCE\r
509 #define JAWS_REPLAY\r
510 #define JAWS_ACCEL\r
511 #define JAWS_COPYRIGHT\r
512 #define JAWS_DELETE(X) X\r
513 #define SAYMACHINEMOVE()\r
514 #define SAY(X)\r
515 #endif\r
516 \r
517 /*---------------------------------------------------------------------------*\\r
518  *\r
519  * WinMain\r
520  *\r
521 \*---------------------------------------------------------------------------*/\r
522 \r
523 int APIENTRY\r
524 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
525         LPSTR lpCmdLine, int nCmdShow)\r
526 {\r
527   MSG msg;\r
528   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
529 //  INITCOMMONCONTROLSEX ex;\r
530 \r
531   debugFP = stderr;\r
532 \r
533   LoadLibrary("RICHED32.DLL");\r
534   consoleCF.cbSize = sizeof(CHARFORMAT);\r
535 \r
536   if (!InitApplication(hInstance)) {\r
537     return (FALSE);\r
538   }\r
539   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
540     return (FALSE);\r
541   }\r
542 \r
543   JAWS_INIT\r
544 \r
545 //  InitCommonControlsEx(&ex);\r
546   InitCommonControls();\r
547 \r
548   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
549   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
550   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
551 \r
552   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
553 \r
554   while (GetMessage(&msg, /* message structure */\r
555                     NULL, /* handle of window receiving the message */\r
556                     0,    /* lowest message to examine */\r
557                     0))   /* highest message to examine */\r
558     {\r
559 \r
560       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
561         // [HGM] navigate: switch between all windows with tab\r
562         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
563         int i, currentElement = 0;\r
564 \r
565         // first determine what element of the chain we come from (if any)\r
566         if(appData.icsActive) {\r
567             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
568             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
569         }\r
570         if(engineOutputDialog && EngineOutputIsUp()) {\r
571             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
572             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
573         }\r
574         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
575             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
576         }\r
577         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
578         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
579         if(msg.hwnd == e1)                 currentElement = 2; else\r
580         if(msg.hwnd == e2)                 currentElement = 3; else\r
581         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
582         if(msg.hwnd == mh)                currentElement = 4; else\r
583         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
584         if(msg.hwnd == hText)  currentElement = 5; else\r
585         if(msg.hwnd == hInput) currentElement = 6; else\r
586         for (i = 0; i < N_BUTTONS; i++) {\r
587             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
588         }\r
589 \r
590         // determine where to go to\r
591         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
592           do {\r
593             currentElement = (currentElement + direction) % 7;\r
594             switch(currentElement) {\r
595                 case 0:\r
596                   h = hwndMain; break; // passing this case always makes the loop exit\r
597                 case 1:\r
598                   h = buttonDesc[0].hwnd; break; // could be NULL\r
599                 case 2:\r
600                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
601                   h = e1; break;\r
602                 case 3:\r
603                   if(!EngineOutputIsUp()) continue;\r
604                   h = e2; break;\r
605                 case 4:\r
606                   if(!MoveHistoryIsUp()) continue;\r
607                   h = mh; break;\r
608 //              case 6: // input to eval graph does not seem to get here!\r
609 //                if(!EvalGraphIsUp()) continue;\r
610 //                h = evalGraphDialog; break;\r
611                 case 5:\r
612                   if(!appData.icsActive) continue;\r
613                   SAY("display");\r
614                   h = hText; break;\r
615                 case 6:\r
616                   if(!appData.icsActive) continue;\r
617                   SAY("input");\r
618                   h = hInput; break;\r
619             }\r
620           } while(h == 0);\r
621 \r
622           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
623           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
624           SetFocus(h);\r
625 \r
626           continue; // this message now has been processed\r
627         }\r
628       }\r
629 \r
630       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
631           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
632           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
633           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
634           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
635           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
636           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
637           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
638           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
639           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
640         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
641         for(i=0; i<MAX_CHAT; i++) \r
642             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
643                 done = 1; break;\r
644         }\r
645         if(done) continue; // [HGM] chat: end patch\r
646         TranslateMessage(&msg); /* Translates virtual key codes */\r
647         DispatchMessage(&msg);  /* Dispatches message to window */\r
648       }\r
649     }\r
650 \r
651 \r
652   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
653 }\r
654 \r
655 /*---------------------------------------------------------------------------*\\r
656  *\r
657  * Initialization functions\r
658  *\r
659 \*---------------------------------------------------------------------------*/\r
660 \r
661 void\r
662 SetUserLogo()\r
663 {   // update user logo if necessary\r
664     static char oldUserName[MSG_SIZ], *curName;\r
665 \r
666     if(appData.autoLogo) {\r
667           curName = UserName();\r
668           if(strcmp(curName, oldUserName)) {\r
669                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
670                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
671                 strcpy(oldUserName, curName);\r
672           }\r
673     }\r
674 }\r
675 \r
676 BOOL\r
677 InitApplication(HINSTANCE hInstance)\r
678 {\r
679   WNDCLASS wc;\r
680 \r
681   /* Fill in window class structure with parameters that describe the */\r
682   /* main window. */\r
683 \r
684   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
685   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
686   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
687   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
688   wc.hInstance     = hInstance;         /* Owner of this class */\r
689   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
690   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
691   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
692   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
693   wc.lpszClassName = szAppName;                 /* Name to register as */\r
694 \r
695   /* Register the window class and return success/failure code. */\r
696   if (!RegisterClass(&wc)) return FALSE;\r
697 \r
698   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
699   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
700   wc.cbClsExtra    = 0;\r
701   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
702   wc.hInstance     = hInstance;\r
703   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
704   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
705   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
706   wc.lpszMenuName  = NULL;\r
707   wc.lpszClassName = szConsoleName;\r
708 \r
709   if (!RegisterClass(&wc)) return FALSE;\r
710   return TRUE;\r
711 }\r
712 \r
713 \r
714 /* Set by InitInstance, used by EnsureOnScreen */\r
715 int screenHeight, screenWidth;\r
716 \r
717 void\r
718 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
719 {\r
720 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
721   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
722   if (*x > screenWidth - 32) *x = 0;\r
723   if (*y > screenHeight - 32) *y = 0;\r
724   if (*x < minX) *x = minX;\r
725   if (*y < minY) *y = minY;\r
726 }\r
727 \r
728 BOOL\r
729 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
730 {\r
731   HWND hwnd; /* Main window handle. */\r
732   int ibs;\r
733   WINDOWPLACEMENT wp;\r
734   char *filepart;\r
735 \r
736   hInst = hInstance;    /* Store instance handle in our global variable */\r
737 \r
738   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
739     *filepart = NULLCHAR;\r
740   } else {\r
741     GetCurrentDirectory(MSG_SIZ, installDir);\r
742   }\r
743   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
744   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
745   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
746   if (appData.debugMode) {\r
747     debugFP = fopen(appData.nameOfDebugFile, "w");\r
748     setbuf(debugFP, NULL);\r
749   }\r
750 \r
751   InitBackEnd1();\r
752 \r
753 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
754 //  InitEngineUCI( installDir, &second );\r
755 \r
756   /* Create a main window for this application instance. */\r
757   hwnd = CreateWindow(szAppName, szTitle,\r
758                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
759                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
760                       NULL, NULL, hInstance, NULL);\r
761   hwndMain = hwnd;\r
762 \r
763   /* If window could not be created, return "failure" */\r
764   if (!hwnd) {\r
765     return (FALSE);\r
766   }\r
767 \r
768   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
769   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
770       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
771 \r
772       if (first.programLogo == NULL && appData.debugMode) {\r
773           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
774       }\r
775   } else if(appData.autoLogo) {\r
776       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
777         char buf[MSG_SIZ];\r
778         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
779         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
780       }\r
781   }\r
782 \r
783   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
784       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
785 \r
786       if (second.programLogo == NULL && appData.debugMode) {\r
787           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
788       }\r
789   } else if(appData.autoLogo) {\r
790       char buf[MSG_SIZ];\r
791       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
792         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
793         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
794       } else\r
795       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
796         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
797         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
798       }\r
799   }\r
800 \r
801   SetUserLogo();\r
802 \r
803   iconWhite = LoadIcon(hInstance, "icon_white");\r
804   iconBlack = LoadIcon(hInstance, "icon_black");\r
805   iconCurrent = iconWhite;\r
806   InitDrawingColors();\r
807   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
808   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
809   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
810     /* Compute window size for each board size, and use the largest\r
811        size that fits on this screen as the default. */\r
812     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
813     if (boardSize == (BoardSize)-1 &&\r
814         winH <= screenHeight\r
815            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
816         && winW <= screenWidth) {\r
817       boardSize = (BoardSize)ibs;\r
818     }\r
819   }\r
820 \r
821   InitDrawingSizes(boardSize, 0);\r
822   InitMenuChecks();\r
823   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
824 \r
825   /* [AS] Load textures if specified */\r
826   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
827   \r
828   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
829       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
830       liteBackTextureMode = appData.liteBackTextureMode;\r
831 \r
832       if (liteBackTexture == NULL && appData.debugMode) {\r
833           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
834       }\r
835   }\r
836   \r
837   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
838       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
839       darkBackTextureMode = appData.darkBackTextureMode;\r
840 \r
841       if (darkBackTexture == NULL && appData.debugMode) {\r
842           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
843       }\r
844   }\r
845 \r
846   mysrandom( (unsigned) time(NULL) );\r
847 \r
848   /* [AS] Restore layout */\r
849   if( wpMoveHistory.visible ) {\r
850       MoveHistoryPopUp();\r
851   }\r
852 \r
853   if( wpEvalGraph.visible ) {\r
854       EvalGraphPopUp();\r
855   }\r
856 \r
857   if( wpEngineOutput.visible ) {\r
858       EngineOutputPopUp();\r
859   }\r
860 \r
861   InitBackEnd2();\r
862 \r
863   /* Make the window visible; update its client area; and return "success" */\r
864   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
865   wp.length = sizeof(WINDOWPLACEMENT);\r
866   wp.flags = 0;\r
867   wp.showCmd = nCmdShow;\r
868   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
869   wp.rcNormalPosition.left = boardX;\r
870   wp.rcNormalPosition.right = boardX + winWidth;\r
871   wp.rcNormalPosition.top = boardY;\r
872   wp.rcNormalPosition.bottom = boardY + winHeight;\r
873   SetWindowPlacement(hwndMain, &wp);\r
874 \r
875   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
876                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
877 \r
878   if (hwndConsole) {\r
879 #if AOT_CONSOLE\r
880     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
881                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
882 #endif\r
883     ShowWindow(hwndConsole, nCmdShow);\r
884   }\r
885   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
886   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
887 \r
888   return TRUE;\r
889 \r
890 }\r
891 \r
892 \r
893 typedef enum {\r
894   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
895   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
896   ArgSettingsFilename,\r
897   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
898 } ArgType;\r
899 \r
900 typedef struct {\r
901   char *argName;\r
902   ArgType argType;\r
903   /***\r
904   union {\r
905     String *pString;       // ArgString\r
906     int *pInt;             // ArgInt\r
907     float *pFloat;         // ArgFloat\r
908     Boolean *pBoolean;     // ArgBoolean\r
909     COLORREF *pColor;      // ArgColor\r
910     ColorClass cc;         // ArgAttribs\r
911     String *pFilename;     // ArgFilename\r
912     BoardSize *pBoardSize; // ArgBoardSize\r
913     int whichFont;         // ArgFont\r
914     DCB *pDCB;             // ArgCommSettings\r
915     String *pFilename;     // ArgSettingsFilename\r
916   } argLoc;\r
917   ***/\r
918   LPVOID argLoc;\r
919   BOOL save;\r
920 } ArgDescriptor;\r
921 \r
922 int junk;\r
923 ArgDescriptor argDescriptors[] = {\r
924   /* positional arguments */\r
925   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
926   { "", ArgNone, NULL },\r
927   /* keyword arguments */\r
928   JAWS_ARGS\r
929   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
930   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
931   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
932   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
933   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
934   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
935   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
936   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
937   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
938   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
939   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
940   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
941   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
942   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
943   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
944   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
945   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
946   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
947     FALSE },\r
948   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
949     FALSE },\r
950   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
951     FALSE },\r
952   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
953   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
954     FALSE },\r
955   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
956   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
957   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
958   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
959   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
960   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
961   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
962   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
963   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
964   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
965   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
966   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
967   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
968   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
969   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
970   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
971   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
972   /*!!bitmapDirectory?*/\r
973   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
974   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
975   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
976   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
977   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
978   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
979   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
980   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
981   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
982   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
983   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
984   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
985   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
986   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
987   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
988   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
989   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
990   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
991   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
992   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
993   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
994   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
995   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
996   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
997   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
998   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
999   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
1000   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
1001   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
1002   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
1003   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
1004   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
1005   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
1006   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1007   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1008   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
1009   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
1010   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
1011   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
1012   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1013   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1014   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1015   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1016   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1017   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1018   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1019   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1020   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
1021   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1022   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1023   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1024   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1025   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1026   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1027   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1028   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1029   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1030   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1031   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1032   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1033   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1034   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1035   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1036   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1037   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1038   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1039   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1040   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1041   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1042   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1043   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1044   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1045   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1046   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1047   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1048   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1049   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1050   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1051   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1052   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1053   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1054   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1055   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1056   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1057   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1058   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1059   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1060   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1061   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1062   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1063   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1064   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1065   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1066   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1067     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1068   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1069   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1070   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1071   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1072   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1073   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1074   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1075   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1076     TRUE }, /* must come after all fonts */\r
1077   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1078   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1079     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1080   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1081   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1082   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1083   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1084   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1085   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1086   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1087   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1088   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1089   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1090   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1091   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1092   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1093   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1094   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1095   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1096   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1097   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1098   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1099   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1100   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1101   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1102   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1103   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1104   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1105   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1106   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1107   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1108   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1109   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1110   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1111   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1112   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1113   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1114   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1115   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1116   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1117   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1118   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1119   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1120   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1121   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1122   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1123   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1124   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1125   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1126   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1127   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1128   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1129   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1130   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1131   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1132   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1133   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1134   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1135   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1136   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1137   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1138   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1139   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1140   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1141   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1142   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1143   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1144   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1145   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1146   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1147   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1148   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1149   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1150   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1151   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1152   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1153   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1154   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1155   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1156   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1157   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1158   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1159   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1160   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1161   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1162   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1163   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1164   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1165   { "highlightLastMove", ArgBoolean,\r
1166     (LPVOID) &appData.highlightLastMove, TRUE },\r
1167   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1168   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1169   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1170   { "highlightDragging", ArgBoolean,\r
1171     (LPVOID) &appData.highlightDragging, TRUE },\r
1172   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1173   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1174   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1175   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1176   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1177   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1178   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1179   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1180   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1181   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1182   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1183   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1184   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1185   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1186   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1187   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1188   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1189   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1190   { "soundShout", ArgFilename,\r
1191     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1192   { "soundSShout", ArgFilename,\r
1193     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1194   { "soundChannel1", ArgFilename,\r
1195     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1196   { "soundChannel", ArgFilename,\r
1197     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1198   { "soundKibitz", ArgFilename,\r
1199     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1200   { "soundTell", ArgFilename,\r
1201     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1202   { "soundChallenge", ArgFilename,\r
1203     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1204   { "soundRequest", ArgFilename,\r
1205     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1206   { "soundSeek", ArgFilename,\r
1207     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1208   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1209   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1210   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1211   { "soundIcsLoss", ArgFilename, \r
1212     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1213   { "soundIcsDraw", ArgFilename, \r
1214     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1215   { "soundIcsUnfinished", ArgFilename, \r
1216     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1217   { "soundIcsAlarm", ArgFilename, \r
1218     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1219   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1220   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1221   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1222   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1223   { "reuseChessPrograms", ArgBoolean,\r
1224     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1225   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1226   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1227   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1228   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1229   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1230   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1231   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1232   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1233   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1234   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1235   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1236   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1237   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1238   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1239   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1240     TRUE },\r
1241   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1242     TRUE },\r
1243   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1244   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1245   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1246   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1247   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1248   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1249   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1250   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1251   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1252   /* [AS] New features */\r
1253   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1254   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1255   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1256   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1257   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1258   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1259   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1260   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1261   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1262   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1263   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1264   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1265   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1266   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1267   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1268   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1269   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1270   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1271   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1272   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1273   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1274   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1275   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1276   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1277   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1278   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1279   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1280   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1281   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1282   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1283   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1284   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1285   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1286   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1287   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1288   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1289   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1290   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1291   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1292   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1293   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1294   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1295   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1296   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1297   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1298   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1299   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1300   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1301   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1302   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1303 \r
1304   /* [HGM] board-size, adjudication and misc. options */\r
1305   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1306   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1307   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1308   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1309   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1310   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1311   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1312   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1313   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1314   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1315   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1316   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1317   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1318   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1319   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1320   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1321   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1322   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1323   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1324   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1325   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1326   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1327   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1328   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1329   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1330   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1331   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1332   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1333   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1334   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1335   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1336   { "keepAlive", ArgInt, (LPVOID) &appData.keepAlive, FALSE },\r
1337   { "icstype", ArgInt, (LPVOID) &ics_type, FALSE },\r
1338   { "forceIllegalMoves", ArgTrue, (LPVOID) &appData.forceIllegal, FALSE },\r
1339 \r
1340 #ifdef ZIPPY\r
1341   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1342   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1343   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1344   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1345   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1346   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1347   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1348   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1349   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1350   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1351   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1352   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1353   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1354     FALSE },\r
1355   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1356   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1357   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1358   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1359   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1360   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1361   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1362     FALSE },\r
1363   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1364   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1365   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1366   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1367   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1368   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1369   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1370   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1371   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1372   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1373   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1374   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1375   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1376   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1377   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1378   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1379   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1380   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1381   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1382 #endif\r
1383   /* [HGM] options for broadcasting and time odds */\r
1384   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1385   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1386   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1387   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1388   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1389   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1390   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1391   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1392   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1393   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1394   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1395   { "keepLineBreaksICS", ArgBoolean, (LPVOID) &appData.noJoin, TRUE },\r
1396 \r
1397   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1398   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1399   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1400   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1401   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1402   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1403   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1404   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1405   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1406   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1407   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1408   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1409   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1410   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1411   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1412   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1413   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1414   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1415   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1416   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1417   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1418   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1419   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1420   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1421   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1422   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1423   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1424   /* [AS] Layout stuff */\r
1425   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1426   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1427   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1428   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1429   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1430 \r
1431   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1432   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1433   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1434   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1435   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1436 \r
1437   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1438   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1439   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1440   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1441   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1442 \r
1443   { NULL, ArgNone, NULL, FALSE }\r
1444 };\r
1445 \r
1446 \r
1447 /* Kludge for indirection files on command line */\r
1448 char* lastIndirectionFilename;\r
1449 ArgDescriptor argDescriptorIndirection =\r
1450 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1451 \r
1452 \r
1453 VOID\r
1454 ExitArgError(char *msg, char *badArg)\r
1455 {\r
1456   char buf[MSG_SIZ];\r
1457 \r
1458   sprintf(buf, "%s %s", msg, badArg);\r
1459   DisplayFatalError(buf, 0, 2);\r
1460   exit(2);\r
1461 }\r
1462 \r
1463 /* Command line font name parser.  NULL name means do nothing.\r
1464    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1465    For backward compatibility, syntax without the colon is also\r
1466    accepted, but font names with digits in them won't work in that case.\r
1467 */\r
1468 VOID\r
1469 ParseFontName(char *name, MyFontParams *mfp)\r
1470 {\r
1471   char *p, *q;\r
1472   if (name == NULL) return;\r
1473   p = name;\r
1474   q = strchr(p, ':');\r
1475   if (q) {\r
1476     if (q - p >= sizeof(mfp->faceName))\r
1477       ExitArgError("Font name too long:", name);\r
1478     memcpy(mfp->faceName, p, q - p);\r
1479     mfp->faceName[q - p] = NULLCHAR;\r
1480     p = q + 1;\r
1481   } else {\r
1482     q = mfp->faceName;\r
1483     while (*p && !isdigit(*p)) {\r
1484       *q++ = *p++;\r
1485       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1486         ExitArgError("Font name too long:", name);\r
1487     }\r
1488     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1489     *q = NULLCHAR;\r
1490   }\r
1491   if (!*p) ExitArgError("Font point size missing:", name);\r
1492   mfp->pointSize = (float) atof(p);\r
1493   mfp->bold = (strchr(p, 'b') != NULL);\r
1494   mfp->italic = (strchr(p, 'i') != NULL);\r
1495   mfp->underline = (strchr(p, 'u') != NULL);\r
1496   mfp->strikeout = (strchr(p, 's') != NULL);\r
1497 }\r
1498 \r
1499 /* Color name parser.\r
1500    X version accepts X color names, but this one\r
1501    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1502 COLORREF\r
1503 ParseColorName(char *name)\r
1504 {\r
1505   int red, green, blue, count;\r
1506   char buf[MSG_SIZ];\r
1507 \r
1508   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1509   if (count != 3) {\r
1510     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1511       &red, &green, &blue);\r
1512   }\r
1513   if (count != 3) {\r
1514     sprintf(buf, "Can't parse color name %s", name);\r
1515     DisplayError(buf, 0);\r
1516     return RGB(0, 0, 0);\r
1517   }\r
1518   return PALETTERGB(red, green, blue);\r
1519 }\r
1520 \r
1521 \r
1522 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1523 {\r
1524   char *e = argValue;\r
1525   int eff = 0;\r
1526 \r
1527   while (*e) {\r
1528     if (*e == 'b')      eff |= CFE_BOLD;\r
1529     else if (*e == 'i') eff |= CFE_ITALIC;\r
1530     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1531     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1532     else if (*e == '#' || isdigit(*e)) break;\r
1533     e++;\r
1534   }\r
1535   *effects = eff;\r
1536   *color   = ParseColorName(e);\r
1537 }\r
1538 \r
1539 \r
1540 BoardSize\r
1541 ParseBoardSize(char *name)\r
1542 {\r
1543   BoardSize bs = SizeTiny;\r
1544   while (sizeInfo[bs].name != NULL) {\r
1545     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1546     bs++;\r
1547   }\r
1548   ExitArgError("Unrecognized board size value", name);\r
1549   return bs; /* not reached */\r
1550 }\r
1551 \r
1552 \r
1553 char\r
1554 StringGet(void *getClosure)\r
1555 {\r
1556   char **p = (char **) getClosure;\r
1557   return *((*p)++);\r
1558 }\r
1559 \r
1560 char\r
1561 FileGet(void *getClosure)\r
1562 {\r
1563   int c;\r
1564   FILE* f = (FILE*) getClosure;\r
1565 \r
1566   c = getc(f);\r
1567   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1568   if (c == EOF)\r
1569     return NULLCHAR;\r
1570   else\r
1571     return (char) c;\r
1572 }\r
1573 \r
1574 /* Parse settings file named "name". If file found, return the\r
1575    full name in fullname and return TRUE; else return FALSE */\r
1576 BOOLEAN\r
1577 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1578 {\r
1579   char *dummy;\r
1580   FILE *f;\r
1581   int ok; char buf[MSG_SIZ];\r
1582 \r
1583   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1584   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1585     sprintf(buf, "%s.ini", name);\r
1586     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1587   }\r
1588   if (ok) {\r
1589     f = fopen(fullname, "r");\r
1590     if (f != NULL) {\r
1591       ParseArgs(FileGet, f);\r
1592       fclose(f);\r
1593       return TRUE;\r
1594     }\r
1595   }\r
1596   return FALSE;\r
1597 }\r
1598 \r
1599 VOID\r
1600 ParseArgs(GetFunc get, void *cl)\r
1601 {\r
1602   char argName[ARG_MAX];\r
1603   char argValue[ARG_MAX];\r
1604   ArgDescriptor *ad;\r
1605   char start;\r
1606   char *q;\r
1607   int i, octval;\r
1608   char ch;\r
1609   int posarg = 0;\r
1610 \r
1611   ch = get(cl);\r
1612   for (;;) {\r
1613     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1614     if (ch == NULLCHAR) break;\r
1615     if (ch == ';') {\r
1616       /* Comment to end of line */\r
1617       ch = get(cl);\r
1618       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1619       continue;\r
1620     } else if (ch == '/' || ch == '-') {\r
1621       /* Switch */\r
1622       q = argName;\r
1623       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1624              ch != '\n' && ch != '\t') {\r
1625         *q++ = ch;\r
1626         ch = get(cl);\r
1627       }\r
1628       *q = NULLCHAR;\r
1629 \r
1630       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1631         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1632 \r
1633       if (ad->argName == NULL)\r
1634         ExitArgError("Unrecognized argument", argName);\r
1635 \r
1636     } else if (ch == '@') {\r
1637       /* Indirection file */\r
1638       ad = &argDescriptorIndirection;\r
1639       ch = get(cl);\r
1640     } else {\r
1641       /* Positional argument */\r
1642       ad = &argDescriptors[posarg++];\r
1643       strcpy(argName, ad->argName);\r
1644     }\r
1645 \r
1646     if (ad->argType == ArgTrue) {\r
1647       *(Boolean *) ad->argLoc = TRUE;\r
1648       continue;\r
1649     }\r
1650     if (ad->argType == ArgFalse) {\r
1651       *(Boolean *) ad->argLoc = FALSE;\r
1652       continue;\r
1653     }\r
1654 \r
1655     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1656     if (ch == NULLCHAR || ch == '\n') {\r
1657       ExitArgError("No value provided for argument", argName);\r
1658     }\r
1659     q = argValue;\r
1660     if (ch == '{') {\r
1661       // Quoting with { }.  No characters have to (or can) be escaped.\r
1662       // Thus the string cannot contain a '}' character.\r
1663       start = ch;\r
1664       ch = get(cl);\r
1665       while (start) {\r
1666         switch (ch) {\r
1667         case NULLCHAR:\r
1668           start = NULLCHAR;\r
1669           break;\r
1670           \r
1671         case '}':\r
1672           ch = get(cl);\r
1673           start = NULLCHAR;\r
1674           break;\r
1675 \r
1676         default:\r
1677           *q++ = ch;\r
1678           ch = get(cl);\r
1679           break;\r
1680         }\r
1681       }   \r
1682     } else if (ch == '\'' || ch == '"') {\r
1683       // Quoting with ' ' or " ", with \ as escape character.\r
1684       // Inconvenient for long strings that may contain Windows filenames.\r
1685       start = ch;\r
1686       ch = get(cl);\r
1687       while (start) {\r
1688         switch (ch) {\r
1689         case NULLCHAR:\r
1690           start = NULLCHAR;\r
1691           break;\r
1692 \r
1693         default:\r
1694         not_special:\r
1695           *q++ = ch;\r
1696           ch = get(cl);\r
1697           break;\r
1698 \r
1699         case '\'':\r
1700         case '\"':\r
1701           if (ch == start) {\r
1702             ch = get(cl);\r
1703             start = NULLCHAR;\r
1704             break;\r
1705           } else {\r
1706             goto not_special;\r
1707           }\r
1708 \r
1709         case '\\':\r
1710           if (ad->argType == ArgFilename\r
1711               || ad->argType == ArgSettingsFilename) {\r
1712               goto not_special;\r
1713           }\r
1714           ch = get(cl);\r
1715           switch (ch) {\r
1716           case NULLCHAR:\r
1717             ExitArgError("Incomplete \\ escape in value for", argName);\r
1718             break;\r
1719           case 'n':\r
1720             *q++ = '\n';\r
1721             ch = get(cl);\r
1722             break;\r
1723           case 'r':\r
1724             *q++ = '\r';\r
1725             ch = get(cl);\r
1726             break;\r
1727           case 't':\r
1728             *q++ = '\t';\r
1729             ch = get(cl);\r
1730             break;\r
1731           case 'b':\r
1732             *q++ = '\b';\r
1733             ch = get(cl);\r
1734             break;\r
1735           case 'f':\r
1736             *q++ = '\f';\r
1737             ch = get(cl);\r
1738             break;\r
1739           default:\r
1740             octval = 0;\r
1741             for (i = 0; i < 3; i++) {\r
1742               if (ch >= '0' && ch <= '7') {\r
1743                 octval = octval*8 + (ch - '0');\r
1744                 ch = get(cl);\r
1745               } else {\r
1746                 break;\r
1747               }\r
1748             }\r
1749             if (i > 0) {\r
1750               *q++ = (char) octval;\r
1751             } else {\r
1752               *q++ = ch;\r
1753               ch = get(cl);\r
1754             }\r
1755             break;\r
1756           }\r
1757           break;\r
1758         }\r
1759       }\r
1760     } else {\r
1761       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1762         *q++ = ch;\r
1763         ch = get(cl);\r
1764       }\r
1765     }\r
1766     *q = NULLCHAR;\r
1767 \r
1768     switch (ad->argType) {\r
1769     case ArgInt:\r
1770       *(int *) ad->argLoc = atoi(argValue);\r
1771       break;\r
1772 \r
1773     case ArgX:\r
1774       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1775       break;\r
1776 \r
1777     case ArgY:\r
1778       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1779       break;\r
1780 \r
1781     case ArgZ:\r
1782       *(int *) ad->argLoc = atoi(argValue);\r
1783       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1784       break;\r
1785 \r
1786     case ArgFloat:\r
1787       *(float *) ad->argLoc = (float) atof(argValue);\r
1788       break;\r
1789 \r
1790     case ArgString:\r
1791     case ArgFilename:\r
1792       *(char **) ad->argLoc = strdup(argValue);\r
1793       break;\r
1794 \r
1795     case ArgSettingsFilename:\r
1796       {\r
1797         char fullname[MSG_SIZ];\r
1798         if (ParseSettingsFile(argValue, fullname)) {\r
1799           if (ad->argLoc != NULL) {\r
1800             *(char **) ad->argLoc = strdup(fullname);\r
1801           }\r
1802         } else {\r
1803           if (ad->argLoc != NULL) {\r
1804           } else {\r
1805             ExitArgError("Failed to open indirection file", argValue);\r
1806           }\r
1807         }\r
1808       }\r
1809       break;\r
1810 \r
1811     case ArgBoolean:\r
1812       switch (argValue[0]) {\r
1813       case 't':\r
1814       case 'T':\r
1815         *(Boolean *) ad->argLoc = TRUE;\r
1816         break;\r
1817       case 'f':\r
1818       case 'F':\r
1819         *(Boolean *) ad->argLoc = FALSE;\r
1820         break;\r
1821       default:\r
1822         ExitArgError("Unrecognized boolean argument value", argValue);\r
1823         break;\r
1824       }\r
1825       break;\r
1826 \r
1827     case ArgColor:\r
1828       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1829       break;\r
1830 \r
1831     case ArgAttribs: {\r
1832       ColorClass cc = (ColorClass)ad->argLoc;\r
1833       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1834       }\r
1835       break;\r
1836       \r
1837     case ArgBoardSize:\r
1838       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1839       break;\r
1840 \r
1841     case ArgFont:\r
1842       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1843       break;\r
1844 \r
1845     case ArgCommSettings:\r
1846       ParseCommSettings(argValue, &dcb);\r
1847       break;\r
1848 \r
1849     case ArgNone:\r
1850       ExitArgError("Unrecognized argument", argValue);\r
1851       break;\r
1852     case ArgTrue:\r
1853     case ArgFalse: ;\r
1854     }\r
1855   }\r
1856 }\r
1857 \r
1858 VOID\r
1859 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1860 {\r
1861   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1862   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1863   DeleteDC(hdc);\r
1864   lf->lfWidth = 0;\r
1865   lf->lfEscapement = 0;\r
1866   lf->lfOrientation = 0;\r
1867   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1868   lf->lfItalic = mfp->italic;\r
1869   lf->lfUnderline = mfp->underline;\r
1870   lf->lfStrikeOut = mfp->strikeout;\r
1871   lf->lfCharSet = DEFAULT_CHARSET;\r
1872   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1873   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1874   lf->lfQuality = DEFAULT_QUALITY;\r
1875   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1876   strcpy(lf->lfFaceName, mfp->faceName);\r
1877 }\r
1878 \r
1879 VOID\r
1880 CreateFontInMF(MyFont *mf)\r
1881 {\r
1882   LFfromMFP(&mf->lf, &mf->mfp);\r
1883   if (mf->hf) DeleteObject(mf->hf);\r
1884   mf->hf = CreateFontIndirect(&mf->lf);\r
1885 }\r
1886 \r
1887 VOID\r
1888 SetDefaultTextAttribs()\r
1889 {\r
1890   ColorClass cc;\r
1891   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1892     ParseAttribs(&textAttribs[cc].color, \r
1893                  &textAttribs[cc].effects, \r
1894                  defaultTextAttribs[cc]);\r
1895   }\r
1896 }\r
1897 \r
1898 VOID\r
1899 SetDefaultSounds()\r
1900 {\r
1901   ColorClass cc;\r
1902   SoundClass sc;\r
1903   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1904     textAttribs[cc].sound.name = strdup("");\r
1905     textAttribs[cc].sound.data = NULL;\r
1906   }\r
1907   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1908     sounds[sc].name = strdup("");\r
1909     sounds[sc].data = NULL;\r
1910   }\r
1911   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1912 }\r
1913 \r
1914 VOID\r
1915 LoadAllSounds()\r
1916 {\r
1917   ColorClass cc;\r
1918   SoundClass sc;\r
1919   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1920     MyLoadSound(&textAttribs[cc].sound);\r
1921   }\r
1922   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1923     MyLoadSound(&sounds[sc]);\r
1924   }\r
1925 }\r
1926 \r
1927 VOID\r
1928 InitAppData(LPSTR lpCmdLine)\r
1929 {\r
1930   int i, j;\r
1931   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1932   char *dummy, *p;\r
1933 \r
1934   programName = szAppName;\r
1935 \r
1936   /* Initialize to defaults */\r
1937   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1938   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1939   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1940   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1941   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1942   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1943   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1944   SetDefaultTextAttribs();\r
1945   SetDefaultSounds();\r
1946   appData.movesPerSession = MOVES_PER_SESSION;\r
1947   appData.initString = INIT_STRING;\r
1948   appData.secondInitString = INIT_STRING;\r
1949   appData.firstComputerString = COMPUTER_STRING;\r
1950   appData.secondComputerString = COMPUTER_STRING;\r
1951   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1952   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1953   appData.firstPlaysBlack = FALSE;\r
1954   appData.noChessProgram = FALSE;\r
1955   chessProgram = FALSE;\r
1956   appData.firstHost = FIRST_HOST;\r
1957   appData.secondHost = SECOND_HOST;\r
1958   appData.firstDirectory = FIRST_DIRECTORY;\r
1959   appData.secondDirectory = SECOND_DIRECTORY;\r
1960   appData.bitmapDirectory = "";\r
1961   appData.remoteShell = REMOTE_SHELL;\r
1962   appData.remoteUser = "";\r
1963   appData.timeDelay = TIME_DELAY;\r
1964   appData.timeControl = TIME_CONTROL;\r
1965   appData.timeIncrement = TIME_INCREMENT;\r
1966   appData.icsActive = FALSE;\r
1967   appData.icsHost = "";\r
1968   appData.icsPort = ICS_PORT;\r
1969   appData.icsCommPort = ICS_COMM_PORT;\r
1970   appData.icsLogon = ICS_LOGON;\r
1971   appData.icsHelper = "";\r
1972   appData.useTelnet = FALSE;\r
1973   appData.telnetProgram = TELNET_PROGRAM;\r
1974   appData.gateway = "";\r
1975   appData.loadGameFile = "";\r
1976   appData.loadGameIndex = 0;\r
1977   appData.saveGameFile = "";\r
1978   appData.autoSaveGames = FALSE;\r
1979   appData.loadPositionFile = "";\r
1980   appData.loadPositionIndex = 1;\r
1981   appData.savePositionFile = "";\r
1982   appData.matchMode = FALSE;\r
1983   appData.matchGames = 0;\r
1984   appData.monoMode = FALSE;\r
1985   appData.debugMode = FALSE;\r
1986   appData.clockMode = TRUE;\r
1987   boardSize = (BoardSize) -1; /* determine by screen size */\r
1988   appData.Iconic = FALSE; /*unused*/\r
1989   appData.searchTime = "";\r
1990   appData.searchDepth = 0;\r
1991   appData.showCoords = FALSE;\r
1992   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1993   appData.autoCallFlag = FALSE;\r
1994   appData.flipView = FALSE;\r
1995   appData.autoFlipView = TRUE;\r
1996   appData.cmailGameName = "";\r
1997   appData.alwaysPromoteToQueen = FALSE;\r
1998   appData.oldSaveStyle = FALSE;\r
1999   appData.quietPlay = FALSE;\r
2000   appData.showThinking = FALSE;\r
2001   appData.ponderNextMove = TRUE;\r
2002   appData.periodicUpdates = TRUE;\r
2003   appData.popupExitMessage = TRUE;\r
2004   appData.popupMoveErrors = FALSE;\r
2005   appData.autoObserve = FALSE;\r
2006   appData.autoComment = FALSE;\r
2007   appData.animate = TRUE;\r
2008   appData.animSpeed = 10;\r
2009   appData.animateDragging = TRUE;\r
2010   appData.highlightLastMove = TRUE;\r
2011   appData.getMoveList = TRUE;\r
2012   appData.testLegality = TRUE;\r
2013   appData.premove = TRUE;\r
2014   appData.premoveWhite = FALSE;\r
2015   appData.premoveWhiteText = "";\r
2016   appData.premoveBlack = FALSE;\r
2017   appData.premoveBlackText = "";\r
2018   appData.icsAlarm = TRUE;\r
2019   appData.icsAlarmTime = 5000;\r
2020   appData.autoRaiseBoard = TRUE;\r
2021   appData.localLineEditing = TRUE;\r
2022   appData.colorize = TRUE;\r
2023   appData.reuseFirst = TRUE;\r
2024   appData.reuseSecond = TRUE;\r
2025   appData.blindfold = FALSE;\r
2026   appData.icsEngineAnalyze = FALSE;\r
2027   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2028   dcb.DCBlength = sizeof(DCB);\r
2029   dcb.BaudRate = 9600;\r
2030   dcb.fBinary = TRUE;\r
2031   dcb.fParity = FALSE;\r
2032   dcb.fOutxCtsFlow = FALSE;\r
2033   dcb.fOutxDsrFlow = FALSE;\r
2034   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2035   dcb.fDsrSensitivity = FALSE;\r
2036   dcb.fTXContinueOnXoff = TRUE;\r
2037   dcb.fOutX = FALSE;\r
2038   dcb.fInX = FALSE;\r
2039   dcb.fNull = FALSE;\r
2040   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2041   dcb.fAbortOnError = FALSE;\r
2042   dcb.ByteSize = 7;\r
2043   dcb.Parity = SPACEPARITY;\r
2044   dcb.StopBits = ONESTOPBIT;\r
2045   settingsFileName = SETTINGS_FILE;\r
2046   saveSettingsOnExit = TRUE;\r
2047   boardX = CW_USEDEFAULT;\r
2048   boardY = CW_USEDEFAULT;\r
2049   analysisX = CW_USEDEFAULT; \r
2050   analysisY = CW_USEDEFAULT; \r
2051   analysisW = CW_USEDEFAULT;\r
2052   analysisH = CW_USEDEFAULT;\r
2053   commentX = CW_USEDEFAULT; \r
2054   commentY = CW_USEDEFAULT; \r
2055   commentW = CW_USEDEFAULT;\r
2056   commentH = CW_USEDEFAULT;\r
2057   editTagsX = CW_USEDEFAULT; \r
2058   editTagsY = CW_USEDEFAULT; \r
2059   editTagsW = CW_USEDEFAULT;\r
2060   editTagsH = CW_USEDEFAULT;\r
2061   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2062   icsNames = ICS_NAMES;\r
2063   firstChessProgramNames = FCP_NAMES;\r
2064   secondChessProgramNames = SCP_NAMES;\r
2065   appData.initialMode = "";\r
2066   appData.variant = "normal";\r
2067   appData.firstProtocolVersion = PROTOVER;\r
2068   appData.secondProtocolVersion = PROTOVER;\r
2069   appData.showButtonBar = TRUE;\r
2070 \r
2071    /* [AS] New properties (see comments in header file) */\r
2072   appData.firstScoreIsAbsolute = FALSE;\r
2073   appData.secondScoreIsAbsolute = FALSE;\r
2074   appData.saveExtendedInfoInPGN = FALSE;\r
2075   appData.hideThinkingFromHuman = FALSE;\r
2076   appData.liteBackTextureFile = "";\r
2077   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2078   appData.darkBackTextureFile = "";\r
2079   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2080   appData.renderPiecesWithFont = "";\r
2081   appData.fontToPieceTable = "";\r
2082   appData.fontBackColorWhite = 0;\r
2083   appData.fontForeColorWhite = 0;\r
2084   appData.fontBackColorBlack = 0;\r
2085   appData.fontForeColorBlack = 0;\r
2086   appData.fontPieceSize = 80;\r
2087   appData.overrideLineGap = 1;\r
2088   appData.adjudicateLossThreshold = 0;\r
2089   appData.delayBeforeQuit = 0;\r
2090   appData.delayAfterQuit = 0;\r
2091   appData.nameOfDebugFile = "winboard.debug";\r
2092   appData.pgnEventHeader = "Computer Chess Game";\r
2093   appData.defaultFrcPosition = -1;\r
2094   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2095   appData.saveOutOfBookInfo = TRUE;\r
2096   appData.showEvalInMoveHistory = TRUE;\r
2097   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2098   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2099   appData.highlightMoveWithArrow = FALSE;\r
2100   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2101   appData.useStickyWindows = TRUE;\r
2102   appData.adjudicateDrawMoves = 0;\r
2103   appData.autoDisplayComment = TRUE;\r
2104   appData.autoDisplayTags = TRUE;\r
2105   appData.firstIsUCI = FALSE;\r
2106   appData.secondIsUCI = FALSE;\r
2107   appData.firstHasOwnBookUCI = TRUE;\r
2108   appData.secondHasOwnBookUCI = TRUE;\r
2109   appData.polyglotDir = "";\r
2110   appData.usePolyglotBook = FALSE;\r
2111   appData.polyglotBook = "";\r
2112   appData.defaultHashSize = 64;\r
2113   appData.defaultCacheSizeEGTB = 4;\r
2114   appData.defaultPathEGTB = "c:\\egtb";\r
2115   appData.firstOptions = "";\r
2116   appData.secondOptions = "";\r
2117 \r
2118   InitWindowPlacement( &wpGameList );\r
2119   InitWindowPlacement( &wpMoveHistory );\r
2120   InitWindowPlacement( &wpEvalGraph );\r
2121   InitWindowPlacement( &wpEngineOutput );\r
2122   InitWindowPlacement( &wpConsole );\r
2123 \r
2124   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2125   appData.NrFiles      = -1;\r
2126   appData.NrRanks      = -1;\r
2127   appData.holdingsSize = -1;\r
2128   appData.testClaims   = FALSE;\r
2129   appData.checkMates   = FALSE;\r
2130   appData.materialDraws= FALSE;\r
2131   appData.trivialDraws = FALSE;\r
2132   appData.ruleMoves    = 51;\r
2133   appData.drawRepeats  = 6;\r
2134   appData.matchPause   = 10000;\r
2135   appData.alphaRank    = FALSE;\r
2136   appData.allWhite     = FALSE;\r
2137   appData.upsideDown   = FALSE;\r
2138   appData.serverPause  = 15;\r
2139   appData.serverMovesName   = NULL;\r
2140   appData.suppressLoadMoves = FALSE;\r
2141   appData.firstTimeOdds  = 1;\r
2142   appData.secondTimeOdds = 1;\r
2143   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2144   appData.secondAccumulateTC = 1;\r
2145   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2146   appData.secondNPS = -1;\r
2147   appData.engineComments = 1;\r
2148   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2149   appData.egtFormats = "";\r
2150 \r
2151 #ifdef ZIPPY\r
2152   appData.zippyTalk = ZIPPY_TALK;\r
2153   appData.zippyPlay = ZIPPY_PLAY;\r
2154   appData.zippyLines = ZIPPY_LINES;\r
2155   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2156   appData.zippyPassword = ZIPPY_PASSWORD;\r
2157   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2158   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2159   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2160   appData.zippyUseI = ZIPPY_USE_I;\r
2161   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2162   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2163   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2164   appData.zippyGameStart = ZIPPY_GAME_START;\r
2165   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2166   appData.zippyAbort = ZIPPY_ABORT;\r
2167   appData.zippyVariants = ZIPPY_VARIANTS;\r
2168   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2169   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2170 #endif\r
2171 \r
2172   /* Point font array elements to structures and\r
2173      parse default font names */\r
2174   for (i=0; i<NUM_FONTS; i++) {\r
2175     for (j=0; j<NUM_SIZES; j++) {\r
2176       font[j][i] = &fontRec[j][i];\r
2177       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2178     }\r
2179   }\r
2180   \r
2181   /* Parse default settings file if any */\r
2182   if (ParseSettingsFile(settingsFileName, buf)) {\r
2183     settingsFileName = strdup(buf);\r
2184   }\r
2185 \r
2186   /* Parse command line */\r
2187   ParseArgs(StringGet, &lpCmdLine);\r
2188 \r
2189   /* [HGM] make sure board size is acceptable */\r
2190   if(appData.NrFiles > BOARD_SIZE ||\r
2191      appData.NrRanks > BOARD_SIZE   )\r
2192       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2193 \r
2194   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2195    * with options from the command line, we now make an even higher priority\r
2196    * overrule by WB options attached to the engine command line. This so that\r
2197    * tournament managers can use WB options (such as /timeOdds) that follow\r
2198    * the engines.\r
2199    */\r
2200   if(appData.firstChessProgram != NULL) {\r
2201       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2202       static char *f = "first";\r
2203       char buf[MSG_SIZ], *q = buf;\r
2204       if(p != NULL) { // engine command line contains WinBoard options\r
2205           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2206           ParseArgs(StringGet, &q);\r
2207           p[-1] = 0; // cut them offengine command line\r
2208       }\r
2209   }\r
2210   // now do same for second chess program\r
2211   if(appData.secondChessProgram != NULL) {\r
2212       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2213       static char *s = "second";\r
2214       char buf[MSG_SIZ], *q = buf;\r
2215       if(p != NULL) { // engine command line contains WinBoard options\r
2216           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2217           ParseArgs(StringGet, &q);\r
2218           p[-1] = 0; // cut them offengine command line\r
2219       }\r
2220   }\r
2221 \r
2222 \r
2223   /* Propagate options that affect others */\r
2224   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2225   if (appData.icsActive || appData.noChessProgram) {\r
2226      chessProgram = FALSE;  /* not local chess program mode */\r
2227   }\r
2228 \r
2229   /* Open startup dialog if needed */\r
2230   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2231       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2232       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2233                         *appData.secondChessProgram == NULLCHAR))) {\r
2234     FARPROC lpProc;\r
2235     \r
2236     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2237     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2238     FreeProcInstance(lpProc);\r
2239   }\r
2240 \r
2241   /* Make sure save files land in the right (?) directory */\r
2242   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2243     appData.saveGameFile = strdup(buf);\r
2244   }\r
2245   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2246     appData.savePositionFile = strdup(buf);\r
2247   }\r
2248 \r
2249   /* Finish initialization for fonts and sounds */\r
2250   for (i=0; i<NUM_FONTS; i++) {\r
2251     for (j=0; j<NUM_SIZES; j++) {\r
2252       CreateFontInMF(font[j][i]);\r
2253     }\r
2254   }\r
2255   /* xboard, and older WinBoards, controlled the move sound with the\r
2256      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2257      always turn the option on (so that the backend will call us),\r
2258      then let the user turn the sound off by setting it to silence if\r
2259      desired.  To accommodate old winboard.ini files saved by old\r
2260      versions of WinBoard, we also turn off the sound if the option\r
2261      was initially set to false. */\r
2262   if (!appData.ringBellAfterMoves) {\r
2263     sounds[(int)SoundMove].name = strdup("");\r
2264     appData.ringBellAfterMoves = TRUE;\r
2265   }\r
2266   GetCurrentDirectory(MSG_SIZ, currDir);\r
2267   SetCurrentDirectory(installDir);\r
2268   LoadAllSounds();\r
2269   SetCurrentDirectory(currDir);\r
2270 \r
2271   p = icsTextMenuString;\r
2272   if (p[0] == '@') {\r
2273     FILE* f = fopen(p + 1, "r");\r
2274     if (f == NULL) {\r
2275       DisplayFatalError(p + 1, errno, 2);\r
2276       return;\r
2277     }\r
2278     i = fread(buf, 1, sizeof(buf)-1, f);\r
2279     fclose(f);\r
2280     buf[i] = NULLCHAR;\r
2281     p = buf;\r
2282   }\r
2283   ParseIcsTextMenu(strdup(p));\r
2284 }\r
2285 \r
2286 \r
2287 VOID\r
2288 InitMenuChecks()\r
2289 {\r
2290   HMENU hmenu = GetMenu(hwndMain);\r
2291 \r
2292   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2293                         MF_BYCOMMAND|((appData.icsActive &&\r
2294                                        *appData.icsCommPort != NULLCHAR) ?\r
2295                                       MF_ENABLED : MF_GRAYED));\r
2296   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2297                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2298                                      MF_CHECKED : MF_UNCHECKED));\r
2299 }\r
2300 \r
2301 \r
2302 VOID\r
2303 SaveSettings(char* name)\r
2304 {\r
2305   FILE *f;\r
2306   ArgDescriptor *ad;\r
2307   WINDOWPLACEMENT wp;\r
2308   char dir[MSG_SIZ];\r
2309 \r
2310   if (!hwndMain) return;\r
2311 \r
2312   GetCurrentDirectory(MSG_SIZ, dir);\r
2313   SetCurrentDirectory(installDir);\r
2314   f = fopen(name, "w");\r
2315   SetCurrentDirectory(dir);\r
2316   if (f == NULL) {\r
2317     DisplayError(name, errno);\r
2318     return;\r
2319   }\r
2320   fprintf(f, ";\n");\r
2321   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2322   fprintf(f, ";\n");\r
2323   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2324   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2325   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2326   fprintf(f, ";\n");\r
2327 \r
2328   wp.length = sizeof(WINDOWPLACEMENT);\r
2329   GetWindowPlacement(hwndMain, &wp);\r
2330   boardX = wp.rcNormalPosition.left;\r
2331   boardY = wp.rcNormalPosition.top;\r
2332 \r
2333   if (hwndConsole) {\r
2334     GetWindowPlacement(hwndConsole, &wp);\r
2335     wpConsole.x = wp.rcNormalPosition.left;\r
2336     wpConsole.y = wp.rcNormalPosition.top;\r
2337     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2338     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2339   }\r
2340 \r
2341   if (analysisDialog) {\r
2342     GetWindowPlacement(analysisDialog, &wp);\r
2343     analysisX = wp.rcNormalPosition.left;\r
2344     analysisY = wp.rcNormalPosition.top;\r
2345     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2346     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2347   }\r
2348 \r
2349   if (commentDialog) {\r
2350     GetWindowPlacement(commentDialog, &wp);\r
2351     commentX = wp.rcNormalPosition.left;\r
2352     commentY = wp.rcNormalPosition.top;\r
2353     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2354     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2355   }\r
2356 \r
2357   if (editTagsDialog) {\r
2358     GetWindowPlacement(editTagsDialog, &wp);\r
2359     editTagsX = wp.rcNormalPosition.left;\r
2360     editTagsY = wp.rcNormalPosition.top;\r
2361     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2362     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2363   }\r
2364 \r
2365   if (gameListDialog) {\r
2366     GetWindowPlacement(gameListDialog, &wp);\r
2367     wpGameList.x = wp.rcNormalPosition.left;\r
2368     wpGameList.y = wp.rcNormalPosition.top;\r
2369     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2370     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2371   }\r
2372 \r
2373   /* [AS] Move history */\r
2374   wpMoveHistory.visible = MoveHistoryIsUp();\r
2375   \r
2376   if( moveHistoryDialog ) {\r
2377     GetWindowPlacement(moveHistoryDialog, &wp);\r
2378     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2379     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2380     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2381     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2382   }\r
2383 \r
2384   /* [AS] Eval graph */\r
2385   wpEvalGraph.visible = EvalGraphIsUp();\r
2386 \r
2387   if( evalGraphDialog ) {\r
2388     GetWindowPlacement(evalGraphDialog, &wp);\r
2389     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2390     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2391     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2392     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2393   }\r
2394 \r
2395   /* [AS] Engine output */\r
2396   wpEngineOutput.visible = EngineOutputIsUp();\r
2397 \r
2398   if( engineOutputDialog ) {\r
2399     GetWindowPlacement(engineOutputDialog, &wp);\r
2400     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2401     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2402     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2403     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2404   }\r
2405 \r
2406   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2407     if (!ad->save) continue;\r
2408     switch (ad->argType) {\r
2409     case ArgString:\r
2410       {\r
2411         char *p = *(char **)ad->argLoc;\r
2412         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2413           /* Quote multiline values or \-containing values\r
2414              with { } if possible */\r
2415           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2416         } else {\r
2417           /* Else quote with " " */\r
2418           fprintf(f, "/%s=\"", ad->argName);\r
2419           while (*p) {\r
2420             if (*p == '\n') fprintf(f, "\n");\r
2421             else if (*p == '\r') fprintf(f, "\\r");\r
2422             else if (*p == '\t') fprintf(f, "\\t");\r
2423             else if (*p == '\b') fprintf(f, "\\b");\r
2424             else if (*p == '\f') fprintf(f, "\\f");\r
2425             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2426             else if (*p == '\"') fprintf(f, "\\\"");\r
2427             else if (*p == '\\') fprintf(f, "\\\\");\r
2428             else putc(*p, f);\r
2429             p++;\r
2430           }\r
2431           fprintf(f, "\"\n");\r
2432         }\r
2433       }\r
2434       break;\r
2435     case ArgInt:\r
2436     case ArgZ:\r
2437       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2438       break;\r
2439     case ArgX:\r
2440       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2441       break;\r
2442     case ArgY:\r
2443       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2444       break;\r
2445     case ArgFloat:\r
2446       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2447       break;\r
2448     case ArgBoolean:\r
2449       fprintf(f, "/%s=%s\n", ad->argName, \r
2450         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2451       break;\r
2452     case ArgTrue:\r
2453       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2454       break;\r
2455     case ArgFalse:\r
2456       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2457       break;\r
2458     case ArgColor:\r
2459       {\r
2460         COLORREF color = *(COLORREF *)ad->argLoc;\r
2461         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2462           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2463       }\r
2464       break;\r
2465     case ArgAttribs:\r
2466       {\r
2467         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2468         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2469           (ta->effects & CFE_BOLD) ? "b" : "",\r
2470           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2471           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2472           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2473           (ta->effects) ? " " : "",\r
2474           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2475       }\r
2476       break;\r
2477     case ArgFilename:\r
2478       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2479         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2480       } else {\r
2481         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2482       }\r
2483       break;\r
2484     case ArgBoardSize:\r
2485       fprintf(f, "/%s=%s\n", ad->argName,\r
2486               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2487       break;\r
2488     case ArgFont:\r
2489       {\r
2490         int bs;\r
2491         for (bs=0; bs<NUM_SIZES; bs++) {\r
2492           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2493           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2494           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2495             ad->argName, mfp->faceName, mfp->pointSize,\r
2496             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2497             mfp->bold ? "b" : "",\r
2498             mfp->italic ? "i" : "",\r
2499             mfp->underline ? "u" : "",\r
2500             mfp->strikeout ? "s" : "");\r
2501         }\r
2502       }\r
2503       break;\r
2504     case ArgCommSettings:\r
2505       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2506     case ArgNone:\r
2507     case ArgSettingsFilename: ;\r
2508     }\r
2509   }\r
2510   fclose(f);\r
2511 }\r
2512 \r
2513 \r
2514 \r
2515 /*---------------------------------------------------------------------------*\\r
2516  *\r
2517  * GDI board drawing routines\r
2518  *\r
2519 \*---------------------------------------------------------------------------*/\r
2520 \r
2521 /* [AS] Draw square using background texture */\r
2522 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2523 {\r
2524     XFORM   x;\r
2525 \r
2526     if( mode == 0 ) {\r
2527         return; /* Should never happen! */\r
2528     }\r
2529 \r
2530     SetGraphicsMode( dst, GM_ADVANCED );\r
2531 \r
2532     switch( mode ) {\r
2533     case 1:\r
2534         /* Identity */\r
2535         break;\r
2536     case 2:\r
2537         /* X reflection */\r
2538         x.eM11 = -1.0;\r
2539         x.eM12 = 0;\r
2540         x.eM21 = 0;\r
2541         x.eM22 = 1.0;\r
2542         x.eDx = (FLOAT) dw + dx - 1;\r
2543         x.eDy = 0;\r
2544         dx = 0;\r
2545         SetWorldTransform( dst, &x );\r
2546         break;\r
2547     case 3:\r
2548         /* Y reflection */\r
2549         x.eM11 = 1.0;\r
2550         x.eM12 = 0;\r
2551         x.eM21 = 0;\r
2552         x.eM22 = -1.0;\r
2553         x.eDx = 0;\r
2554         x.eDy = (FLOAT) dh + dy - 1;\r
2555         dy = 0;\r
2556         SetWorldTransform( dst, &x );\r
2557         break;\r
2558     case 4:\r
2559         /* X/Y flip */\r
2560         x.eM11 = 0;\r
2561         x.eM12 = 1.0;\r
2562         x.eM21 = 1.0;\r
2563         x.eM22 = 0;\r
2564         x.eDx = (FLOAT) dx;\r
2565         x.eDy = (FLOAT) dy;\r
2566         dx = 0;\r
2567         dy = 0;\r
2568         SetWorldTransform( dst, &x );\r
2569         break;\r
2570     }\r
2571 \r
2572     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2573 \r
2574     x.eM11 = 1.0;\r
2575     x.eM12 = 0;\r
2576     x.eM21 = 0;\r
2577     x.eM22 = 1.0;\r
2578     x.eDx = 0;\r
2579     x.eDy = 0;\r
2580     SetWorldTransform( dst, &x );\r
2581 \r
2582     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2583 }\r
2584 \r
2585 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2586 enum {\r
2587     PM_WP = (int) WhitePawn, \r
2588     PM_WN = (int) WhiteKnight, \r
2589     PM_WB = (int) WhiteBishop, \r
2590     PM_WR = (int) WhiteRook, \r
2591     PM_WQ = (int) WhiteQueen, \r
2592     PM_WF = (int) WhiteFerz, \r
2593     PM_WW = (int) WhiteWazir, \r
2594     PM_WE = (int) WhiteAlfil, \r
2595     PM_WM = (int) WhiteMan, \r
2596     PM_WO = (int) WhiteCannon, \r
2597     PM_WU = (int) WhiteUnicorn, \r
2598     PM_WH = (int) WhiteNightrider, \r
2599     PM_WA = (int) WhiteAngel, \r
2600     PM_WC = (int) WhiteMarshall, \r
2601     PM_WAB = (int) WhiteCardinal, \r
2602     PM_WD = (int) WhiteDragon, \r
2603     PM_WL = (int) WhiteLance, \r
2604     PM_WS = (int) WhiteCobra, \r
2605     PM_WV = (int) WhiteFalcon, \r
2606     PM_WSG = (int) WhiteSilver, \r
2607     PM_WG = (int) WhiteGrasshopper, \r
2608     PM_WK = (int) WhiteKing,\r
2609     PM_BP = (int) BlackPawn, \r
2610     PM_BN = (int) BlackKnight, \r
2611     PM_BB = (int) BlackBishop, \r
2612     PM_BR = (int) BlackRook, \r
2613     PM_BQ = (int) BlackQueen, \r
2614     PM_BF = (int) BlackFerz, \r
2615     PM_BW = (int) BlackWazir, \r
2616     PM_BE = (int) BlackAlfil, \r
2617     PM_BM = (int) BlackMan,\r
2618     PM_BO = (int) BlackCannon, \r
2619     PM_BU = (int) BlackUnicorn, \r
2620     PM_BH = (int) BlackNightrider, \r
2621     PM_BA = (int) BlackAngel, \r
2622     PM_BC = (int) BlackMarshall, \r
2623     PM_BG = (int) BlackGrasshopper, \r
2624     PM_BAB = (int) BlackCardinal,\r
2625     PM_BD = (int) BlackDragon,\r
2626     PM_BL = (int) BlackLance,\r
2627     PM_BS = (int) BlackCobra,\r
2628     PM_BV = (int) BlackFalcon,\r
2629     PM_BSG = (int) BlackSilver,\r
2630     PM_BK = (int) BlackKing\r
2631 };\r
2632 \r
2633 static HFONT hPieceFont = NULL;\r
2634 static HBITMAP hPieceMask[(int) EmptySquare];\r
2635 static HBITMAP hPieceFace[(int) EmptySquare];\r
2636 static int fontBitmapSquareSize = 0;\r
2637 static char pieceToFontChar[(int) EmptySquare] =\r
2638                               { 'p', 'n', 'b', 'r', 'q', \r
2639                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2640                       'k', 'o', 'm', 'v', 't', 'w', \r
2641                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2642                                                               'l' };\r
2643 \r
2644 extern BOOL SetCharTable( char *table, const char * map );\r
2645 /* [HGM] moved to backend.c */\r
2646 \r
2647 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2648 {\r
2649     HBRUSH hbrush;\r
2650     BYTE r1 = GetRValue( color );\r
2651     BYTE g1 = GetGValue( color );\r
2652     BYTE b1 = GetBValue( color );\r
2653     BYTE r2 = r1 / 2;\r
2654     BYTE g2 = g1 / 2;\r
2655     BYTE b2 = b1 / 2;\r
2656     RECT rc;\r
2657 \r
2658     /* Create a uniform background first */\r
2659     hbrush = CreateSolidBrush( color );\r
2660     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2661     FillRect( hdc, &rc, hbrush );\r
2662     DeleteObject( hbrush );\r
2663     \r
2664     if( mode == 1 ) {\r
2665         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2666         int steps = squareSize / 2;\r
2667         int i;\r
2668 \r
2669         for( i=0; i<steps; i++ ) {\r
2670             BYTE r = r1 - (r1-r2) * i / steps;\r
2671             BYTE g = g1 - (g1-g2) * i / steps;\r
2672             BYTE b = b1 - (b1-b2) * i / steps;\r
2673 \r
2674             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2675             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2676             FillRect( hdc, &rc, hbrush );\r
2677             DeleteObject(hbrush);\r
2678         }\r
2679     }\r
2680     else if( mode == 2 ) {\r
2681         /* Diagonal gradient, good more or less for every piece */\r
2682         POINT triangle[3];\r
2683         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2684         HBRUSH hbrush_old;\r
2685         int steps = squareSize;\r
2686         int i;\r
2687 \r
2688         triangle[0].x = squareSize - steps;\r
2689         triangle[0].y = squareSize;\r
2690         triangle[1].x = squareSize;\r
2691         triangle[1].y = squareSize;\r
2692         triangle[2].x = squareSize;\r
2693         triangle[2].y = squareSize - steps;\r
2694 \r
2695         for( i=0; i<steps; i++ ) {\r
2696             BYTE r = r1 - (r1-r2) * i / steps;\r
2697             BYTE g = g1 - (g1-g2) * i / steps;\r
2698             BYTE b = b1 - (b1-b2) * i / steps;\r
2699 \r
2700             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2701             hbrush_old = SelectObject( hdc, hbrush );\r
2702             Polygon( hdc, triangle, 3 );\r
2703             SelectObject( hdc, hbrush_old );\r
2704             DeleteObject(hbrush);\r
2705             triangle[0].x++;\r
2706             triangle[2].y++;\r
2707         }\r
2708 \r
2709         SelectObject( hdc, hpen );\r
2710     }\r
2711 }\r
2712 \r
2713 /*\r
2714     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2715     seems to work ok. The main problem here is to find the "inside" of a chess\r
2716     piece: follow the steps as explained below.\r
2717 */\r
2718 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2719 {\r
2720     HBITMAP hbm;\r
2721     HBITMAP hbm_old;\r
2722     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2723     RECT rc;\r
2724     SIZE sz;\r
2725     POINT pt;\r
2726     int backColor = whitePieceColor; \r
2727     int foreColor = blackPieceColor;\r
2728     \r
2729     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2730         backColor = appData.fontBackColorWhite;\r
2731         foreColor = appData.fontForeColorWhite;\r
2732     }\r
2733     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2734         backColor = appData.fontBackColorBlack;\r
2735         foreColor = appData.fontForeColorBlack;\r
2736     }\r
2737 \r
2738     /* Mask */\r
2739     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2740 \r
2741     hbm_old = SelectObject( hdc, hbm );\r
2742 \r
2743     rc.left = 0;\r
2744     rc.top = 0;\r
2745     rc.right = squareSize;\r
2746     rc.bottom = squareSize;\r
2747 \r
2748     /* Step 1: background is now black */\r
2749     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2750 \r
2751     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2752 \r
2753     pt.x = (squareSize - sz.cx) / 2;\r
2754     pt.y = (squareSize - sz.cy) / 2;\r
2755 \r
2756     SetBkMode( hdc, TRANSPARENT );\r
2757     SetTextColor( hdc, chroma );\r
2758     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2759     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2760 \r
2761     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2762     /* Step 3: the area outside the piece is filled with white */\r
2763 //    FloodFill( hdc, 0, 0, chroma );\r
2764     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2765     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2766     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2767     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2768     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2769     /* \r
2770         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2771         but if the start point is not inside the piece we're lost!\r
2772         There should be a better way to do this... if we could create a region or path\r
2773         from the fill operation we would be fine for example.\r
2774     */\r
2775 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2776     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2777 \r
2778     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2779         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2780         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2781 \r
2782         SelectObject( dc2, bm2 );\r
2783         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2784         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2785         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2786         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2787         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2788 \r
2789         DeleteDC( dc2 );\r
2790         DeleteObject( bm2 );\r
2791     }\r
2792 \r
2793     SetTextColor( hdc, 0 );\r
2794     /* \r
2795         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2796         draw the piece again in black for safety.\r
2797     */\r
2798     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2799 \r
2800     SelectObject( hdc, hbm_old );\r
2801 \r
2802     if( hPieceMask[index] != NULL ) {\r
2803         DeleteObject( hPieceMask[index] );\r
2804     }\r
2805 \r
2806     hPieceMask[index] = hbm;\r
2807 \r
2808     /* Face */\r
2809     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2810 \r
2811     SelectObject( hdc, hbm );\r
2812 \r
2813     {\r
2814         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2815         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2816         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2817 \r
2818         SelectObject( dc1, hPieceMask[index] );\r
2819         SelectObject( dc2, bm2 );\r
2820         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2821         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2822         \r
2823         /* \r
2824             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2825             the piece background and deletes (makes transparent) the rest.\r
2826             Thanks to that mask, we are free to paint the background with the greates\r
2827             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2828             We use this, to make gradients and give the pieces a "roundish" look.\r
2829         */\r
2830         SetPieceBackground( hdc, backColor, 2 );\r
2831         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2832 \r
2833         DeleteDC( dc2 );\r
2834         DeleteDC( dc1 );\r
2835         DeleteObject( bm2 );\r
2836     }\r
2837 \r
2838     SetTextColor( hdc, foreColor );\r
2839     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2840 \r
2841     SelectObject( hdc, hbm_old );\r
2842 \r
2843     if( hPieceFace[index] != NULL ) {\r
2844         DeleteObject( hPieceFace[index] );\r
2845     }\r
2846 \r
2847     hPieceFace[index] = hbm;\r
2848 }\r
2849 \r
2850 static int TranslatePieceToFontPiece( int piece )\r
2851 {\r
2852     switch( piece ) {\r
2853     case BlackPawn:\r
2854         return PM_BP;\r
2855     case BlackKnight:\r
2856         return PM_BN;\r
2857     case BlackBishop:\r
2858         return PM_BB;\r
2859     case BlackRook:\r
2860         return PM_BR;\r
2861     case BlackQueen:\r
2862         return PM_BQ;\r
2863     case BlackKing:\r
2864         return PM_BK;\r
2865     case WhitePawn:\r
2866         return PM_WP;\r
2867     case WhiteKnight:\r
2868         return PM_WN;\r
2869     case WhiteBishop:\r
2870         return PM_WB;\r
2871     case WhiteRook:\r
2872         return PM_WR;\r
2873     case WhiteQueen:\r
2874         return PM_WQ;\r
2875     case WhiteKing:\r
2876         return PM_WK;\r
2877 \r
2878     case BlackAngel:\r
2879         return PM_BA;\r
2880     case BlackMarshall:\r
2881         return PM_BC;\r
2882     case BlackFerz:\r
2883         return PM_BF;\r
2884     case BlackNightrider:\r
2885         return PM_BH;\r
2886     case BlackAlfil:\r
2887         return PM_BE;\r
2888     case BlackWazir:\r
2889         return PM_BW;\r
2890     case BlackUnicorn:\r
2891         return PM_BU;\r
2892     case BlackCannon:\r
2893         return PM_BO;\r
2894     case BlackGrasshopper:\r
2895         return PM_BG;\r
2896     case BlackMan:\r
2897         return PM_BM;\r
2898     case BlackSilver:\r
2899         return PM_BSG;\r
2900     case BlackLance:\r
2901         return PM_BL;\r
2902     case BlackFalcon:\r
2903         return PM_BV;\r
2904     case BlackCobra:\r
2905         return PM_BS;\r
2906     case BlackCardinal:\r
2907         return PM_BAB;\r
2908     case BlackDragon:\r
2909         return PM_BD;\r
2910 \r
2911     case WhiteAngel:\r
2912         return PM_WA;\r
2913     case WhiteMarshall:\r
2914         return PM_WC;\r
2915     case WhiteFerz:\r
2916         return PM_WF;\r
2917     case WhiteNightrider:\r
2918         return PM_WH;\r
2919     case WhiteAlfil:\r
2920         return PM_WE;\r
2921     case WhiteWazir:\r
2922         return PM_WW;\r
2923     case WhiteUnicorn:\r
2924         return PM_WU;\r
2925     case WhiteCannon:\r
2926         return PM_WO;\r
2927     case WhiteGrasshopper:\r
2928         return PM_WG;\r
2929     case WhiteMan:\r
2930         return PM_WM;\r
2931     case WhiteSilver:\r
2932         return PM_WSG;\r
2933     case WhiteLance:\r
2934         return PM_WL;\r
2935     case WhiteFalcon:\r
2936         return PM_WV;\r
2937     case WhiteCobra:\r
2938         return PM_WS;\r
2939     case WhiteCardinal:\r
2940         return PM_WAB;\r
2941     case WhiteDragon:\r
2942         return PM_WD;\r
2943     }\r
2944 \r
2945     return 0;\r
2946 }\r
2947 \r
2948 void CreatePiecesFromFont()\r
2949 {\r
2950     LOGFONT lf;\r
2951     HDC hdc_window = NULL;\r
2952     HDC hdc = NULL;\r
2953     HFONT hfont_old;\r
2954     int fontHeight;\r
2955     int i;\r
2956 \r
2957     if( fontBitmapSquareSize < 0 ) {\r
2958         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2959         return;\r
2960     }\r
2961 \r
2962     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2963         fontBitmapSquareSize = -1;\r
2964         return;\r
2965     }\r
2966 \r
2967     if( fontBitmapSquareSize != squareSize ) {\r
2968         hdc_window = GetDC( hwndMain );\r
2969         hdc = CreateCompatibleDC( hdc_window );\r
2970 \r
2971         if( hPieceFont != NULL ) {\r
2972             DeleteObject( hPieceFont );\r
2973         }\r
2974         else {\r
2975             for( i=0; i<=(int)BlackKing; i++ ) {\r
2976                 hPieceMask[i] = NULL;\r
2977                 hPieceFace[i] = NULL;\r
2978             }\r
2979         }\r
2980 \r
2981         fontHeight = 75;\r
2982 \r
2983         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2984             fontHeight = appData.fontPieceSize;\r
2985         }\r
2986 \r
2987         fontHeight = (fontHeight * squareSize) / 100;\r
2988 \r
2989         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2990         lf.lfWidth = 0;\r
2991         lf.lfEscapement = 0;\r
2992         lf.lfOrientation = 0;\r
2993         lf.lfWeight = FW_NORMAL;\r
2994         lf.lfItalic = 0;\r
2995         lf.lfUnderline = 0;\r
2996         lf.lfStrikeOut = 0;\r
2997         lf.lfCharSet = DEFAULT_CHARSET;\r
2998         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2999         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
3000         lf.lfQuality = PROOF_QUALITY;\r
3001         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
3002         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
3003         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
3004 \r
3005         hPieceFont = CreateFontIndirect( &lf );\r
3006 \r
3007         if( hPieceFont == NULL ) {\r
3008             fontBitmapSquareSize = -2;\r
3009         }\r
3010         else {\r
3011             /* Setup font-to-piece character table */\r
3012             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
3013                 /* No (or wrong) global settings, try to detect the font */\r
3014                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
3015                     /* Alpha */\r
3016                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
3017                 }\r
3018                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3019                     /* DiagramTT* family */\r
3020                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3021                 }\r
3022                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3023                     /* Fairy symbols */\r
3024                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3025                 }\r
3026                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3027                     /* Good Companion (Some characters get warped as literal :-( */\r
3028                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3029                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3030                     SetCharTable(pieceToFontChar, s);\r
3031                 }\r
3032                 else {\r
3033                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3034                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3035                 }\r
3036             }\r
3037 \r
3038             /* Create bitmaps */\r
3039             hfont_old = SelectObject( hdc, hPieceFont );\r
3040             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3041                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3042                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3043 \r
3044             SelectObject( hdc, hfont_old );\r
3045 \r
3046             fontBitmapSquareSize = squareSize;\r
3047         }\r
3048     }\r
3049 \r
3050     if( hdc != NULL ) {\r
3051         DeleteDC( hdc );\r
3052     }\r
3053 \r
3054     if( hdc_window != NULL ) {\r
3055         ReleaseDC( hwndMain, hdc_window );\r
3056     }\r
3057 }\r
3058 \r
3059 HBITMAP\r
3060 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3061 {\r
3062   char name[128];\r
3063 \r
3064   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3065   if (gameInfo.event &&\r
3066       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3067       strcmp(name, "k80s") == 0) {\r
3068     strcpy(name, "tim");\r
3069   }\r
3070   return LoadBitmap(hinst, name);\r
3071 }\r
3072 \r
3073 \r
3074 /* Insert a color into the program's logical palette\r
3075    structure.  This code assumes the given color is\r
3076    the result of the RGB or PALETTERGB macro, and it\r
3077    knows how those macros work (which is documented).\r
3078 */\r
3079 VOID\r
3080 InsertInPalette(COLORREF color)\r
3081 {\r
3082   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3083 \r
3084   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3085     DisplayFatalError("Too many colors", 0, 1);\r
3086     pLogPal->palNumEntries--;\r
3087     return;\r
3088   }\r
3089 \r
3090   pe->peFlags = (char) 0;\r
3091   pe->peRed = (char) (0xFF & color);\r
3092   pe->peGreen = (char) (0xFF & (color >> 8));\r
3093   pe->peBlue = (char) (0xFF & (color >> 16));\r
3094   return;\r
3095 }\r
3096 \r
3097 \r
3098 VOID\r
3099 InitDrawingColors()\r
3100 {\r
3101   if (pLogPal == NULL) {\r
3102     /* Allocate enough memory for a logical palette with\r
3103      * PALETTESIZE entries and set the size and version fields\r
3104      * of the logical palette structure.\r
3105      */\r
3106     pLogPal = (NPLOGPALETTE)\r
3107       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3108                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3109     pLogPal->palVersion    = 0x300;\r
3110   }\r
3111   pLogPal->palNumEntries = 0;\r
3112 \r
3113   InsertInPalette(lightSquareColor);\r
3114   InsertInPalette(darkSquareColor);\r
3115   InsertInPalette(whitePieceColor);\r
3116   InsertInPalette(blackPieceColor);\r
3117   InsertInPalette(highlightSquareColor);\r
3118   InsertInPalette(premoveHighlightColor);\r
3119 \r
3120   /*  create a logical color palette according the information\r
3121    *  in the LOGPALETTE structure.\r
3122    */\r
3123   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3124 \r
3125   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3126   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3127   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3128   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3129   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3130   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3131   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3132   /* [AS] Force rendering of the font-based pieces */\r
3133   if( fontBitmapSquareSize > 0 ) {\r
3134     fontBitmapSquareSize = 0;\r
3135   }\r
3136 }\r
3137 \r
3138 \r
3139 int\r
3140 BoardWidth(int boardSize, int n)\r
3141 { /* [HGM] argument n added to allow different width and height */\r
3142   int lineGap = sizeInfo[boardSize].lineGap;\r
3143 \r
3144   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3145       lineGap = appData.overrideLineGap;\r
3146   }\r
3147 \r
3148   return (n + 1) * lineGap +\r
3149           n * sizeInfo[boardSize].squareSize;\r
3150 }\r
3151 \r
3152 /* Respond to board resize by dragging edge */\r
3153 VOID\r
3154 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3155 {\r
3156   BoardSize newSize = NUM_SIZES - 1;\r
3157   static int recurse = 0;\r
3158   if (IsIconic(hwndMain)) return;\r
3159   if (recurse > 0) return;\r
3160   recurse++;\r
3161   while (newSize > 0) {\r
3162         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3163         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3164            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3165     newSize--;\r
3166   } \r
3167   boardSize = newSize;\r
3168   InitDrawingSizes(boardSize, flags);\r
3169   recurse--;\r
3170 }\r
3171 \r
3172 \r
3173 \r
3174 VOID\r
3175 InitDrawingSizes(BoardSize boardSize, int flags)\r
3176 {\r
3177   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3178   ChessSquare piece;\r
3179   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3180   HDC hdc;\r
3181   SIZE clockSize, messageSize;\r
3182   HFONT oldFont;\r
3183   char buf[MSG_SIZ];\r
3184   char *str;\r
3185   HMENU hmenu = GetMenu(hwndMain);\r
3186   RECT crect, wrect, oldRect;\r
3187   int offby;\r
3188   LOGBRUSH logbrush;\r
3189 \r
3190   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3191   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3192 \r
3193   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3194   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3195 \r
3196   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3197   oldRect.top = boardY;\r
3198   oldRect.right = boardX + winWidth;\r
3199   oldRect.bottom = boardY + winHeight;\r
3200 \r
3201   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3202   smallLayout = sizeInfo[boardSize].smallLayout;\r
3203   squareSize = sizeInfo[boardSize].squareSize;\r
3204   lineGap = sizeInfo[boardSize].lineGap;\r
3205   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3206 \r
3207   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3208       lineGap = appData.overrideLineGap;\r
3209   }\r
3210 \r
3211   if (tinyLayout != oldTinyLayout) {\r
3212     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3213     if (tinyLayout) {\r
3214       style &= ~WS_SYSMENU;\r
3215       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3216                  "&Minimize\tCtrl+F4");\r
3217     } else {\r
3218       style |= WS_SYSMENU;\r
3219       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3220     }\r
3221     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3222 \r
3223     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3224       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3225         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3226     }\r
3227     DrawMenuBar(hwndMain);\r
3228   }\r
3229 \r
3230   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3231   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3232 \r
3233   /* Get text area sizes */\r
3234   hdc = GetDC(hwndMain);\r
3235   if (appData.clockMode) {\r
3236     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3237   } else {\r
3238     sprintf(buf, "White");\r
3239   }\r
3240   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3241   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3242   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3243   str = "We only care about the height here";\r
3244   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3245   SelectObject(hdc, oldFont);\r
3246   ReleaseDC(hwndMain, hdc);\r
3247 \r
3248   /* Compute where everything goes */\r
3249   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3250         /* [HGM] logo: if either logo is on, reserve space for it */\r
3251         logoHeight =  2*clockSize.cy;\r
3252         leftLogoRect.left   = OUTER_MARGIN;\r
3253         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3254         leftLogoRect.top    = OUTER_MARGIN;\r
3255         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3256 \r
3257         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3258         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3259         rightLogoRect.top    = OUTER_MARGIN;\r
3260         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3261 \r
3262 \r
3263     whiteRect.left = leftLogoRect.right;\r
3264     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3265     whiteRect.top = OUTER_MARGIN;\r
3266     whiteRect.bottom = whiteRect.top + logoHeight;\r
3267 \r
3268     blackRect.right = rightLogoRect.left;\r
3269     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3270     blackRect.top = whiteRect.top;\r
3271     blackRect.bottom = whiteRect.bottom;\r
3272   } else {\r
3273     whiteRect.left = OUTER_MARGIN;\r
3274     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3275     whiteRect.top = OUTER_MARGIN;\r
3276     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3277 \r
3278     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3279     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3280     blackRect.top = whiteRect.top;\r
3281     blackRect.bottom = whiteRect.bottom;\r
3282   }\r
3283 \r
3284   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3285   if (appData.showButtonBar) {\r
3286     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3287       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3288   } else {\r
3289     messageRect.right = OUTER_MARGIN + boardWidth;\r
3290   }\r
3291   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3292   messageRect.bottom = messageRect.top + messageSize.cy;\r
3293 \r
3294   boardRect.left = OUTER_MARGIN;\r
3295   boardRect.right = boardRect.left + boardWidth;\r
3296   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3297   boardRect.bottom = boardRect.top + boardHeight;\r
3298 \r
3299   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3300   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3301   oldBoardSize = boardSize;\r
3302   oldTinyLayout = tinyLayout;\r
3303   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3304   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3305     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3306   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3307   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3308   winHeight = winH; //       without disturbing window attachments\r
3309   GetWindowRect(hwndMain, &wrect);\r
3310   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3311                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3312 \r
3313   // [HGM] placement: let attached windows follow size change.\r
3314   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3315   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3316   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3317   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3318   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3319 \r
3320   /* compensate if menu bar wrapped */\r
3321   GetClientRect(hwndMain, &crect);\r
3322   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3323   winHeight += offby;\r
3324   switch (flags) {\r
3325   case WMSZ_TOPLEFT:\r
3326     SetWindowPos(hwndMain, NULL, \r
3327                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3328                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3329     break;\r
3330 \r
3331   case WMSZ_TOPRIGHT:\r
3332   case WMSZ_TOP:\r
3333     SetWindowPos(hwndMain, NULL, \r
3334                  wrect.left, wrect.bottom - winHeight, \r
3335                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3336     break;\r
3337 \r
3338   case WMSZ_BOTTOMLEFT:\r
3339   case WMSZ_LEFT:\r
3340     SetWindowPos(hwndMain, NULL, \r
3341                  wrect.right - winWidth, wrect.top, \r
3342                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3343     break;\r
3344 \r
3345   case WMSZ_BOTTOMRIGHT:\r
3346   case WMSZ_BOTTOM:\r
3347   case WMSZ_RIGHT:\r
3348   default:\r
3349     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3350                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3351     break;\r
3352   }\r
3353 \r
3354   hwndPause = NULL;\r
3355   for (i = 0; i < N_BUTTONS; i++) {\r
3356     if (buttonDesc[i].hwnd != NULL) {\r
3357       DestroyWindow(buttonDesc[i].hwnd);\r
3358       buttonDesc[i].hwnd = NULL;\r
3359     }\r
3360     if (appData.showButtonBar) {\r
3361       buttonDesc[i].hwnd =\r
3362         CreateWindow("BUTTON", buttonDesc[i].label,\r
3363                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3364                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3365                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3366                      (HMENU) buttonDesc[i].id,\r
3367                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3368       if (tinyLayout) {\r
3369         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3370                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3371                     MAKELPARAM(FALSE, 0));\r
3372       }\r
3373       if (buttonDesc[i].id == IDM_Pause)\r
3374         hwndPause = buttonDesc[i].hwnd;\r
3375       buttonDesc[i].wndproc = (WNDPROC)\r
3376         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3377     }\r
3378   }\r
3379   if (gridPen != NULL) DeleteObject(gridPen);\r
3380   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3381   if (premovePen != NULL) DeleteObject(premovePen);\r
3382   if (lineGap != 0) {\r
3383     logbrush.lbStyle = BS_SOLID;\r
3384     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3385     gridPen =\r
3386       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3387                    lineGap, &logbrush, 0, NULL);\r
3388     logbrush.lbColor = highlightSquareColor;\r
3389     highlightPen =\r
3390       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3391                    lineGap, &logbrush, 0, NULL);\r
3392 \r
3393     logbrush.lbColor = premoveHighlightColor; \r
3394     premovePen =\r
3395       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3396                    lineGap, &logbrush, 0, NULL);\r
3397 \r
3398     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3399     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3400       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3401       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3402         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3403       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3404         BOARD_WIDTH * (squareSize + lineGap);\r
3405       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3406     }\r
3407     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3408       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3409       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3410         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3411         lineGap / 2 + (i * (squareSize + lineGap));\r
3412       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3413         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3414       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3415     }\r
3416   }\r
3417 \r
3418   /* [HGM] Licensing requirement */\r
3419 #ifdef GOTHIC\r
3420   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3421 #endif\r
3422 #ifdef FALCON\r
3423   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3424 #endif\r
3425   GothicPopUp( "", VariantNormal);\r
3426 \r
3427 \r
3428 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3429 \r
3430   /* Load piece bitmaps for this board size */\r
3431   for (i=0; i<=2; i++) {\r
3432     for (piece = WhitePawn;\r
3433          (int) piece < (int) BlackPawn;\r
3434          piece = (ChessSquare) ((int) piece + 1)) {\r
3435       if (pieceBitmap[i][piece] != NULL)\r
3436         DeleteObject(pieceBitmap[i][piece]);\r
3437     }\r
3438   }\r
3439 \r
3440   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3441   // Orthodox Chess pieces\r
3442   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3443   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3444   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3445   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3446   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3447   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3448   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3449   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3450   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3451   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3452   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3453   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3454   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3455   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3456   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3457   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3458     // in Shogi, Hijack the unused Queen for Lance\r
3459     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3460     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3461     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3462   } else {\r
3463     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3464     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3465     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3466   }\r
3467 \r
3468   if(squareSize <= 72 && squareSize >= 33) { \r
3469     /* A & C are available in most sizes now */\r
3470     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3471       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3472       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3473       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3474       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3475       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3476       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3477       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3478       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3479       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3480       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3481       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3482       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3483     } else { // Smirf-like\r
3484       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3485       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3486       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3487     }\r
3488     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3489       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3490       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3491       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3492     } else { // WinBoard standard\r
3493       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3494       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3495       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3496     }\r
3497   }\r
3498 \r
3499 \r
3500   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3501     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3502     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3503     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3504     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3505     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3506     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3507     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3508     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3509     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3510     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3511     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3512     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3513     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3514     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3515     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3516     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3517     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3518     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3519     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3520     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3521     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3522     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3523     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3524     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3525     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3526     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3527     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3528     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3529     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3530     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3531 \r
3532     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3533       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3534       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3535       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3536       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3537       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3538       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3539       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3540       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3541       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3542       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3543       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3544       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3545     } else {\r
3546       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3547       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3548       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3549       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3550       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3551       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3552       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3553       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3554       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3555       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3556       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3557       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3558     }\r
3559 \r
3560   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3561     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3562     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3563     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3564     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3565     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3566     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3567     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3568     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3569     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3570     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3571     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3572     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3573     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3574     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3575   }\r
3576 \r
3577 \r
3578   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3579   /* special Shogi support in this size */\r
3580   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3581       for (piece = WhitePawn;\r
3582            (int) piece < (int) BlackPawn;\r
3583            piece = (ChessSquare) ((int) piece + 1)) {\r
3584         if (pieceBitmap[i][piece] != NULL)\r
3585           DeleteObject(pieceBitmap[i][piece]);\r
3586       }\r
3587     }\r
3588   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3589   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3590   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3591   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3592   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3593   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3594   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3595   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3596   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3597   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3598   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3599   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3600   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3601   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3602   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3603   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3604   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3605   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3606   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3607   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3608   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3609   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3610   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3611   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3612   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3613   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3614   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3615   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3616   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3617   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3618   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3619   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3620   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3621   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3622   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3623   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3624   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3625   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3626   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3627   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3628   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3629   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3630   minorSize = 0;\r
3631   }\r
3632 }\r
3633 \r
3634 HBITMAP\r
3635 PieceBitmap(ChessSquare p, int kind)\r
3636 {\r
3637   if ((int) p >= (int) BlackPawn)\r
3638     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3639 \r
3640   return pieceBitmap[kind][(int) p];\r
3641 }\r
3642 \r
3643 /***************************************************************/\r
3644 \r
3645 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3646 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3647 /*\r
3648 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3649 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3650 */\r
3651 \r
3652 VOID\r
3653 SquareToPos(int row, int column, int * x, int * y)\r
3654 {\r
3655   if (flipView) {\r
3656     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3657     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3658   } else {\r
3659     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3660     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3661   }\r
3662 }\r
3663 \r
3664 VOID\r
3665 DrawCoordsOnDC(HDC hdc)\r
3666 {\r
3667   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
3668   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
3669   char str[2] = { NULLCHAR, NULLCHAR };\r
3670   int oldMode, oldAlign, x, y, start, i;\r
3671   HFONT oldFont;\r
3672   HBRUSH oldBrush;\r
3673 \r
3674   if (!appData.showCoords)\r
3675     return;\r
3676 \r
3677   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3678 \r
3679   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3680   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3681   oldAlign = GetTextAlign(hdc);\r
3682   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3683 \r
3684   y = boardRect.top + lineGap;\r
3685   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3686 \r
3687   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3688   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3689     str[0] = files[start + i];\r
3690     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3691     y += squareSize + lineGap;\r
3692   }\r
3693 \r
3694   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3695 \r
3696   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3697   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3698     str[0] = ranks[start + i];\r
3699     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3700     x += squareSize + lineGap;\r
3701   }    \r
3702 \r
3703   SelectObject(hdc, oldBrush);\r
3704   SetBkMode(hdc, oldMode);\r
3705   SetTextAlign(hdc, oldAlign);\r
3706   SelectObject(hdc, oldFont);\r
3707 }\r
3708 \r
3709 VOID\r
3710 DrawGridOnDC(HDC hdc)\r
3711 {\r
3712   HPEN oldPen;\r
3713  \r
3714   if (lineGap != 0) {\r
3715     oldPen = SelectObject(hdc, gridPen);\r
3716     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3717     SelectObject(hdc, oldPen);\r
3718   }\r
3719 }\r
3720 \r
3721 #define HIGHLIGHT_PEN 0\r
3722 #define PREMOVE_PEN   1\r
3723 \r
3724 VOID\r
3725 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3726 {\r
3727   int x1, y1;\r
3728   HPEN oldPen, hPen;\r
3729   if (lineGap == 0) return;\r
3730   if (flipView) {\r
3731     x1 = boardRect.left +\r
3732       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3733     y1 = boardRect.top +\r
3734       lineGap/2 + y * (squareSize + lineGap);\r
3735   } else {\r
3736     x1 = boardRect.left +\r
3737       lineGap/2 + x * (squareSize + lineGap);\r
3738     y1 = boardRect.top +\r
3739       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3740   }\r
3741   hPen = pen ? premovePen : highlightPen;\r
3742   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3743   MoveToEx(hdc, x1, y1, NULL);\r
3744   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3745   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3746   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3747   LineTo(hdc, x1, y1);\r
3748   SelectObject(hdc, oldPen);\r
3749 }\r
3750 \r
3751 VOID\r
3752 DrawHighlightsOnDC(HDC hdc)\r
3753 {\r
3754   int i;\r
3755   for (i=0; i<2; i++) {\r
3756     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3757       DrawHighlightOnDC(hdc, TRUE,\r
3758                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3759                         HIGHLIGHT_PEN);\r
3760   }\r
3761   for (i=0; i<2; i++) {\r
3762     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3763         premoveHighlightInfo.sq[i].y >= 0) {\r
3764         DrawHighlightOnDC(hdc, TRUE,\r
3765                           premoveHighlightInfo.sq[i].x, \r
3766                           premoveHighlightInfo.sq[i].y,\r
3767                           PREMOVE_PEN);\r
3768     }\r
3769   }\r
3770 }\r
3771 \r
3772 /* Note: sqcolor is used only in monoMode */\r
3773 /* Note that this code is largely duplicated in woptions.c,\r
3774    function DrawSampleSquare, so that needs to be updated too */\r
3775 VOID\r
3776 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3777 {\r
3778   HBITMAP oldBitmap;\r
3779   HBRUSH oldBrush;\r
3780   int tmpSize;\r
3781 \r
3782   if (appData.blindfold) return;\r
3783 \r
3784   /* [AS] Use font-based pieces if needed */\r
3785   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3786     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3787     CreatePiecesFromFont();\r
3788 \r
3789     if( fontBitmapSquareSize == squareSize ) {\r
3790         int index = TranslatePieceToFontPiece(piece);\r
3791 \r
3792         SelectObject( tmphdc, hPieceMask[ index ] );\r
3793 \r
3794         BitBlt( hdc,\r
3795             x, y,\r
3796             squareSize, squareSize,\r
3797             tmphdc,\r
3798             0, 0,\r
3799             SRCAND );\r
3800 \r
3801         SelectObject( tmphdc, hPieceFace[ index ] );\r
3802 \r
3803         BitBlt( hdc,\r
3804             x, y,\r
3805             squareSize, squareSize,\r
3806             tmphdc,\r
3807             0, 0,\r
3808             SRCPAINT );\r
3809 \r
3810         return;\r
3811     }\r
3812   }\r
3813 \r
3814   if (appData.monoMode) {\r
3815     SelectObject(tmphdc, PieceBitmap(piece, \r
3816       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3817     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3818            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3819   } else {\r
3820     tmpSize = squareSize;\r
3821     if(minorSize &&\r
3822         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3823          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3824       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3825       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3826       x += (squareSize - minorSize)>>1;\r
3827       y += squareSize - minorSize - 2;\r
3828       tmpSize = minorSize;\r
3829     }\r
3830     if (color || appData.allWhite ) {\r
3831       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3832       if( color )\r
3833               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3834       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3835       if(appData.upsideDown && color==flipView)\r
3836         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3837       else\r
3838         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3839       /* Use black for outline of white pieces */\r
3840       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3841       if(appData.upsideDown && color==flipView)\r
3842         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3843       else\r
3844         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3845     } else {\r
3846       /* Use square color for details of black pieces */\r
3847       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3848       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3849       if(appData.upsideDown && !flipView)\r
3850         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3851       else\r
3852         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3853     }\r
3854     SelectObject(hdc, oldBrush);\r
3855     SelectObject(tmphdc, oldBitmap);\r
3856   }\r
3857 }\r
3858 \r
3859 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3860 int GetBackTextureMode( int algo )\r
3861 {\r
3862     int result = BACK_TEXTURE_MODE_DISABLED;\r
3863 \r
3864     switch( algo ) \r
3865     {\r
3866         case BACK_TEXTURE_MODE_PLAIN:\r
3867             result = 1; /* Always use identity map */\r
3868             break;\r
3869         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3870             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3871             break;\r
3872     }\r
3873 \r
3874     return result;\r
3875 }\r
3876 \r
3877 /* \r
3878     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3879     to handle redraws cleanly (as random numbers would always be different).\r
3880 */\r
3881 VOID RebuildTextureSquareInfo()\r
3882 {\r
3883     BITMAP bi;\r
3884     int lite_w = 0;\r
3885     int lite_h = 0;\r
3886     int dark_w = 0;\r
3887     int dark_h = 0;\r
3888     int row;\r
3889     int col;\r
3890 \r
3891     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3892 \r
3893     if( liteBackTexture != NULL ) {\r
3894         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3895             lite_w = bi.bmWidth;\r
3896             lite_h = bi.bmHeight;\r
3897         }\r
3898     }\r
3899 \r
3900     if( darkBackTexture != NULL ) {\r
3901         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3902             dark_w = bi.bmWidth;\r
3903             dark_h = bi.bmHeight;\r
3904         }\r
3905     }\r
3906 \r
3907     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3908         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3909             if( (col + row) & 1 ) {\r
3910                 /* Lite square */\r
3911                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3912                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3913                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3914                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3915                 }\r
3916             }\r
3917             else {\r
3918                 /* Dark square */\r
3919                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3920                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3921                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3922                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3923                 }\r
3924             }\r
3925         }\r
3926     }\r
3927 }\r
3928 \r
3929 /* [AS] Arrow highlighting support */\r
3930 \r
3931 static int A_WIDTH = 5; /* Width of arrow body */\r
3932 \r
3933 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3934 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3935 \r
3936 static double Sqr( double x )\r
3937 {\r
3938     return x*x;\r
3939 }\r
3940 \r
3941 static int Round( double x )\r
3942 {\r
3943     return (int) (x + 0.5);\r
3944 }\r
3945 \r
3946 /* Draw an arrow between two points using current settings */\r
3947 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3948 {\r
3949     POINT arrow[7];\r
3950     double dx, dy, j, k, x, y;\r
3951 \r
3952     if( d_x == s_x ) {\r
3953         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3954 \r
3955         arrow[0].x = s_x + A_WIDTH;\r
3956         arrow[0].y = s_y;\r
3957 \r
3958         arrow[1].x = s_x + A_WIDTH;\r
3959         arrow[1].y = d_y - h;\r
3960 \r
3961         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3962         arrow[2].y = d_y - h;\r
3963 \r
3964         arrow[3].x = d_x;\r
3965         arrow[3].y = d_y;\r
3966 \r
3967         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3968         arrow[4].y = d_y - h;\r
3969 \r
3970         arrow[5].x = s_x - A_WIDTH;\r
3971         arrow[5].y = d_y - h;\r
3972 \r
3973         arrow[6].x = s_x - A_WIDTH;\r
3974         arrow[6].y = s_y;\r
3975     }\r
3976     else if( d_y == s_y ) {\r
3977         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3978 \r
3979         arrow[0].x = s_x;\r
3980         arrow[0].y = s_y + A_WIDTH;\r
3981 \r
3982         arrow[1].x = d_x - w;\r
3983         arrow[1].y = s_y + A_WIDTH;\r
3984 \r
3985         arrow[2].x = d_x - w;\r
3986         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3987 \r
3988         arrow[3].x = d_x;\r
3989         arrow[3].y = d_y;\r
3990 \r
3991         arrow[4].x = d_x - w;\r
3992         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3993 \r
3994         arrow[5].x = d_x - w;\r
3995         arrow[5].y = s_y - A_WIDTH;\r
3996 \r
3997         arrow[6].x = s_x;\r
3998         arrow[6].y = s_y - A_WIDTH;\r
3999     }\r
4000     else {\r
4001         /* [AS] Needed a lot of paper for this! :-) */\r
4002         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4003         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4004   \r
4005         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4006 \r
4007         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4008 \r
4009         x = s_x;\r
4010         y = s_y;\r
4011 \r
4012         arrow[0].x = Round(x - j);\r
4013         arrow[0].y = Round(y + j*dx);\r
4014 \r
4015         arrow[1].x = Round(x + j);\r
4016         arrow[1].y = Round(y - j*dx);\r
4017 \r
4018         if( d_x > s_x ) {\r
4019             x = (double) d_x - k;\r
4020             y = (double) d_y - k*dy;\r
4021         }\r
4022         else {\r
4023             x = (double) d_x + k;\r
4024             y = (double) d_y + k*dy;\r
4025         }\r
4026 \r
4027         arrow[2].x = Round(x + j);\r
4028         arrow[2].y = Round(y - j*dx);\r
4029 \r
4030         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4031         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4032 \r
4033         arrow[4].x = d_x;\r
4034         arrow[4].y = d_y;\r
4035 \r
4036         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4037         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4038 \r
4039         arrow[6].x = Round(x - j);\r
4040         arrow[6].y = Round(y + j*dx);\r
4041     }\r
4042 \r
4043     Polygon( hdc, arrow, 7 );\r
4044 }\r
4045 \r
4046 /* [AS] Draw an arrow between two squares */\r
4047 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4048 {\r
4049     int s_x, s_y, d_x, d_y;\r
4050     HPEN hpen;\r
4051     HPEN holdpen;\r
4052     HBRUSH hbrush;\r
4053     HBRUSH holdbrush;\r
4054     LOGBRUSH stLB;\r
4055 \r
4056     if( s_col == d_col && s_row == d_row ) {\r
4057         return;\r
4058     }\r
4059 \r
4060     /* Get source and destination points */\r
4061     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4062     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4063 \r
4064     if( d_y > s_y ) {\r
4065         d_y += squareSize / 4;\r
4066     }\r
4067     else if( d_y < s_y ) {\r
4068         d_y += 3 * squareSize / 4;\r
4069     }\r
4070     else {\r
4071         d_y += squareSize / 2;\r
4072     }\r
4073 \r
4074     if( d_x > s_x ) {\r
4075         d_x += squareSize / 4;\r
4076     }\r
4077     else if( d_x < s_x ) {\r
4078         d_x += 3 * squareSize / 4;\r
4079     }\r
4080     else {\r
4081         d_x += squareSize / 2;\r
4082     }\r
4083 \r
4084     s_x += squareSize / 2;\r
4085     s_y += squareSize / 2;\r
4086 \r
4087     /* Adjust width */\r
4088     A_WIDTH = squareSize / 14;\r
4089 \r
4090     /* Draw */\r
4091     stLB.lbStyle = BS_SOLID;\r
4092     stLB.lbColor = appData.highlightArrowColor;\r
4093     stLB.lbHatch = 0;\r
4094 \r
4095     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4096     holdpen = SelectObject( hdc, hpen );\r
4097     hbrush = CreateBrushIndirect( &stLB );\r
4098     holdbrush = SelectObject( hdc, hbrush );\r
4099 \r
4100     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4101 \r
4102     SelectObject( hdc, holdpen );\r
4103     SelectObject( hdc, holdbrush );\r
4104     DeleteObject( hpen );\r
4105     DeleteObject( hbrush );\r
4106 }\r
4107 \r
4108 BOOL HasHighlightInfo()\r
4109 {\r
4110     BOOL result = FALSE;\r
4111 \r
4112     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4113         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4114     {\r
4115         result = TRUE;\r
4116     }\r
4117 \r
4118     return result;\r
4119 }\r
4120 \r
4121 BOOL IsDrawArrowEnabled()\r
4122 {\r
4123     BOOL result = FALSE;\r
4124 \r
4125     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4126         result = TRUE;\r
4127     }\r
4128 \r
4129     return result;\r
4130 }\r
4131 \r
4132 VOID DrawArrowHighlight( HDC hdc )\r
4133 {\r
4134     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4135         DrawArrowBetweenSquares( hdc,\r
4136             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4137             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4138     }\r
4139 }\r
4140 \r
4141 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4142 {\r
4143     HRGN result = NULL;\r
4144 \r
4145     if( HasHighlightInfo() ) {\r
4146         int x1, y1, x2, y2;\r
4147         int sx, sy, dx, dy;\r
4148 \r
4149         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4150         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4151 \r
4152         sx = MIN( x1, x2 );\r
4153         sy = MIN( y1, y2 );\r
4154         dx = MAX( x1, x2 ) + squareSize;\r
4155         dy = MAX( y1, y2 ) + squareSize;\r
4156 \r
4157         result = CreateRectRgn( sx, sy, dx, dy );\r
4158     }\r
4159 \r
4160     return result;\r
4161 }\r
4162 \r
4163 /*\r
4164     Warning: this function modifies the behavior of several other functions. \r
4165     \r
4166     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4167     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4168     repaint is scattered all over the place, which is not good for features such as\r
4169     "arrow highlighting" that require a full repaint of the board.\r
4170 \r
4171     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4172     user interaction, when speed is not so important) but especially to avoid errors\r
4173     in the displayed graphics.\r
4174 \r
4175     In such patched places, I always try refer to this function so there is a single\r
4176     place to maintain knowledge.\r
4177     \r
4178     To restore the original behavior, just return FALSE unconditionally.\r
4179 */\r
4180 BOOL IsFullRepaintPreferrable()\r
4181 {\r
4182     BOOL result = FALSE;\r
4183 \r
4184     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4185         /* Arrow may appear on the board */\r
4186         result = TRUE;\r
4187     }\r
4188 \r
4189     return result;\r
4190 }\r
4191 \r
4192 /* \r
4193     This function is called by DrawPosition to know whether a full repaint must\r
4194     be forced or not.\r
4195 \r
4196     Only DrawPosition may directly call this function, which makes use of \r
4197     some state information. Other function should call DrawPosition specifying \r
4198     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4199 */\r
4200 BOOL DrawPositionNeedsFullRepaint()\r
4201 {\r
4202     BOOL result = FALSE;\r
4203 \r
4204     /* \r
4205         Probably a slightly better policy would be to trigger a full repaint\r
4206         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4207         but animation is fast enough that it's difficult to notice.\r
4208     */\r
4209     if( animInfo.piece == EmptySquare ) {\r
4210         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4211             result = TRUE;\r
4212         }\r
4213     }\r
4214 \r
4215     return result;\r
4216 }\r
4217 \r
4218 VOID\r
4219 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4220 {\r
4221   int row, column, x, y, square_color, piece_color;\r
4222   ChessSquare piece;\r
4223   HBRUSH oldBrush;\r
4224   HDC texture_hdc = NULL;\r
4225 \r
4226   /* [AS] Initialize background textures if needed */\r
4227   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4228       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4229       if( backTextureSquareSize != squareSize \r
4230        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4231           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4232           backTextureSquareSize = squareSize;\r
4233           RebuildTextureSquareInfo();\r
4234       }\r
4235 \r
4236       texture_hdc = CreateCompatibleDC( hdc );\r
4237   }\r
4238 \r
4239   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4240     for (column = 0; column < BOARD_WIDTH; column++) {\r
4241   \r
4242       SquareToPos(row, column, &x, &y);\r
4243 \r
4244       piece = board[row][column];\r
4245 \r
4246       square_color = ((column + row) % 2) == 1;\r
4247       if( gameInfo.variant == VariantXiangqi ) {\r
4248           square_color = !InPalace(row, column);\r
4249           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4250           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4251       }\r
4252       piece_color = (int) piece < (int) BlackPawn;\r
4253 \r
4254 \r
4255       /* [HGM] holdings file: light square or black */\r
4256       if(column == BOARD_LEFT-2) {\r
4257             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4258                 square_color = 1;\r
4259             else {\r
4260                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4261                 continue;\r
4262             }\r
4263       } else\r
4264       if(column == BOARD_RGHT + 1 ) {\r
4265             if( row < gameInfo.holdingsSize )\r
4266                 square_color = 1;\r
4267             else {\r
4268                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4269                 continue;\r
4270             }\r
4271       }\r
4272       if(column == BOARD_LEFT-1 ) /* left align */\r
4273             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4274       else if( column == BOARD_RGHT) /* right align */\r
4275             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4276       else\r
4277       if (appData.monoMode) {\r
4278         if (piece == EmptySquare) {\r
4279           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4280                  square_color ? WHITENESS : BLACKNESS);\r
4281         } else {\r
4282           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4283         }\r
4284       } \r
4285       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4286           /* [AS] Draw the square using a texture bitmap */\r
4287           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4288           int r = row, c = column; // [HGM] do not flip board in flipView\r
4289           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4290 \r
4291           DrawTile( x, y, \r
4292               squareSize, squareSize, \r
4293               hdc, \r
4294               texture_hdc,\r
4295               backTextureSquareInfo[r][c].mode,\r
4296               backTextureSquareInfo[r][c].x,\r
4297               backTextureSquareInfo[r][c].y );\r
4298 \r
4299           SelectObject( texture_hdc, hbm );\r
4300 \r
4301           if (piece != EmptySquare) {\r
4302               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4303           }\r
4304       }\r
4305       else {\r
4306         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4307 \r
4308         oldBrush = SelectObject(hdc, brush );\r
4309         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4310         SelectObject(hdc, oldBrush);\r
4311         if (piece != EmptySquare)\r
4312           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4313       }\r
4314     }\r
4315   }\r
4316 \r
4317   if( texture_hdc != NULL ) {\r
4318     DeleteDC( texture_hdc );\r
4319   }\r
4320 }\r
4321 \r
4322 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4323 void fputDW(FILE *f, int x)\r
4324 {\r
4325         fputc(x     & 255, f);\r
4326         fputc(x>>8  & 255, f);\r
4327         fputc(x>>16 & 255, f);\r
4328         fputc(x>>24 & 255, f);\r
4329 }\r
4330 \r
4331 #define MAX_CLIPS 200   /* more than enough */\r
4332 \r
4333 VOID\r
4334 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4335 {\r
4336 //  HBITMAP bufferBitmap;\r
4337   BITMAP bi;\r
4338 //  RECT Rect;\r
4339   HDC tmphdc;\r
4340   HBITMAP hbm;\r
4341   int w = 100, h = 50;\r
4342 \r
4343   if(logo == NULL) return;\r
4344 //  GetClientRect(hwndMain, &Rect);\r
4345 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4346 //                                      Rect.bottom-Rect.top+1);\r
4347   tmphdc = CreateCompatibleDC(hdc);\r
4348   hbm = SelectObject(tmphdc, logo);\r
4349   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4350             w = bi.bmWidth;\r
4351             h = bi.bmHeight;\r
4352   }\r
4353   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4354                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4355   SelectObject(tmphdc, hbm);\r
4356   DeleteDC(tmphdc);\r
4357 }\r
4358 \r
4359 VOID\r
4360 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4361 {\r
4362   static Board lastReq, lastDrawn;\r
4363   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4364   static int lastDrawnFlipView = 0;\r
4365   static int lastReqValid = 0, lastDrawnValid = 0;\r
4366   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4367   HDC tmphdc;\r
4368   HDC hdcmem;\r
4369   HBITMAP bufferBitmap;\r
4370   HBITMAP oldBitmap;\r
4371   RECT Rect;\r
4372   HRGN clips[MAX_CLIPS];\r
4373   ChessSquare dragged_piece = EmptySquare;\r
4374 \r
4375   /* I'm undecided on this - this function figures out whether a full\r
4376    * repaint is necessary on its own, so there's no real reason to have the\r
4377    * caller tell it that.  I think this can safely be set to FALSE - but\r
4378    * if we trust the callers not to request full repaints unnessesarily, then\r
4379    * we could skip some clipping work.  In other words, only request a full\r
4380    * redraw when the majority of pieces have changed positions (ie. flip, \r
4381    * gamestart and similar)  --Hawk\r
4382    */\r
4383   Boolean fullrepaint = repaint;\r
4384 \r
4385   if( DrawPositionNeedsFullRepaint() ) {\r
4386       fullrepaint = TRUE;\r
4387   }\r
4388 \r
4389   if (board == NULL) {\r
4390     if (!lastReqValid) {\r
4391       return;\r
4392     }\r
4393     board = lastReq;\r
4394   } else {\r
4395     CopyBoard(lastReq, board);\r
4396     lastReqValid = 1;\r
4397   }\r
4398 \r
4399   if (doingSizing) {\r
4400     return;\r
4401   }\r
4402 \r
4403   if (IsIconic(hwndMain)) {\r
4404     return;\r
4405   }\r
4406 \r
4407   if (hdc == NULL) {\r
4408     hdc = GetDC(hwndMain);\r
4409     if (!appData.monoMode) {\r
4410       SelectPalette(hdc, hPal, FALSE);\r
4411       RealizePalette(hdc);\r
4412     }\r
4413     releaseDC = TRUE;\r
4414   } else {\r
4415     releaseDC = FALSE;\r
4416   }\r
4417 \r
4418   /* Create some work-DCs */\r
4419   hdcmem = CreateCompatibleDC(hdc);\r
4420   tmphdc = CreateCompatibleDC(hdc);\r
4421 \r
4422   /* If dragging is in progress, we temporarely remove the piece */\r
4423   /* [HGM] or temporarily decrease count if stacked              */\r
4424   /*       !! Moved to before board compare !!                   */\r
4425   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4426     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4427     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4428             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4429         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4430     } else \r
4431     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4432             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4433         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4434     } else \r
4435         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4436   }\r
4437 \r
4438   /* Figure out which squares need updating by comparing the \r
4439    * newest board with the last drawn board and checking if\r
4440    * flipping has changed.\r
4441    */\r
4442   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4443     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4444       for (column = 0; column < BOARD_WIDTH; column++) {\r
4445         if (lastDrawn[row][column] != board[row][column]) {\r
4446           SquareToPos(row, column, &x, &y);\r
4447           clips[num_clips++] =\r
4448             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4449         }\r
4450       }\r
4451     }\r
4452     for (i=0; i<2; i++) {\r
4453       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4454           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4455         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4456             lastDrawnHighlight.sq[i].y >= 0) {\r
4457           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4458                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4459           clips[num_clips++] =\r
4460             CreateRectRgn(x - lineGap, y - lineGap, \r
4461                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4462         }\r
4463         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4464           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4465           clips[num_clips++] =\r
4466             CreateRectRgn(x - lineGap, y - lineGap, \r
4467                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4468         }\r
4469       }\r
4470     }\r
4471     for (i=0; i<2; i++) {\r
4472       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4473           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4474         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4475             lastDrawnPremove.sq[i].y >= 0) {\r
4476           SquareToPos(lastDrawnPremove.sq[i].y,\r
4477                       lastDrawnPremove.sq[i].x, &x, &y);\r
4478           clips[num_clips++] =\r
4479             CreateRectRgn(x - lineGap, y - lineGap, \r
4480                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4481         }\r
4482         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4483             premoveHighlightInfo.sq[i].y >= 0) {\r
4484           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4485                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4486           clips[num_clips++] =\r
4487             CreateRectRgn(x - lineGap, y - lineGap, \r
4488                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4489         }\r
4490       }\r
4491     }\r
4492   } else {\r
4493     fullrepaint = TRUE;\r
4494   }\r
4495 \r
4496   /* Create a buffer bitmap - this is the actual bitmap\r
4497    * being written to.  When all the work is done, we can\r
4498    * copy it to the real DC (the screen).  This avoids\r
4499    * the problems with flickering.\r
4500    */\r
4501   GetClientRect(hwndMain, &Rect);\r
4502   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4503                                         Rect.bottom-Rect.top+1);\r
4504   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4505   if (!appData.monoMode) {\r
4506     SelectPalette(hdcmem, hPal, FALSE);\r
4507   }\r
4508 \r
4509   /* Create clips for dragging */\r
4510   if (!fullrepaint) {\r
4511     if (dragInfo.from.x >= 0) {\r
4512       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4513       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4514     }\r
4515     if (dragInfo.start.x >= 0) {\r
4516       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4517       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4518     }\r
4519     if (dragInfo.pos.x >= 0) {\r
4520       x = dragInfo.pos.x - squareSize / 2;\r
4521       y = dragInfo.pos.y - squareSize / 2;\r
4522       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4523     }\r
4524     if (dragInfo.lastpos.x >= 0) {\r
4525       x = dragInfo.lastpos.x - squareSize / 2;\r
4526       y = dragInfo.lastpos.y - squareSize / 2;\r
4527       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4528     }\r
4529   }\r
4530 \r
4531   /* Are we animating a move?  \r
4532    * If so, \r
4533    *   - remove the piece from the board (temporarely)\r
4534    *   - calculate the clipping region\r
4535    */\r
4536   if (!fullrepaint) {\r
4537     if (animInfo.piece != EmptySquare) {\r
4538       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4539       x = boardRect.left + animInfo.lastpos.x;\r
4540       y = boardRect.top + animInfo.lastpos.y;\r
4541       x2 = boardRect.left + animInfo.pos.x;\r
4542       y2 = boardRect.top + animInfo.pos.y;\r
4543       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4544       /* Slight kludge.  The real problem is that after AnimateMove is\r
4545          done, the position on the screen does not match lastDrawn.\r
4546          This currently causes trouble only on e.p. captures in\r
4547          atomic, where the piece moves to an empty square and then\r
4548          explodes.  The old and new positions both had an empty square\r
4549          at the destination, but animation has drawn a piece there and\r
4550          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4551       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4552     }\r
4553   }\r
4554 \r
4555   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4556   if (num_clips == 0)\r
4557     fullrepaint = TRUE;\r
4558 \r
4559   /* Set clipping on the memory DC */\r
4560   if (!fullrepaint) {\r
4561     SelectClipRgn(hdcmem, clips[0]);\r
4562     for (x = 1; x < num_clips; x++) {\r
4563       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4564         abort();  // this should never ever happen!\r
4565     }\r
4566   }\r
4567 \r
4568   /* Do all the drawing to the memory DC */\r
4569   if(explodeInfo.radius) { // [HGM] atomic\r
4570         HBRUSH oldBrush;\r
4571         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4572         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4573         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4574         x += squareSize/2;\r
4575         y += squareSize/2;\r
4576         if(!fullrepaint) {\r
4577           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4578           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4579         }\r
4580         DrawGridOnDC(hdcmem);\r
4581         DrawHighlightsOnDC(hdcmem);\r
4582         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4583         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4584         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4585         SelectObject(hdcmem, oldBrush);\r
4586   } else {\r
4587     DrawGridOnDC(hdcmem);\r
4588     DrawHighlightsOnDC(hdcmem);\r
4589     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4590   }\r
4591   if(logoHeight) {\r
4592         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4593         if(appData.autoLogo) {\r
4594           \r
4595           switch(gameMode) { // pick logos based on game mode\r
4596             case IcsObserving:\r
4597                 whiteLogo = second.programLogo; // ICS logo\r
4598                 blackLogo = second.programLogo;\r
4599             default:\r
4600                 break;\r
4601             case IcsPlayingWhite:\r
4602                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4603                 blackLogo = second.programLogo; // ICS logo\r
4604                 break;\r
4605             case IcsPlayingBlack:\r
4606                 whiteLogo = second.programLogo; // ICS logo\r
4607                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4608                 break;\r
4609             case TwoMachinesPlay:\r
4610                 if(first.twoMachinesColor[0] == 'b') {\r
4611                     whiteLogo = second.programLogo;\r
4612                     blackLogo = first.programLogo;\r
4613                 }\r
4614                 break;\r
4615             case MachinePlaysWhite:\r
4616                 blackLogo = userLogo;\r
4617                 break;\r
4618             case MachinePlaysBlack:\r
4619                 whiteLogo = userLogo;\r
4620                 blackLogo = first.programLogo;\r
4621           }\r
4622         }\r
4623         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4624         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4625   }\r
4626 \r
4627   if( appData.highlightMoveWithArrow ) {\r
4628     DrawArrowHighlight(hdcmem);\r
4629   }\r
4630 \r
4631   DrawCoordsOnDC(hdcmem);\r
4632 \r
4633   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4634                  /* to make sure lastDrawn contains what is actually drawn */\r
4635 \r
4636   /* Put the dragged piece back into place and draw it (out of place!) */\r
4637     if (dragged_piece != EmptySquare) {\r
4638     /* [HGM] or restack */\r
4639     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4640                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4641     else\r
4642     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4643                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4644     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4645     x = dragInfo.pos.x - squareSize / 2;\r
4646     y = dragInfo.pos.y - squareSize / 2;\r
4647     DrawPieceOnDC(hdcmem, dragged_piece,\r
4648                   ((int) dragged_piece < (int) BlackPawn), \r
4649                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4650   }   \r
4651   \r
4652   /* Put the animated piece back into place and draw it */\r
4653   if (animInfo.piece != EmptySquare) {\r
4654     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4655     x = boardRect.left + animInfo.pos.x;\r
4656     y = boardRect.top + animInfo.pos.y;\r
4657     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4658                   ((int) animInfo.piece < (int) BlackPawn),\r
4659                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4660   }\r
4661 \r
4662   /* Release the bufferBitmap by selecting in the old bitmap \r
4663    * and delete the memory DC\r
4664    */\r
4665   SelectObject(hdcmem, oldBitmap);\r
4666   DeleteDC(hdcmem);\r
4667 \r
4668   /* Set clipping on the target DC */\r
4669   if (!fullrepaint) {\r
4670     SelectClipRgn(hdc, clips[0]);\r
4671     for (x = 1; x < num_clips; x++) {\r
4672       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4673         abort();   // this should never ever happen!\r
4674     } \r
4675   }\r
4676 \r
4677   /* Copy the new bitmap onto the screen in one go.\r
4678    * This way we avoid any flickering\r
4679    */\r
4680   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4681   BitBlt(hdc, boardRect.left, boardRect.top,\r
4682          boardRect.right - boardRect.left,\r
4683          boardRect.bottom - boardRect.top,\r
4684          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4685   if(saveDiagFlag) { \r
4686     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4687     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4688 \r
4689     GetObject(bufferBitmap, sizeof(b), &b);\r
4690     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4691         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4692         bih.biWidth = b.bmWidth;\r
4693         bih.biHeight = b.bmHeight;\r
4694         bih.biPlanes = 1;\r
4695         bih.biBitCount = b.bmBitsPixel;\r
4696         bih.biCompression = 0;\r
4697         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4698         bih.biXPelsPerMeter = 0;\r
4699         bih.biYPelsPerMeter = 0;\r
4700         bih.biClrUsed = 0;\r
4701         bih.biClrImportant = 0;\r
4702 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4703 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4704         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4705 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4706 \r
4707         wb = b.bmWidthBytes;\r
4708         // count colors\r
4709         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4710                 int k = ((int*) pData)[i];\r
4711                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4712                 if(j >= 16) break;\r
4713                 color[j] = k;\r
4714                 if(j >= nrColors) nrColors = j+1;\r
4715         }\r
4716         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4717                 INT p = 0;\r
4718                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4719                     for(w=0; w<(wb>>2); w+=2) {\r
4720                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4721                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4722                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4723                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4724                         pData[p++] = m | j<<4;\r
4725                     }\r
4726                     while(p&3) pData[p++] = 0;\r
4727                 }\r
4728                 fac = 3;\r
4729                 wb = ((wb+31)>>5)<<2;\r
4730         }\r
4731         // write BITMAPFILEHEADER\r
4732         fprintf(diagFile, "BM");\r
4733         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4734         fputDW(diagFile, 0);\r
4735         fputDW(diagFile, 0x36 + (fac?64:0));\r
4736         // write BITMAPINFOHEADER\r
4737         fputDW(diagFile, 40);\r
4738         fputDW(diagFile, b.bmWidth);\r
4739         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4740         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4741         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4742         fputDW(diagFile, 0);\r
4743         fputDW(diagFile, 0);\r
4744         fputDW(diagFile, 0);\r
4745         fputDW(diagFile, 0);\r
4746         fputDW(diagFile, 0);\r
4747         fputDW(diagFile, 0);\r
4748         // write color table\r
4749         if(fac)\r
4750         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4751         // write bitmap data\r
4752         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4753                 fputc(pData[i], diagFile);\r
4754      }\r
4755   }\r
4756 \r
4757   SelectObject(tmphdc, oldBitmap);\r
4758 \r
4759   /* Massive cleanup */\r
4760   for (x = 0; x < num_clips; x++)\r
4761     DeleteObject(clips[x]);\r
4762 \r
4763   DeleteDC(tmphdc);\r
4764   DeleteObject(bufferBitmap);\r
4765 \r
4766   if (releaseDC) \r
4767     ReleaseDC(hwndMain, hdc);\r
4768   \r
4769   if (lastDrawnFlipView != flipView) {\r
4770     if (flipView)\r
4771       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4772     else\r
4773       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4774   }\r
4775 \r
4776 /*  CopyBoard(lastDrawn, board);*/\r
4777   lastDrawnHighlight = highlightInfo;\r
4778   lastDrawnPremove   = premoveHighlightInfo;\r
4779   lastDrawnFlipView = flipView;\r
4780   lastDrawnValid = 1;\r
4781 }\r
4782 \r
4783 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4784 int\r
4785 SaveDiagram(f)\r
4786      FILE *f;\r
4787 {\r
4788     saveDiagFlag = 1; diagFile = f;\r
4789     HDCDrawPosition(NULL, TRUE, NULL);\r
4790 \r
4791     saveDiagFlag = 0;\r
4792 \r
4793 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4794     \r
4795     fclose(f);\r
4796     return TRUE;\r
4797 }\r
4798 \r
4799 \r
4800 /*---------------------------------------------------------------------------*\\r
4801 | CLIENT PAINT PROCEDURE\r
4802 |   This is the main event-handler for the WM_PAINT message.\r
4803 |\r
4804 \*---------------------------------------------------------------------------*/\r
4805 VOID\r
4806 PaintProc(HWND hwnd)\r
4807 {\r
4808   HDC         hdc;\r
4809   PAINTSTRUCT ps;\r
4810   HFONT       oldFont;\r
4811 \r
4812   if((hdc = BeginPaint(hwnd, &ps))) {\r
4813     if (IsIconic(hwnd)) {\r
4814       DrawIcon(hdc, 2, 2, iconCurrent);\r
4815     } else {\r
4816       if (!appData.monoMode) {\r
4817         SelectPalette(hdc, hPal, FALSE);\r
4818         RealizePalette(hdc);\r
4819       }\r
4820       HDCDrawPosition(hdc, 1, NULL);\r
4821       oldFont =\r
4822         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4823       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4824                  ETO_CLIPPED|ETO_OPAQUE,\r
4825                  &messageRect, messageText, strlen(messageText), NULL);\r
4826       SelectObject(hdc, oldFont);\r
4827       DisplayBothClocks();\r
4828     }\r
4829     EndPaint(hwnd,&ps);\r
4830   }\r
4831 \r
4832   return;\r
4833 }\r
4834 \r
4835 \r
4836 /*\r
4837  * If the user selects on a border boundary, return -1; if off the board,\r
4838  *   return -2.  Otherwise map the event coordinate to the square.\r
4839  * The offset boardRect.left or boardRect.top must already have been\r
4840  *   subtracted from x.\r
4841  */\r
4842 int\r
4843 EventToSquare(int x)\r
4844 {\r
4845   if (x <= 0)\r
4846     return -2;\r
4847   if (x < lineGap)\r
4848     return -1;\r
4849   x -= lineGap;\r
4850   if ((x % (squareSize + lineGap)) >= squareSize)\r
4851     return -1;\r
4852   x /= (squareSize + lineGap);\r
4853   if (x >= BOARD_SIZE)\r
4854     return -2;\r
4855   return x;\r
4856 }\r
4857 \r
4858 typedef struct {\r
4859   char piece;\r
4860   int command;\r
4861   char* name;\r
4862 } DropEnable;\r
4863 \r
4864 DropEnable dropEnables[] = {\r
4865   { 'P', DP_Pawn, "Pawn" },\r
4866   { 'N', DP_Knight, "Knight" },\r
4867   { 'B', DP_Bishop, "Bishop" },\r
4868   { 'R', DP_Rook, "Rook" },\r
4869   { 'Q', DP_Queen, "Queen" },\r
4870 };\r
4871 \r
4872 VOID\r
4873 SetupDropMenu(HMENU hmenu)\r
4874 {\r
4875   int i, count, enable;\r
4876   char *p;\r
4877   extern char white_holding[], black_holding[];\r
4878   char item[MSG_SIZ];\r
4879 \r
4880   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4881     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4882                dropEnables[i].piece);\r
4883     count = 0;\r
4884     while (p && *p++ == dropEnables[i].piece) count++;\r
4885     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4886     enable = count > 0 || !appData.testLegality\r
4887       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4888                       && !appData.icsActive);\r
4889     ModifyMenu(hmenu, dropEnables[i].command,\r
4890                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4891                dropEnables[i].command, item);\r
4892   }\r
4893 }\r
4894 \r
4895 /* Event handler for mouse messages */\r
4896 VOID\r
4897 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4898 {\r
4899   int x, y;\r
4900   POINT pt;\r
4901   static int recursive = 0;\r
4902   HMENU hmenu;\r
4903 //  BOOLEAN needsRedraw = FALSE;\r
4904   BOOLEAN saveAnimate;\r
4905   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4906   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4907   ChessMove moveType;\r
4908 \r
4909   if (recursive) {\r
4910     if (message == WM_MBUTTONUP) {\r
4911       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4912          to the middle button: we simulate pressing the left button too!\r
4913          */\r
4914       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4915       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4916     }\r
4917     return;\r
4918   }\r
4919   recursive++;\r
4920   \r
4921   pt.x = LOWORD(lParam);\r
4922   pt.y = HIWORD(lParam);\r
4923   x = EventToSquare(pt.x - boardRect.left);\r
4924   y = EventToSquare(pt.y - boardRect.top);\r
4925   if (!flipView && y >= 0) {\r
4926     y = BOARD_HEIGHT - 1 - y;\r
4927   }\r
4928   if (flipView && x >= 0) {\r
4929     x = BOARD_WIDTH - 1 - x;\r
4930   }\r
4931 \r
4932   switch (message) {\r
4933   case WM_LBUTTONDOWN:\r
4934     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4935         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4936         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4937         if(gameInfo.holdingsWidth && \r
4938                 (WhiteOnMove(currentMove) \r
4939                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4940                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4941             // click in right holdings, for determining promotion piece\r
4942             ChessSquare p = boards[currentMove][y][x];\r
4943             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4944             if(p != EmptySquare) {\r
4945                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4946                 fromX = fromY = -1;\r
4947                 break;\r
4948             }\r
4949         }\r
4950         DrawPosition(FALSE, boards[currentMove]);\r
4951         break;\r
4952     }\r
4953     ErrorPopDown();\r
4954     sameAgain = FALSE;\r
4955     if (y == -2) {\r
4956       /* Downclick vertically off board; check if on clock */\r
4957       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4958         if (gameMode == EditPosition) {\r
4959           SetWhiteToPlayEvent();\r
4960         } else if (gameMode == IcsPlayingBlack ||\r
4961                    gameMode == MachinePlaysWhite) {\r
4962           CallFlagEvent();\r
4963         } else if (gameMode == EditGame) {\r
4964           AdjustClock(flipClock, -1);\r
4965         }\r
4966       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4967         if (gameMode == EditPosition) {\r
4968           SetBlackToPlayEvent();\r
4969         } else if (gameMode == IcsPlayingWhite ||\r
4970                    gameMode == MachinePlaysBlack) {\r
4971           CallFlagEvent();\r
4972         } else if (gameMode == EditGame) {\r
4973           AdjustClock(!flipClock, -1);\r
4974         }\r
4975       }\r
4976       if (!appData.highlightLastMove) {\r
4977         ClearHighlights();\r
4978         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4979       }\r
4980       fromX = fromY = -1;\r
4981       dragInfo.start.x = dragInfo.start.y = -1;\r
4982       dragInfo.from = dragInfo.start;\r
4983       break;\r
4984     } else if (x < 0 || y < 0\r
4985       /* [HGM] block clicks between board and holdings */\r
4986               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4987               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4988               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4989         /* EditPosition, empty square, or different color piece;\r
4990            click-click move is possible */\r
4991                                ) {\r
4992       break;\r
4993     } else if (fromX == x && fromY == y) {\r
4994       /* Downclick on same square again */\r
4995       ClearHighlights();\r
4996       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4997       sameAgain = TRUE;  \r
4998     } else if (fromX != -1 &&\r
4999                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5000                                                                         ) {\r
5001       /* Downclick on different square. */\r
5002       /* [HGM] if on holdings file, should count as new first click ! */\r
5003       /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5004         toX = x;\r
5005         toY = y;\r
5006         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5007            to make sure move is legal before showing promotion popup */\r
5008         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR, FALSE);\r
5009         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5010                 fromX = fromY = -1; \r
5011                 ClearHighlights();\r
5012                 DrawPosition(FALSE, boards[currentMove]);\r
5013                 break; \r
5014         } else \r
5015         if(moveType != ImpossibleMove && moveType != Comment) {\r
5016           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5017           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5018             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5019               appData.alwaysPromoteToQueen)) {\r
5020                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5021                   if (!appData.highlightLastMove) {\r
5022                       ClearHighlights();\r
5023                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5024                   }\r
5025           } else\r
5026           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5027                   SetHighlights(fromX, fromY, toX, toY);\r
5028                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5029                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5030                      If promotion to Q is legal, all are legal! */\r
5031                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5032                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5033                     // kludge to temporarily execute move on display, without promoting yet\r
5034                     promotionChoice = TRUE;\r
5035                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5036                     boards[currentMove][toY][toX] = p;\r
5037                     DrawPosition(FALSE, boards[currentMove]);\r
5038                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5039                     boards[currentMove][toY][toX] = q;\r
5040                     DisplayMessage("Select piece from holdings", "");\r
5041                   } else\r
5042                   PromotionPopup(hwnd);\r
5043                   goto noClear;\r
5044           } else { // not a promotion. Move can be illegal if testLegality off, and should be made then.\r
5045              if (appData.animate || appData.highlightLastMove) {\r
5046                  SetHighlights(fromX, fromY, toX, toY);\r
5047              } else {\r
5048                  ClearHighlights();\r
5049              }\r
5050              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5051              if (appData.animate && !appData.highlightLastMove) {\r
5052                   ClearHighlights();\r
5053                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5054              }\r
5055           }\r
5056           fromX = fromY = -1;\r
5057         noClear:\r
5058           break;\r
5059         }\r
5060         if (gotPremove && moveType != Comment) {\r
5061             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5062 //            DrawPosition(forceFullRepaint || FALSE, NULL);\r
5063         } else ClearHighlights();\r
5064         fromX = fromY = -1;\r
5065         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5066         if(moveType != Comment) break;\r
5067     }\r
5068     /* First downclick, or restart on a square with same color piece */\r
5069     if (!frozen && OKToStartUserMove(x, y)) {\r
5070       fromX = x;\r
5071       fromY = y;\r
5072       dragInfo.lastpos = pt;\r
5073       dragInfo.from.x = fromX;\r
5074       dragInfo.from.y = fromY;\r
5075       dragInfo.start = dragInfo.from;\r
5076       SetCapture(hwndMain);\r
5077     } else {\r
5078       fromX = fromY = -1;\r
5079       dragInfo.start.x = dragInfo.start.y = -1;\r
5080       dragInfo.from = dragInfo.start;\r
5081       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5082     }\r
5083     break;\r
5084 \r
5085   case WM_LBUTTONUP:\r
5086     ReleaseCapture();\r
5087     if (fromX == -1) break;\r
5088     if (x == fromX && y == fromY) {\r
5089       dragInfo.from.x = dragInfo.from.y = -1;\r
5090       /* Upclick on same square */\r
5091       if (sameAgain) {\r
5092         /* Clicked same square twice: abort click-click move */\r
5093         fromX = fromY = -1;\r
5094         gotPremove = 0;\r
5095         ClearPremoveHighlights();\r
5096       } else {\r
5097         /* First square clicked: start click-click move */\r
5098         SetHighlights(fromX, fromY, -1, -1);\r
5099       }\r
5100       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5101     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5102       /* Errant click; ignore */\r
5103       break;\r
5104     } else {\r
5105       /* Finish drag move. */\r
5106     if (appData.debugMode) {\r
5107         fprintf(debugFP, "release\n");\r
5108     }\r
5109       dragInfo.from.x = dragInfo.from.y = -1;\r
5110       toX = x;\r
5111       toY = y;\r
5112       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5113       appData.animate = appData.animate && !appData.animateDragging;\r
5114       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR, TRUE);\r
5115       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5116                 fromX = fromY = -1; \r
5117                 ClearHighlights();\r
5118                 DrawPosition(FALSE, boards[currentMove]);\r
5119                 appData.animate = saveAnimate;\r
5120                 break; \r
5121       } else \r
5122       if(moveType != ImpossibleMove) {\r
5123           /* [HGM] use move type to determine if move is promotion.\r
5124              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5125           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5126             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5127               appData.alwaysPromoteToQueen)) \r
5128                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5129           else \r
5130           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5131                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5132                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5133                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5134                     // kludge to temporarily execute move on display, wthout promotng yet\r
5135                     promotionChoice = TRUE;\r
5136                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5137                     boards[currentMove][toY][toX] = p;\r
5138                     DrawPosition(FALSE, boards[currentMove]);\r
5139                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5140                     boards[currentMove][toY][toX] = q;\r
5141                     appData.animate = saveAnimate;\r
5142                     DisplayMessage("Select piece from holdings", "");\r
5143                     break;\r
5144                   } else\r
5145                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5146           } else {\r
5147             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5148                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5149                                         moveType == WhiteCapturesEnPassant || \r
5150                                         moveType == BlackCapturesEnPassant   ) )\r
5151                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5152             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5153           }\r
5154       }\r
5155       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5156       appData.animate = saveAnimate;\r
5157       fromX = fromY = -1;\r
5158       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5159         ClearHighlights();\r
5160       }\r
5161       if (appData.animate || appData.animateDragging ||\r
5162           appData.highlightDragging || gotPremove) {\r
5163         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5164       }\r
5165     }\r
5166     dragInfo.start.x = dragInfo.start.y = -1; \r
5167     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5168     break;\r
5169 \r
5170   case WM_MOUSEMOVE:\r
5171     if ((appData.animateDragging || appData.highlightDragging)\r
5172         && (wParam & MK_LBUTTON)\r
5173         && dragInfo.from.x >= 0) \r
5174     {\r
5175       BOOL full_repaint = FALSE;\r
5176 \r
5177       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5178       if (appData.animateDragging) {\r
5179         dragInfo.pos = pt;\r
5180       }\r
5181       if (appData.highlightDragging) {\r
5182         SetHighlights(fromX, fromY, x, y);\r
5183         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5184             full_repaint = TRUE;\r
5185         }\r
5186       }\r
5187       \r
5188       DrawPosition( full_repaint, NULL);\r
5189       \r
5190       dragInfo.lastpos = dragInfo.pos;\r
5191     }\r
5192     break;\r
5193 \r
5194   case WM_MOUSEWHEEL: // [DM]\r
5195     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5196        /* Mouse Wheel is being rolled forward\r
5197         * Play moves forward\r
5198         */\r
5199        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5200                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5201        /* Mouse Wheel is being rolled backward\r
5202         * Play moves backward\r
5203         */\r
5204        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5205                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5206     }\r
5207     break;\r
5208 \r
5209   case WM_MBUTTONDOWN:\r
5210   case WM_RBUTTONDOWN:\r
5211     ErrorPopDown();\r
5212     ReleaseCapture();\r
5213     fromX = fromY = -1;\r
5214     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5215     dragInfo.start.x = dragInfo.start.y = -1;\r
5216     dragInfo.from = dragInfo.start;\r
5217     dragInfo.lastpos = dragInfo.pos;\r
5218     if (appData.highlightDragging) {\r
5219       ClearHighlights();\r
5220     }\r
5221     if(y == -2) {\r
5222       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5223       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5224           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5225       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5226           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5227       }\r
5228     }\r
5229     DrawPosition(TRUE, NULL);\r
5230 \r
5231     switch (gameMode) {\r
5232     case EditPosition:\r
5233     case IcsExamining:\r
5234       if (x < 0 || y < 0) break;\r
5235       fromX = x;\r
5236       fromY = y;\r
5237       if (message == WM_MBUTTONDOWN) {\r
5238         buttonCount = 3;  /* even if system didn't think so */\r
5239         if (wParam & MK_SHIFT) \r
5240           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5241         else\r
5242           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5243       } else { /* message == WM_RBUTTONDOWN */\r
5244         /* Just have one menu, on the right button.  Windows users don't\r
5245            think to try the middle one, and sometimes other software steals\r
5246            it, or it doesn't really exist. */\r
5247         if(gameInfo.variant != VariantShogi)\r
5248             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5249         else\r
5250             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5251       }\r
5252       break;\r
5253     case IcsPlayingWhite:\r
5254     case IcsPlayingBlack:\r
5255     case EditGame:\r
5256     case MachinePlaysWhite:\r
5257     case MachinePlaysBlack:\r
5258       if (appData.testLegality &&\r
5259           gameInfo.variant != VariantBughouse &&\r
5260           gameInfo.variant != VariantCrazyhouse) break;\r
5261       if (x < 0 || y < 0) break;\r
5262       fromX = x;\r
5263       fromY = y;\r
5264       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5265       SetupDropMenu(hmenu);\r
5266       MenuPopup(hwnd, pt, hmenu, -1);\r
5267       break;\r
5268     default:\r
5269       break;\r
5270     }\r
5271     break;\r
5272   }\r
5273 \r
5274   recursive--;\r
5275 }\r
5276 \r
5277 /* Preprocess messages for buttons in main window */\r
5278 LRESULT CALLBACK\r
5279 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5280 {\r
5281   int id = GetWindowLong(hwnd, GWL_ID);\r
5282   int i, dir;\r
5283 \r
5284   for (i=0; i<N_BUTTONS; i++) {\r
5285     if (buttonDesc[i].id == id) break;\r
5286   }\r
5287   if (i == N_BUTTONS) return 0;\r
5288   switch (message) {\r
5289   case WM_KEYDOWN:\r
5290     switch (wParam) {\r
5291     case VK_LEFT:\r
5292     case VK_RIGHT:\r
5293       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5294       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5295       return TRUE;\r
5296     }\r
5297     break;\r
5298   case WM_CHAR:\r
5299     switch (wParam) {\r
5300     case '\r':\r
5301       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5302       return TRUE;\r
5303     default:\r
5304       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5305         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5306         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5307         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5308         SetFocus(h);\r
5309         SendMessage(h, WM_CHAR, wParam, lParam);\r
5310         return TRUE;\r
5311       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5312         PopUpMoveDialog((char)wParam);\r
5313       }\r
5314       break;\r
5315     }\r
5316     break;\r
5317   }\r
5318   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5319 }\r
5320 \r
5321 /* Process messages for Promotion dialog box */\r
5322 LRESULT CALLBACK\r
5323 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5324 {\r
5325   char promoChar;\r
5326 \r
5327   switch (message) {\r
5328   case WM_INITDIALOG: /* message: initialize dialog box */\r
5329     /* Center the dialog over the application window */\r
5330     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5331     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5332       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5333        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5334                SW_SHOW : SW_HIDE);\r
5335     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5336     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5337        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5338          PieceToChar(WhiteAngel) != '~') ||\r
5339         (PieceToChar(BlackAngel) >= 'A' &&\r
5340          PieceToChar(BlackAngel) != '~')   ) ?\r
5341                SW_SHOW : SW_HIDE);\r
5342     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5343        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5344          PieceToChar(WhiteMarshall) != '~') ||\r
5345         (PieceToChar(BlackMarshall) >= 'A' &&\r
5346          PieceToChar(BlackMarshall) != '~')   ) ?\r
5347                SW_SHOW : SW_HIDE);\r
5348     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5349     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5350        gameInfo.variant != VariantShogi ?\r
5351                SW_SHOW : SW_HIDE);\r
5352     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5353        gameInfo.variant != VariantShogi ?\r
5354                SW_SHOW : SW_HIDE);\r
5355     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5356        gameInfo.variant == VariantShogi ?\r
5357                SW_SHOW : SW_HIDE);\r
5358     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5359        gameInfo.variant == VariantShogi ?\r
5360                SW_SHOW : SW_HIDE);\r
5361     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5362        gameInfo.variant == VariantSuper ?\r
5363                SW_SHOW : SW_HIDE);\r
5364     return TRUE;\r
5365 \r
5366   case WM_COMMAND: /* message: received a command */\r
5367     switch (LOWORD(wParam)) {\r
5368     case IDCANCEL:\r
5369       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5370       ClearHighlights();\r
5371       DrawPosition(FALSE, NULL);\r
5372       return TRUE;\r
5373     case PB_King:\r
5374       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5375       break;\r
5376     case PB_Queen:\r
5377       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5378       break;\r
5379     case PB_Rook:\r
5380       promoChar = PieceToChar(BlackRook);\r
5381       break;\r
5382     case PB_Bishop:\r
5383       promoChar = PieceToChar(BlackBishop);\r
5384       break;\r
5385     case PB_Chancellor:\r
5386       promoChar = PieceToChar(BlackMarshall);\r
5387       break;\r
5388     case PB_Archbishop:\r
5389       promoChar = PieceToChar(BlackAngel);\r
5390       break;\r
5391     case PB_Knight:\r
5392       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5393       break;\r
5394     default:\r
5395       return FALSE;\r
5396     }\r
5397     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5398     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5399        only show the popup when we are already sure the move is valid or\r
5400        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5401        will figure out it is a promotion from the promoChar. */\r
5402     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5403     if (!appData.highlightLastMove) {\r
5404       ClearHighlights();\r
5405       DrawPosition(FALSE, NULL);\r
5406     }\r
5407     return TRUE;\r
5408   }\r
5409   return FALSE;\r
5410 }\r
5411 \r
5412 /* Pop up promotion dialog */\r
5413 VOID\r
5414 PromotionPopup(HWND hwnd)\r
5415 {\r
5416   FARPROC lpProc;\r
5417 \r
5418   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5419   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5420     hwnd, (DLGPROC)lpProc);\r
5421   FreeProcInstance(lpProc);\r
5422 }\r
5423 \r
5424 /* Toggle ShowThinking */\r
5425 VOID\r
5426 ToggleShowThinking()\r
5427 {\r
5428   appData.showThinking = !appData.showThinking;\r
5429   ShowThinkingEvent();\r
5430 }\r
5431 \r
5432 VOID\r
5433 LoadGameDialog(HWND hwnd, char* title)\r
5434 {\r
5435   UINT number = 0;\r
5436   FILE *f;\r
5437   char fileTitle[MSG_SIZ];\r
5438   f = OpenFileDialog(hwnd, "rb", "",\r
5439                      appData.oldSaveStyle ? "gam" : "pgn",\r
5440                      GAME_FILT,\r
5441                      title, &number, fileTitle, NULL);\r
5442   if (f != NULL) {\r
5443     cmailMsgLoaded = FALSE;\r
5444     if (number == 0) {\r
5445       int error = GameListBuild(f);\r
5446       if (error) {\r
5447         DisplayError("Cannot build game list", error);\r
5448       } else if (!ListEmpty(&gameList) &&\r
5449                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5450         GameListPopUp(f, fileTitle);\r
5451         return;\r
5452       }\r
5453       GameListDestroy();\r
5454       number = 1;\r
5455     }\r
5456     LoadGame(f, number, fileTitle, FALSE);\r
5457   }\r
5458 }\r
5459 \r
5460 void UpdateICSWidth(HWND hText)\r
5461 {\r
5462         HDC hdc;\r
5463         TEXTMETRIC tm;\r
5464         RECT rc;\r
5465         HFONT hfont, hold_font;\r
5466         LONG old_width, new_width;\r
5467         \r
5468         // get the text metrics\r
5469         hdc = GetDC(hText);\r
5470         hfont = CreateFontIndirect(&font[boardSize][CONSOLE_FONT]->lf);\r
5471         hold_font = SelectObject(hdc, hfont);\r
5472         GetTextMetrics(hdc, &tm);\r
5473         SelectObject(hdc, hold_font);\r
5474         DeleteObject(hfont);\r
5475         ReleaseDC(hText, hdc);\r
5476 \r
5477         // get the rectangle\r
5478         SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
5479 \r
5480         // update the width\r
5481         new_width = (rc.right-rc.left) / tm.tmAveCharWidth;\r
5482         old_width = GetWindowLong(hText, GWL_USERDATA);\r
5483         if (new_width != old_width)\r
5484         {\r
5485                 ics_update_width(new_width);\r
5486                 SetWindowLong(hText, GWL_USERDATA, new_width);\r
5487         }\r
5488 }\r
5489 \r
5490 VOID\r
5491 ChangedConsoleFont()\r
5492 {\r
5493   CHARFORMAT cfmt;\r
5494   CHARRANGE tmpsel, sel;\r
5495   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5496   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5497   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5498   PARAFORMAT paraf;\r
5499 \r
5500   cfmt.cbSize = sizeof(CHARFORMAT);\r
5501   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5502   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5503   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5504    * size.  This was undocumented in the version of MSVC++ that I had\r
5505    * when I wrote the code, but is apparently documented now.\r
5506    */\r
5507   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5508   cfmt.bCharSet = f->lf.lfCharSet;\r
5509   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5510   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5511   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5512   /* Why are the following seemingly needed too? */\r
5513   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5514   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5515   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5516   tmpsel.cpMin = 0;\r
5517   tmpsel.cpMax = -1; /*999999?*/\r
5518   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5519   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5520   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5521    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5522    */\r
5523   paraf.cbSize = sizeof(paraf);\r
5524   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5525   paraf.dxStartIndent = 0;\r
5526   paraf.dxOffset = WRAP_INDENT;\r
5527   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5528   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5529   UpdateICSWidth(hText);\r
5530 }\r
5531 \r
5532 /*---------------------------------------------------------------------------*\\r
5533  *\r
5534  * Window Proc for main window\r
5535  *\r
5536 \*---------------------------------------------------------------------------*/\r
5537 \r
5538 /* Process messages for main window, etc. */\r
5539 LRESULT CALLBACK\r
5540 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5541 {\r
5542   FARPROC lpProc;\r
5543   int wmId, wmEvent;\r
5544   char *defName;\r
5545   FILE *f;\r
5546   UINT number;\r
5547   char fileTitle[MSG_SIZ];\r
5548   char buf[MSG_SIZ];\r
5549   static SnapData sd;\r
5550 \r
5551   switch (message) {\r
5552 \r
5553   case WM_PAINT: /* message: repaint portion of window */\r
5554     PaintProc(hwnd);\r
5555     break;\r
5556 \r
5557   case WM_ERASEBKGND:\r
5558     if (IsIconic(hwnd)) {\r
5559       /* Cheat; change the message */\r
5560       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5561     } else {\r
5562       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5563     }\r
5564     break;\r
5565 \r
5566   case WM_LBUTTONDOWN:\r
5567   case WM_MBUTTONDOWN:\r
5568   case WM_RBUTTONDOWN:\r
5569   case WM_LBUTTONUP:\r
5570   case WM_MBUTTONUP:\r
5571   case WM_RBUTTONUP:\r
5572   case WM_MOUSEMOVE:\r
5573   case WM_MOUSEWHEEL:\r
5574     MouseEvent(hwnd, message, wParam, lParam);\r
5575     break;\r
5576 \r
5577   JAWS_KB_NAVIGATION\r
5578 \r
5579   case WM_CHAR:\r
5580     \r
5581     JAWS_ALT_INTERCEPT\r
5582 \r
5583     if (appData.icsActive && (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9')) { \r
5584         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5585         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5586         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5587         SetFocus(h);\r
5588         SendMessage(h, message, wParam, lParam);\r
5589     } else if(lParam != KF_REPEAT) {\r
5590         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5591                 PopUpMoveDialog((char)wParam);\r
5592         } else if((char)wParam == 003) CopyGameToClipboard();\r
5593          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5594     }\r
5595 \r
5596     break;\r
5597 \r
5598   case WM_PALETTECHANGED:\r
5599     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5600       int nnew;\r
5601       HDC hdc = GetDC(hwndMain);\r
5602       SelectPalette(hdc, hPal, TRUE);\r
5603       nnew = RealizePalette(hdc);\r
5604       if (nnew > 0) {\r
5605         paletteChanged = TRUE;\r
5606         InvalidateRect(hwnd, &boardRect, FALSE);\r
5607       }\r
5608       ReleaseDC(hwnd, hdc);\r
5609     }\r
5610     break;\r
5611 \r
5612   case WM_QUERYNEWPALETTE:\r
5613     if (!appData.monoMode /*&& paletteChanged*/) {\r
5614       int nnew;\r
5615       HDC hdc = GetDC(hwndMain);\r
5616       paletteChanged = FALSE;\r
5617       SelectPalette(hdc, hPal, FALSE);\r
5618       nnew = RealizePalette(hdc);\r
5619       if (nnew > 0) {\r
5620         InvalidateRect(hwnd, &boardRect, FALSE);\r
5621       }\r
5622       ReleaseDC(hwnd, hdc);\r
5623       return TRUE;\r
5624     }\r
5625     return FALSE;\r
5626 \r
5627   case WM_COMMAND: /* message: command from application menu */\r
5628     wmId    = LOWORD(wParam);\r
5629     wmEvent = HIWORD(wParam);\r
5630 \r
5631     switch (wmId) {\r
5632     case IDM_NewGame:\r
5633       ResetGameEvent();\r
5634       AnalysisPopDown();\r
5635       SAY("new game enter a move to play against the computer with white");\r
5636       break;\r
5637 \r
5638     case IDM_NewGameFRC:\r
5639       if( NewGameFRC() == 0 ) {\r
5640         ResetGameEvent();\r
5641         AnalysisPopDown();\r
5642       }\r
5643       break;\r
5644 \r
5645     case IDM_NewVariant:\r
5646       NewVariantPopup(hwnd);\r
5647       break;\r
5648 \r
5649     case IDM_LoadGame:\r
5650       LoadGameDialog(hwnd, "Load Game from File");\r
5651       break;\r
5652 \r
5653     case IDM_LoadNextGame:\r
5654       ReloadGame(1);\r
5655       break;\r
5656 \r
5657     case IDM_LoadPrevGame:\r
5658       ReloadGame(-1);\r
5659       break;\r
5660 \r
5661     case IDM_ReloadGame:\r
5662       ReloadGame(0);\r
5663       break;\r
5664 \r
5665     case IDM_LoadPosition:\r
5666       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5667         Reset(FALSE, TRUE);\r
5668       }\r
5669       number = 1;\r
5670       f = OpenFileDialog(hwnd, "rb", "",\r
5671                          appData.oldSaveStyle ? "pos" : "fen",\r
5672                          POSITION_FILT,\r
5673                          "Load Position from File", &number, fileTitle, NULL);\r
5674       if (f != NULL) {\r
5675         LoadPosition(f, number, fileTitle);\r
5676       }\r
5677       break;\r
5678 \r
5679     case IDM_LoadNextPosition:\r
5680       ReloadPosition(1);\r
5681       break;\r
5682 \r
5683     case IDM_LoadPrevPosition:\r
5684       ReloadPosition(-1);\r
5685       break;\r
5686 \r
5687     case IDM_ReloadPosition:\r
5688       ReloadPosition(0);\r
5689       break;\r
5690 \r
5691     case IDM_SaveGame:\r
5692       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5693       f = OpenFileDialog(hwnd, "a", defName,\r
5694                          appData.oldSaveStyle ? "gam" : "pgn",\r
5695                          GAME_FILT,\r
5696                          "Save Game to File", NULL, fileTitle, NULL);\r
5697       if (f != NULL) {\r
5698         SaveGame(f, 0, "");\r
5699       }\r
5700       break;\r
5701 \r
5702     case IDM_SavePosition:\r
5703       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5704       f = OpenFileDialog(hwnd, "a", defName,\r
5705                          appData.oldSaveStyle ? "pos" : "fen",\r
5706                          POSITION_FILT,\r
5707                          "Save Position to File", NULL, fileTitle, NULL);\r
5708       if (f != NULL) {\r
5709         SavePosition(f, 0, "");\r
5710       }\r
5711       break;\r
5712 \r
5713     case IDM_SaveDiagram:\r
5714       defName = "diagram";\r
5715       f = OpenFileDialog(hwnd, "wb", defName,\r
5716                          "bmp",\r
5717                          DIAGRAM_FILT,\r
5718                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5719       if (f != NULL) {\r
5720         SaveDiagram(f);\r
5721       }\r
5722       break;\r
5723 \r
5724     case IDM_CopyGame:\r
5725       CopyGameToClipboard();\r
5726       break;\r
5727 \r
5728     case IDM_PasteGame:\r
5729       PasteGameFromClipboard();\r
5730       break;\r
5731 \r
5732     case IDM_CopyGameListToClipboard:\r
5733       CopyGameListToClipboard();\r
5734       break;\r
5735 \r
5736     /* [AS] Autodetect FEN or PGN data */\r
5737     case IDM_PasteAny:\r
5738       PasteGameOrFENFromClipboard();\r
5739       break;\r
5740 \r
5741     /* [AS] Move history */\r
5742     case IDM_ShowMoveHistory:\r
5743         if( MoveHistoryIsUp() ) {\r
5744             MoveHistoryPopDown();\r
5745         }\r
5746         else {\r
5747             MoveHistoryPopUp();\r
5748         }\r
5749         break;\r
5750 \r
5751     /* [AS] Eval graph */\r
5752     case IDM_ShowEvalGraph:\r
5753         if( EvalGraphIsUp() ) {\r
5754             EvalGraphPopDown();\r
5755         }\r
5756         else {\r
5757             EvalGraphPopUp();\r
5758             SetFocus(hwndMain);\r
5759         }\r
5760         break;\r
5761 \r
5762     /* [AS] Engine output */\r
5763     case IDM_ShowEngineOutput:\r
5764         if( EngineOutputIsUp() ) {\r
5765             EngineOutputPopDown();\r
5766         }\r
5767         else {\r
5768             EngineOutputPopUp();\r
5769         }\r
5770         break;\r
5771 \r
5772     /* [AS] User adjudication */\r
5773     case IDM_UserAdjudication_White:\r
5774         UserAdjudicationEvent( +1 );\r
5775         break;\r
5776 \r
5777     case IDM_UserAdjudication_Black:\r
5778         UserAdjudicationEvent( -1 );\r
5779         break;\r
5780 \r
5781     case IDM_UserAdjudication_Draw:\r
5782         UserAdjudicationEvent( 0 );\r
5783         break;\r
5784 \r
5785     /* [AS] Game list options dialog */\r
5786     case IDM_GameListOptions:\r
5787       GameListOptions();\r
5788       break;\r
5789 \r
5790     case IDM_NewChat:\r
5791       ChatPopUp();\r
5792       break;\r
5793 \r
5794     case IDM_CopyPosition:\r
5795       CopyFENToClipboard();\r
5796       break;\r
5797 \r
5798     case IDM_PastePosition:\r
5799       PasteFENFromClipboard();\r
5800       break;\r
5801 \r
5802     case IDM_MailMove:\r
5803       MailMoveEvent();\r
5804       break;\r
5805 \r
5806     case IDM_ReloadCMailMsg:\r
5807       Reset(TRUE, TRUE);\r
5808       ReloadCmailMsgEvent(FALSE);\r
5809       break;\r
5810 \r
5811     case IDM_Minimize:\r
5812       ShowWindow(hwnd, SW_MINIMIZE);\r
5813       break;\r
5814 \r
5815     case IDM_Exit:\r
5816       ExitEvent(0);\r
5817       break;\r
5818 \r
5819     case IDM_MachineWhite:\r
5820       MachineWhiteEvent();\r
5821       /*\r
5822        * refresh the tags dialog only if it's visible\r
5823        */\r
5824       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5825           char *tags;\r
5826           tags = PGNTags(&gameInfo);\r
5827           TagsPopUp(tags, CmailMsg());\r
5828           free(tags);\r
5829       }\r
5830       SAY("computer starts playing white");\r
5831       break;\r
5832 \r
5833     case IDM_MachineBlack:\r
5834       MachineBlackEvent();\r
5835       /*\r
5836        * refresh the tags dialog only if it's visible\r
5837        */\r
5838       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5839           char *tags;\r
5840           tags = PGNTags(&gameInfo);\r
5841           TagsPopUp(tags, CmailMsg());\r
5842           free(tags);\r
5843       }\r
5844       SAY("computer starts playing black");\r
5845       break;\r
5846 \r
5847     case IDM_TwoMachines:\r
5848       TwoMachinesEvent();\r
5849       /*\r
5850        * refresh the tags dialog only if it's visible\r
5851        */\r
5852       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5853           char *tags;\r
5854           tags = PGNTags(&gameInfo);\r
5855           TagsPopUp(tags, CmailMsg());\r
5856           free(tags);\r
5857       }\r
5858       SAY("programs start playing each other");\r
5859       break;\r
5860 \r
5861     case IDM_AnalysisMode:\r
5862       if (!first.analysisSupport) {\r
5863         sprintf(buf, "%s does not support analysis", first.tidy);\r
5864         DisplayError(buf, 0);\r
5865       } else {\r
5866         SAY("analyzing current position");\r
5867         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5868         if (appData.icsActive) {\r
5869                if (gameMode != IcsObserving) {\r
5870                        sprintf(buf, "You are not observing a game");\r
5871                        DisplayError(buf, 0);\r
5872                        /* secure check */\r
5873                        if (appData.icsEngineAnalyze) {\r
5874                                if (appData.debugMode) \r
5875                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5876                                ExitAnalyzeMode();\r
5877                                ModeHighlight();\r
5878                                break;\r
5879                        }\r
5880                        break;\r
5881                } else {\r
5882                        /* if enable, user want disable icsEngineAnalyze */\r
5883                        if (appData.icsEngineAnalyze) {\r
5884                                ExitAnalyzeMode();\r
5885                                ModeHighlight();\r
5886                                break;\r
5887                        }\r
5888                        appData.icsEngineAnalyze = TRUE;\r
5889                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5890                }\r
5891         } \r
5892         if (!appData.showThinking) ToggleShowThinking();\r
5893         AnalyzeModeEvent();\r
5894       }\r
5895       break;\r
5896 \r
5897     case IDM_AnalyzeFile:\r
5898       if (!first.analysisSupport) {\r
5899         char buf[MSG_SIZ];\r
5900         sprintf(buf, "%s does not support analysis", first.tidy);\r
5901         DisplayError(buf, 0);\r
5902       } else {\r
5903         if (!appData.showThinking) ToggleShowThinking();\r
5904         AnalyzeFileEvent();\r
5905         LoadGameDialog(hwnd, "Analyze Game from File");\r
5906         AnalysisPeriodicEvent(1);\r
5907       }\r
5908       break;\r
5909 \r
5910     case IDM_IcsClient:\r
5911       IcsClientEvent();\r
5912       break;\r
5913 \r
5914     case IDM_EditGame:\r
5915       EditGameEvent();\r
5916       SAY("edit game");\r
5917       break;\r
5918 \r
5919     case IDM_EditPosition:\r
5920       EditPositionEvent();\r
5921       SAY("to set up a position type a FEN");\r
5922       break;\r
5923 \r
5924     case IDM_Training:\r
5925       TrainingEvent();\r
5926       break;\r
5927 \r
5928     case IDM_ShowGameList:\r
5929       ShowGameListProc();\r
5930       break;\r
5931 \r
5932     case IDM_EditTags:\r
5933       EditTagsProc();\r
5934       break;\r
5935 \r
5936     case IDM_EditComment:\r
5937       if (commentDialogUp && editComment) {\r
5938         CommentPopDown();\r
5939       } else {\r
5940         EditCommentEvent();\r
5941       }\r
5942       break;\r
5943 \r
5944     case IDM_Pause:\r
5945       PauseEvent();\r
5946       break;\r
5947 \r
5948     case IDM_Accept:\r
5949       AcceptEvent();\r
5950       break;\r
5951 \r
5952     case IDM_Decline:\r
5953       DeclineEvent();\r
5954       break;\r
5955 \r
5956     case IDM_Rematch:\r
5957       RematchEvent();\r
5958       break;\r
5959 \r
5960     case IDM_CallFlag:\r
5961       CallFlagEvent();\r
5962       break;\r
5963 \r
5964     case IDM_Draw:\r
5965       DrawEvent();\r
5966       break;\r
5967 \r
5968     case IDM_Adjourn:\r
5969       AdjournEvent();\r
5970       break;\r
5971 \r
5972     case IDM_Abort:\r
5973       AbortEvent();\r
5974       break;\r
5975 \r
5976     case IDM_Resign:\r
5977       ResignEvent();\r
5978       break;\r
5979 \r
5980     case IDM_StopObserving:\r
5981       StopObservingEvent();\r
5982       break;\r
5983 \r
5984     case IDM_StopExamining:\r
5985       StopExaminingEvent();\r
5986       break;\r
5987 \r
5988     case IDM_TypeInMove:\r
5989       PopUpMoveDialog('\000');\r
5990       break;\r
5991 \r
5992     case IDM_TypeInName:\r
5993       PopUpNameDialog('\000');\r
5994       break;\r
5995 \r
5996     case IDM_Backward:\r
5997       BackwardEvent();\r
5998       SetFocus(hwndMain);\r
5999       break;\r
6000 \r
6001     JAWS_MENU_ITEMS\r
6002 \r
6003     case IDM_Forward:\r
6004       ForwardEvent();\r
6005       SetFocus(hwndMain);\r
6006       break;\r
6007 \r
6008     case IDM_ToStart:\r
6009       ToStartEvent();\r
6010       SetFocus(hwndMain);\r
6011       break;\r
6012 \r
6013     case IDM_ToEnd:\r
6014       ToEndEvent();\r
6015       SetFocus(hwndMain);\r
6016       break;\r
6017 \r
6018     case IDM_Revert:\r
6019       RevertEvent();\r
6020       break;\r
6021 \r
6022     case IDM_TruncateGame:\r
6023       TruncateGameEvent();\r
6024       break;\r
6025 \r
6026     case IDM_MoveNow:\r
6027       MoveNowEvent();\r
6028       break;\r
6029 \r
6030     case IDM_RetractMove:\r
6031       RetractMoveEvent();\r
6032       break;\r
6033 \r
6034     case IDM_FlipView:\r
6035       flipView = !flipView;\r
6036       DrawPosition(FALSE, NULL);\r
6037       break;\r
6038 \r
6039     case IDM_FlipClock:\r
6040       flipClock = !flipClock;\r
6041       DisplayBothClocks();\r
6042       DrawPosition(FALSE, NULL);\r
6043       break;\r
6044 \r
6045     case IDM_MuteSounds:\r
6046       mute = !mute; // [HGM] mute: keep track of global muting variable\r
6047       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
6048                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
6049       break;\r
6050 \r
6051     case IDM_GeneralOptions:\r
6052       GeneralOptionsPopup(hwnd);\r
6053       DrawPosition(TRUE, NULL);\r
6054       break;\r
6055 \r
6056     case IDM_BoardOptions:\r
6057       BoardOptionsPopup(hwnd);\r
6058       break;\r
6059 \r
6060     case IDM_EnginePlayOptions:\r
6061       EnginePlayOptionsPopup(hwnd);\r
6062       break;\r
6063 \r
6064     case IDM_Engine1Options:\r
6065       EngineOptionsPopup(hwnd, &first);\r
6066       break;\r
6067 \r
6068     case IDM_Engine2Options:\r
6069       EngineOptionsPopup(hwnd, &second);\r
6070       break;\r
6071 \r
6072     case IDM_OptionsUCI:\r
6073       UciOptionsPopup(hwnd);\r
6074       break;\r
6075 \r
6076     case IDM_IcsOptions:\r
6077       IcsOptionsPopup(hwnd);\r
6078       break;\r
6079 \r
6080     case IDM_Fonts:\r
6081       FontsOptionsPopup(hwnd);\r
6082       break;\r
6083 \r
6084     case IDM_Sounds:\r
6085       SoundOptionsPopup(hwnd);\r
6086       break;\r
6087 \r
6088     case IDM_CommPort:\r
6089       CommPortOptionsPopup(hwnd);\r
6090       break;\r
6091 \r
6092     case IDM_LoadOptions:\r
6093       LoadOptionsPopup(hwnd);\r
6094       break;\r
6095 \r
6096     case IDM_SaveOptions:\r
6097       SaveOptionsPopup(hwnd);\r
6098       break;\r
6099 \r
6100     case IDM_TimeControl:\r
6101       TimeControlOptionsPopup(hwnd);\r
6102       break;\r
6103 \r
6104     case IDM_SaveSettings:\r
6105       SaveSettings(settingsFileName);\r
6106       break;\r
6107 \r
6108     case IDM_SaveSettingsOnExit:\r
6109       saveSettingsOnExit = !saveSettingsOnExit;\r
6110       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6111                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6112                                          MF_CHECKED : MF_UNCHECKED));\r
6113       break;\r
6114 \r
6115     case IDM_Hint:\r
6116       HintEvent();\r
6117       break;\r
6118 \r
6119     case IDM_Book:\r
6120       BookEvent();\r
6121       break;\r
6122 \r
6123     case IDM_AboutGame:\r
6124       AboutGameEvent();\r
6125       break;\r
6126 \r
6127     case IDM_Debug:\r
6128       appData.debugMode = !appData.debugMode;\r
6129       if (appData.debugMode) {\r
6130         char dir[MSG_SIZ];\r
6131         GetCurrentDirectory(MSG_SIZ, dir);\r
6132         SetCurrentDirectory(installDir);\r
6133         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6134         SetCurrentDirectory(dir);\r
6135         setbuf(debugFP, NULL);\r
6136       } else {\r
6137         fclose(debugFP);\r
6138         debugFP = NULL;\r
6139       }\r
6140       break;\r
6141 \r
6142     case IDM_HELPCONTENTS:\r
6143       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6144           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6145           MessageBox (GetFocus(),\r
6146                     "Unable to activate help",\r
6147                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6148       }\r
6149       break;\r
6150 \r
6151     case IDM_HELPSEARCH:\r
6152         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6153             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6154         MessageBox (GetFocus(),\r
6155                     "Unable to activate help",\r
6156                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6157       }\r
6158       break;\r
6159 \r
6160     case IDM_HELPHELP:\r
6161       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6162         MessageBox (GetFocus(),\r
6163                     "Unable to activate help",\r
6164                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6165       }\r
6166       break;\r
6167 \r
6168     case IDM_ABOUT:\r
6169       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6170       DialogBox(hInst, \r
6171         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6172         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6173       FreeProcInstance(lpProc);\r
6174       break;\r
6175 \r
6176     case IDM_DirectCommand1:\r
6177       AskQuestionEvent("Direct Command",\r
6178                        "Send to chess program:", "", "1");\r
6179       break;\r
6180     case IDM_DirectCommand2:\r
6181       AskQuestionEvent("Direct Command",\r
6182                        "Send to second chess program:", "", "2");\r
6183       break;\r
6184 \r
6185     case EP_WhitePawn:\r
6186       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6187       fromX = fromY = -1;\r
6188       break;\r
6189 \r
6190     case EP_WhiteKnight:\r
6191       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6192       fromX = fromY = -1;\r
6193       break;\r
6194 \r
6195     case EP_WhiteBishop:\r
6196       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6197       fromX = fromY = -1;\r
6198       break;\r
6199 \r
6200     case EP_WhiteRook:\r
6201       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6202       fromX = fromY = -1;\r
6203       break;\r
6204 \r
6205     case EP_WhiteQueen:\r
6206       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6207       fromX = fromY = -1;\r
6208       break;\r
6209 \r
6210     case EP_WhiteFerz:\r
6211       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6212       fromX = fromY = -1;\r
6213       break;\r
6214 \r
6215     case EP_WhiteWazir:\r
6216       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6217       fromX = fromY = -1;\r
6218       break;\r
6219 \r
6220     case EP_WhiteAlfil:\r
6221       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6222       fromX = fromY = -1;\r
6223       break;\r
6224 \r
6225     case EP_WhiteCannon:\r
6226       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6227       fromX = fromY = -1;\r
6228       break;\r
6229 \r
6230     case EP_WhiteCardinal:\r
6231       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6232       fromX = fromY = -1;\r
6233       break;\r
6234 \r
6235     case EP_WhiteMarshall:\r
6236       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6237       fromX = fromY = -1;\r
6238       break;\r
6239 \r
6240     case EP_WhiteKing:\r
6241       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6242       fromX = fromY = -1;\r
6243       break;\r
6244 \r
6245     case EP_BlackPawn:\r
6246       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6247       fromX = fromY = -1;\r
6248       break;\r
6249 \r
6250     case EP_BlackKnight:\r
6251       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6252       fromX = fromY = -1;\r
6253       break;\r
6254 \r
6255     case EP_BlackBishop:\r
6256       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6257       fromX = fromY = -1;\r
6258       break;\r
6259 \r
6260     case EP_BlackRook:\r
6261       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6262       fromX = fromY = -1;\r
6263       break;\r
6264 \r
6265     case EP_BlackQueen:\r
6266       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6267       fromX = fromY = -1;\r
6268       break;\r
6269 \r
6270     case EP_BlackFerz:\r
6271       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6272       fromX = fromY = -1;\r
6273       break;\r
6274 \r
6275     case EP_BlackWazir:\r
6276       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6277       fromX = fromY = -1;\r
6278       break;\r
6279 \r
6280     case EP_BlackAlfil:\r
6281       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6282       fromX = fromY = -1;\r
6283       break;\r
6284 \r
6285     case EP_BlackCannon:\r
6286       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6287       fromX = fromY = -1;\r
6288       break;\r
6289 \r
6290     case EP_BlackCardinal:\r
6291       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6292       fromX = fromY = -1;\r
6293       break;\r
6294 \r
6295     case EP_BlackMarshall:\r
6296       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6297       fromX = fromY = -1;\r
6298       break;\r
6299 \r
6300     case EP_BlackKing:\r
6301       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6302       fromX = fromY = -1;\r
6303       break;\r
6304 \r
6305     case EP_EmptySquare:\r
6306       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6307       fromX = fromY = -1;\r
6308       break;\r
6309 \r
6310     case EP_ClearBoard:\r
6311       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6312       fromX = fromY = -1;\r
6313       break;\r
6314 \r
6315     case EP_White:\r
6316       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6317       fromX = fromY = -1;\r
6318       break;\r
6319 \r
6320     case EP_Black:\r
6321       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6322       fromX = fromY = -1;\r
6323       break;\r
6324 \r
6325     case EP_Promote:\r
6326       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6327       fromX = fromY = -1;\r
6328       break;\r
6329 \r
6330     case EP_Demote:\r
6331       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6332       fromX = fromY = -1;\r
6333       break;\r
6334 \r
6335     case DP_Pawn:\r
6336       DropMenuEvent(WhitePawn, fromX, fromY);\r
6337       fromX = fromY = -1;\r
6338       break;\r
6339 \r
6340     case DP_Knight:\r
6341       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6342       fromX = fromY = -1;\r
6343       break;\r
6344 \r
6345     case DP_Bishop:\r
6346       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6347       fromX = fromY = -1;\r
6348       break;\r
6349 \r
6350     case DP_Rook:\r
6351       DropMenuEvent(WhiteRook, fromX, fromY);\r
6352       fromX = fromY = -1;\r
6353       break;\r
6354 \r
6355     case DP_Queen:\r
6356       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6357       fromX = fromY = -1;\r
6358       break;\r
6359 \r
6360     default:\r
6361       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6362     }\r
6363     break;\r
6364 \r
6365   case WM_TIMER:\r
6366     switch (wParam) {\r
6367     case CLOCK_TIMER_ID:\r
6368       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6369       clockTimerEvent = 0;\r
6370       DecrementClocks(); /* call into back end */\r
6371       break;\r
6372     case LOAD_GAME_TIMER_ID:\r
6373       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6374       loadGameTimerEvent = 0;\r
6375       AutoPlayGameLoop(); /* call into back end */\r
6376       break;\r
6377     case ANALYSIS_TIMER_ID:\r
6378       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6379                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6380         AnalysisPeriodicEvent(0);\r
6381       } else {\r
6382         KillTimer(hwnd, analysisTimerEvent);\r
6383         analysisTimerEvent = 0;\r
6384       }\r
6385       break;\r
6386     case DELAYED_TIMER_ID:\r
6387       KillTimer(hwnd, delayedTimerEvent);\r
6388       delayedTimerEvent = 0;\r
6389       delayedTimerCallback();\r
6390       break;\r
6391     }\r
6392     break;\r
6393 \r
6394   case WM_USER_Input:\r
6395     InputEvent(hwnd, message, wParam, lParam);\r
6396     break;\r
6397 \r
6398   /* [AS] Also move "attached" child windows */\r
6399   case WM_WINDOWPOSCHANGING:\r
6400 \r
6401     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6402         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6403 \r
6404         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6405             /* Window is moving */\r
6406             RECT rcMain;\r
6407 \r
6408 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6409             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6410             rcMain.right  = boardX + winWidth;\r
6411             rcMain.top    = boardY;\r
6412             rcMain.bottom = boardY + winHeight;\r
6413             \r
6414             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6415             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6416             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6417             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6418             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6419             boardX = lpwp->x;\r
6420             boardY = lpwp->y;\r
6421         }\r
6422     }\r
6423     break;\r
6424 \r
6425   /* [AS] Snapping */\r
6426   case WM_ENTERSIZEMOVE:\r
6427     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6428     if (hwnd == hwndMain) {\r
6429       doingSizing = TRUE;\r
6430       lastSizing = 0;\r
6431     }\r
6432     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6433     break;\r
6434 \r
6435   case WM_SIZING:\r
6436     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6437     if (hwnd == hwndMain) {\r
6438       lastSizing = wParam;\r
6439     }\r
6440     break;\r
6441 \r
6442   case WM_MOVING:\r
6443     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6444       return OnMoving( &sd, hwnd, wParam, lParam );\r
6445 \r
6446   case WM_EXITSIZEMOVE:\r
6447     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6448     if (hwnd == hwndMain) {\r
6449       RECT client;\r
6450       doingSizing = FALSE;\r
6451       InvalidateRect(hwnd, &boardRect, FALSE);\r
6452       GetClientRect(hwnd, &client);\r
6453       ResizeBoard(client.right, client.bottom, lastSizing);\r
6454       lastSizing = 0;\r
6455       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6456     }\r
6457     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6458     break;\r
6459 \r
6460   case WM_DESTROY: /* message: window being destroyed */\r
6461     PostQuitMessage(0);\r
6462     break;\r
6463 \r
6464   case WM_CLOSE:\r
6465     if (hwnd == hwndMain) {\r
6466       ExitEvent(0);\r
6467     }\r
6468     break;\r
6469 \r
6470   default:      /* Passes it on if unprocessed */\r
6471     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6472   }\r
6473   return 0;\r
6474 }\r
6475 \r
6476 /*---------------------------------------------------------------------------*\\r
6477  *\r
6478  * Misc utility routines\r
6479  *\r
6480 \*---------------------------------------------------------------------------*/\r
6481 \r
6482 /*\r
6483  * Decent random number generator, at least not as bad as Windows\r
6484  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6485  */\r
6486 unsigned int randstate;\r
6487 \r
6488 int\r
6489 myrandom(void)\r
6490 {\r
6491   randstate = randstate * 1664525 + 1013904223;\r
6492   return (int) randstate & 0x7fffffff;\r
6493 }\r
6494 \r
6495 void\r
6496 mysrandom(unsigned int seed)\r
6497 {\r
6498   randstate = seed;\r
6499 }\r
6500 \r
6501 \r
6502 /* \r
6503  * returns TRUE if user selects a different color, FALSE otherwise \r
6504  */\r
6505 \r
6506 BOOL\r
6507 ChangeColor(HWND hwnd, COLORREF *which)\r
6508 {\r
6509   static BOOL firstTime = TRUE;\r
6510   static DWORD customColors[16];\r
6511   CHOOSECOLOR cc;\r
6512   COLORREF newcolor;\r
6513   int i;\r
6514   ColorClass ccl;\r
6515 \r
6516   if (firstTime) {\r
6517     /* Make initial colors in use available as custom colors */\r
6518     /* Should we put the compiled-in defaults here instead? */\r
6519     i = 0;\r
6520     customColors[i++] = lightSquareColor & 0xffffff;\r
6521     customColors[i++] = darkSquareColor & 0xffffff;\r
6522     customColors[i++] = whitePieceColor & 0xffffff;\r
6523     customColors[i++] = blackPieceColor & 0xffffff;\r
6524     customColors[i++] = highlightSquareColor & 0xffffff;\r
6525     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6526 \r
6527     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6528       customColors[i++] = textAttribs[ccl].color;\r
6529     }\r
6530     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6531     firstTime = FALSE;\r
6532   }\r
6533 \r
6534   cc.lStructSize = sizeof(cc);\r
6535   cc.hwndOwner = hwnd;\r
6536   cc.hInstance = NULL;\r
6537   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6538   cc.lpCustColors = (LPDWORD) customColors;\r
6539   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6540 \r
6541   if (!ChooseColor(&cc)) return FALSE;\r
6542 \r
6543   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6544   if (newcolor == *which) return FALSE;\r
6545   *which = newcolor;\r
6546   return TRUE;\r
6547 \r
6548   /*\r
6549   InitDrawingColors();\r
6550   InvalidateRect(hwnd, &boardRect, FALSE);\r
6551   */\r
6552 }\r
6553 \r
6554 BOOLEAN\r
6555 MyLoadSound(MySound *ms)\r
6556 {\r
6557   BOOL ok = FALSE;\r
6558   struct stat st;\r
6559   FILE *f;\r
6560 \r
6561   if (ms->data) free(ms->data);\r
6562   ms->data = NULL;\r
6563 \r
6564   switch (ms->name[0]) {\r
6565   case NULLCHAR:\r
6566     /* Silence */\r
6567     ok = TRUE;\r
6568     break;\r
6569   case '$':\r
6570     /* System sound from Control Panel.  Don't preload here. */\r
6571     ok = TRUE;\r
6572     break;\r
6573   case '!':\r
6574     if (ms->name[1] == NULLCHAR) {\r
6575       /* "!" alone = silence */\r
6576       ok = TRUE;\r
6577     } else {\r
6578       /* Builtin wave resource.  Error if not found. */\r
6579       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6580       if (h == NULL) break;\r
6581       ms->data = (void *)LoadResource(hInst, h);\r
6582       if (h == NULL) break;\r
6583       ok = TRUE;\r
6584     }\r
6585     break;\r
6586   default:\r
6587     /* .wav file.  Error if not found. */\r
6588     f = fopen(ms->name, "rb");\r
6589     if (f == NULL) break;\r
6590     if (fstat(fileno(f), &st) < 0) break;\r
6591     ms->data = malloc(st.st_size);\r
6592     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6593     fclose(f);\r
6594     ok = TRUE;\r
6595     break;\r
6596   }\r
6597   if (!ok) {\r
6598     char buf[MSG_SIZ];\r
6599     sprintf(buf, "Error loading sound %s", ms->name);\r
6600     DisplayError(buf, GetLastError());\r
6601   }\r
6602   return ok;\r
6603 }\r
6604 \r
6605 BOOLEAN\r
6606 MyPlaySound(MySound *ms)\r
6607 {\r
6608   BOOLEAN ok = FALSE;\r
6609 \r
6610   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6611   switch (ms->name[0]) {\r
6612   case NULLCHAR:\r
6613         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6614     /* Silence */\r
6615     ok = TRUE;\r
6616     break;\r
6617   case '$':\r
6618     /* System sound from Control Panel (deprecated feature).\r
6619        "$" alone or an unset sound name gets default beep (still in use). */\r
6620     if (ms->name[1]) {\r
6621       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6622     }\r
6623     if (!ok) ok = MessageBeep(MB_OK);\r
6624     break; \r
6625   case '!':\r
6626     /* Builtin wave resource, or "!" alone for silence */\r
6627     if (ms->name[1]) {\r
6628       if (ms->data == NULL) return FALSE;\r
6629       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6630     } else {\r
6631       ok = TRUE;\r
6632     }\r
6633     break;\r
6634   default:\r
6635     /* .wav file.  Error if not found. */\r
6636     if (ms->data == NULL) return FALSE;\r
6637     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6638     break;\r
6639   }\r
6640   /* Don't print an error: this can happen innocently if the sound driver\r
6641      is busy; for instance, if another instance of WinBoard is playing\r
6642      a sound at about the same time. */\r
6643   return ok;\r
6644 }\r
6645 \r
6646 \r
6647 LRESULT CALLBACK\r
6648 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6649 {\r
6650   BOOL ok;\r
6651   OPENFILENAME *ofn;\r
6652   static UINT *number; /* gross that this is static */\r
6653 \r
6654   switch (message) {\r
6655   case WM_INITDIALOG: /* message: initialize dialog box */\r
6656     /* Center the dialog over the application window */\r
6657     ofn = (OPENFILENAME *) lParam;\r
6658     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6659       number = (UINT *) ofn->lCustData;\r
6660       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6661     } else {\r
6662       number = NULL;\r
6663     }\r
6664     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6665     return FALSE;  /* Allow for further processing */\r
6666 \r
6667   case WM_COMMAND:\r
6668     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6669       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6670     }\r
6671     return FALSE;  /* Allow for further processing */\r
6672   }\r
6673   return FALSE;\r
6674 }\r
6675 \r
6676 UINT APIENTRY\r
6677 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6678 {\r
6679   static UINT *number;\r
6680   OPENFILENAME *ofname;\r
6681   OFNOTIFY *ofnot;\r
6682   switch (uiMsg) {\r
6683   case WM_INITDIALOG:\r
6684     ofname = (OPENFILENAME *)lParam;\r
6685     number = (UINT *)(ofname->lCustData);\r
6686     break;\r
6687   case WM_NOTIFY:\r
6688     ofnot = (OFNOTIFY *)lParam;\r
6689     if (ofnot->hdr.code == CDN_FILEOK) {\r
6690       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6691     }\r
6692     break;\r
6693   }\r
6694   return 0;\r
6695 }\r
6696 \r
6697 \r
6698 FILE *\r
6699 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6700                char *nameFilt, char *dlgTitle, UINT *number,\r
6701                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6702 {\r
6703   OPENFILENAME openFileName;\r
6704   char buf1[MSG_SIZ];\r
6705   FILE *f;\r
6706 \r
6707   if (fileName == NULL) fileName = buf1;\r
6708   if (defName == NULL) {\r
6709     strcpy(fileName, "*.");\r
6710     strcat(fileName, defExt);\r
6711   } else {\r
6712     strcpy(fileName, defName);\r
6713   }\r
6714   if (fileTitle) strcpy(fileTitle, "");\r
6715   if (number) *number = 0;\r
6716 \r
6717   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6718   openFileName.hwndOwner         = hwnd;\r
6719   openFileName.hInstance         = (HANDLE) hInst;\r
6720   openFileName.lpstrFilter       = nameFilt;\r
6721   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6722   openFileName.nMaxCustFilter    = 0L;\r
6723   openFileName.nFilterIndex      = 1L;\r
6724   openFileName.lpstrFile         = fileName;\r
6725   openFileName.nMaxFile          = MSG_SIZ;\r
6726   openFileName.lpstrFileTitle    = fileTitle;\r
6727   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6728   openFileName.lpstrInitialDir   = NULL;\r
6729   openFileName.lpstrTitle        = dlgTitle;\r
6730   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6731     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6732     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6733     | (oldDialog ? 0 : OFN_EXPLORER);\r
6734   openFileName.nFileOffset       = 0;\r
6735   openFileName.nFileExtension    = 0;\r
6736   openFileName.lpstrDefExt       = defExt;\r
6737   openFileName.lCustData         = (LONG) number;\r
6738   openFileName.lpfnHook          = oldDialog ?\r
6739     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6740   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6741 \r
6742   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6743                         GetOpenFileName(&openFileName)) {\r
6744     /* open the file */\r
6745     f = fopen(openFileName.lpstrFile, write);\r
6746     if (f == NULL) {\r
6747       MessageBox(hwnd, "File open failed", NULL,\r
6748                  MB_OK|MB_ICONEXCLAMATION);\r
6749       return NULL;\r
6750     }\r
6751   } else {\r
6752     int err = CommDlgExtendedError();\r
6753     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6754     return FALSE;\r
6755   }\r
6756   return f;\r
6757 }\r
6758 \r
6759 \r
6760 \r
6761 VOID APIENTRY\r
6762 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6763 {\r
6764   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6765 \r
6766   /*\r
6767    * Get the first pop-up menu in the menu template. This is the\r
6768    * menu that TrackPopupMenu displays.\r
6769    */\r
6770   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6771 \r
6772   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6773 \r
6774   /*\r
6775    * TrackPopup uses screen coordinates, so convert the\r
6776    * coordinates of the mouse click to screen coordinates.\r
6777    */\r
6778   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6779 \r
6780   /* Draw and track the floating pop-up menu. */\r
6781   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6782                  pt.x, pt.y, 0, hwnd, NULL);\r
6783 \r
6784   /* Destroy the menu.*/\r
6785   DestroyMenu(hmenu);\r
6786 }\r
6787    \r
6788 typedef struct {\r
6789   HWND hDlg, hText;\r
6790   int sizeX, sizeY, newSizeX, newSizeY;\r
6791   HDWP hdwp;\r
6792 } ResizeEditPlusButtonsClosure;\r
6793 \r
6794 BOOL CALLBACK\r
6795 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6796 {\r
6797   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6798   RECT rect;\r
6799   POINT pt;\r
6800 \r
6801   if (hChild == cl->hText) return TRUE;\r
6802   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6803   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6804   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6805   ScreenToClient(cl->hDlg, &pt);\r
6806   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6807     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6808   return TRUE;\r
6809 }\r
6810 \r
6811 /* Resize a dialog that has a (rich) edit field filling most of\r
6812    the top, with a row of buttons below */\r
6813 VOID\r
6814 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6815 {\r
6816   RECT rectText;\r
6817   int newTextHeight, newTextWidth;\r
6818   ResizeEditPlusButtonsClosure cl;\r
6819   \r
6820   /*if (IsIconic(hDlg)) return;*/\r
6821   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6822   \r
6823   cl.hdwp = BeginDeferWindowPos(8);\r
6824 \r
6825   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6826   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6827   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6828   if (newTextHeight < 0) {\r
6829     newSizeY += -newTextHeight;\r
6830     newTextHeight = 0;\r
6831   }\r
6832   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6833     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6834 \r
6835   cl.hDlg = hDlg;\r
6836   cl.hText = hText;\r
6837   cl.sizeX = sizeX;\r
6838   cl.sizeY = sizeY;\r
6839   cl.newSizeX = newSizeX;\r
6840   cl.newSizeY = newSizeY;\r
6841   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6842 \r
6843   EndDeferWindowPos(cl.hdwp);\r
6844 }\r
6845 \r
6846 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6847 {\r
6848     RECT    rChild, rParent;\r
6849     int     wChild, hChild, wParent, hParent;\r
6850     int     wScreen, hScreen, xNew, yNew;\r
6851     HDC     hdc;\r
6852 \r
6853     /* Get the Height and Width of the child window */\r
6854     GetWindowRect (hwndChild, &rChild);\r
6855     wChild = rChild.right - rChild.left;\r
6856     hChild = rChild.bottom - rChild.top;\r
6857 \r
6858     /* Get the Height and Width of the parent window */\r
6859     GetWindowRect (hwndParent, &rParent);\r
6860     wParent = rParent.right - rParent.left;\r
6861     hParent = rParent.bottom - rParent.top;\r
6862 \r
6863     /* Get the display limits */\r
6864     hdc = GetDC (hwndChild);\r
6865     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6866     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6867     ReleaseDC(hwndChild, hdc);\r
6868 \r
6869     /* Calculate new X position, then adjust for screen */\r
6870     xNew = rParent.left + ((wParent - wChild) /2);\r
6871     if (xNew < 0) {\r
6872         xNew = 0;\r
6873     } else if ((xNew+wChild) > wScreen) {\r
6874         xNew = wScreen - wChild;\r
6875     }\r
6876 \r
6877     /* Calculate new Y position, then adjust for screen */\r
6878     if( mode == 0 ) {\r
6879         yNew = rParent.top  + ((hParent - hChild) /2);\r
6880     }\r
6881     else {\r
6882         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6883     }\r
6884 \r
6885     if (yNew < 0) {\r
6886         yNew = 0;\r
6887     } else if ((yNew+hChild) > hScreen) {\r
6888         yNew = hScreen - hChild;\r
6889     }\r
6890 \r
6891     /* Set it, and return */\r
6892     return SetWindowPos (hwndChild, NULL,\r
6893                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6894 }\r
6895 \r
6896 /* Center one window over another */\r
6897 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6898 {\r
6899     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6900 }\r
6901 \r
6902 /*---------------------------------------------------------------------------*\\r
6903  *\r
6904  * Startup Dialog functions\r
6905  *\r
6906 \*---------------------------------------------------------------------------*/\r
6907 void\r
6908 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6909 {\r
6910   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6911 \r
6912   while (*cd != NULL) {\r
6913     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6914     cd++;\r
6915   }\r
6916 }\r
6917 \r
6918 void\r
6919 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6920 {\r
6921   char buf1[ARG_MAX];\r
6922   int len;\r
6923 \r
6924   if (str[0] == '@') {\r
6925     FILE* f = fopen(str + 1, "r");\r
6926     if (f == NULL) {\r
6927       DisplayFatalError(str + 1, errno, 2);\r
6928       return;\r
6929     }\r
6930     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6931     fclose(f);\r
6932     buf1[len] = NULLCHAR;\r
6933     str = buf1;\r
6934   }\r
6935 \r
6936   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6937 \r
6938   for (;;) {\r
6939     char buf[MSG_SIZ];\r
6940     char *end = strchr(str, '\n');\r
6941     if (end == NULL) return;\r
6942     memcpy(buf, str, end - str);\r
6943     buf[end - str] = NULLCHAR;\r
6944     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6945     str = end + 1;\r
6946   }\r
6947 }\r
6948 \r
6949 void\r
6950 SetStartupDialogEnables(HWND hDlg)\r
6951 {\r
6952   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6953     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6954     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6955   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6956     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6957   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6958     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6959   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6960     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6961   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6962     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6963     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6964     IsDlgButtonChecked(hDlg, OPT_View));\r
6965 }\r
6966 \r
6967 char *\r
6968 QuoteForFilename(char *filename)\r
6969 {\r
6970   int dquote, space;\r
6971   dquote = strchr(filename, '"') != NULL;\r
6972   space = strchr(filename, ' ') != NULL;\r
6973   if (dquote || space) {\r
6974     if (dquote) {\r
6975       return "'";\r
6976     } else {\r
6977       return "\"";\r
6978     }\r
6979   } else {\r
6980     return "";\r
6981   }\r
6982 }\r
6983 \r
6984 VOID\r
6985 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6986 {\r
6987   char buf[MSG_SIZ];\r
6988   char *q;\r
6989 \r
6990   InitComboStringsFromOption(hwndCombo, nthnames);\r
6991   q = QuoteForFilename(nthcp);\r
6992   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6993   if (*nthdir != NULLCHAR) {\r
6994     q = QuoteForFilename(nthdir);\r
6995     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6996   }\r
6997   if (*nthcp == NULLCHAR) {\r
6998     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6999   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7000     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7001     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7002   }\r
7003 }\r
7004 \r
7005 LRESULT CALLBACK\r
7006 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7007 {\r
7008   char buf[MSG_SIZ];\r
7009   HANDLE hwndCombo;\r
7010   char *p;\r
7011 \r
7012   switch (message) {\r
7013   case WM_INITDIALOG:\r
7014     /* Center the dialog */\r
7015     CenterWindow (hDlg, GetDesktopWindow());\r
7016     /* Initialize the dialog items */\r
7017     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7018                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7019                   firstChessProgramNames);\r
7020     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7021                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7022                   secondChessProgramNames);\r
7023     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7024     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7025     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7026     if (*appData.icsHelper != NULLCHAR) {\r
7027       char *q = QuoteForFilename(appData.icsHelper);\r
7028       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7029     }\r
7030     if (*appData.icsHost == NULLCHAR) {\r
7031       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7032       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7033     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7034       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7035       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7036     }\r
7037 \r
7038     if (appData.icsActive) {\r
7039       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7040     }\r
7041     else if (appData.noChessProgram) {\r
7042       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7043     }\r
7044     else {\r
7045       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7046     }\r
7047 \r
7048     SetStartupDialogEnables(hDlg);\r
7049     return TRUE;\r
7050 \r
7051   case WM_COMMAND:\r
7052     switch (LOWORD(wParam)) {\r
7053     case IDOK:\r
7054       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7055         strcpy(buf, "/fcp=");\r
7056         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7057         p = buf;\r
7058         ParseArgs(StringGet, &p);\r
7059         strcpy(buf, "/scp=");\r
7060         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7061         p = buf;\r
7062         ParseArgs(StringGet, &p);\r
7063         appData.noChessProgram = FALSE;\r
7064         appData.icsActive = FALSE;\r
7065       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7066         strcpy(buf, "/ics /icshost=");\r
7067         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7068         p = buf;\r
7069         ParseArgs(StringGet, &p);\r
7070         if (appData.zippyPlay) {\r
7071           strcpy(buf, "/fcp=");\r
7072           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7073           p = buf;\r
7074           ParseArgs(StringGet, &p);\r
7075         }\r
7076       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7077         appData.noChessProgram = TRUE;\r
7078         appData.icsActive = FALSE;\r
7079       } else {\r
7080         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7081                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7082         return TRUE;\r
7083       }\r
7084       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7085         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7086         p = buf;\r
7087         ParseArgs(StringGet, &p);\r
7088       }\r
7089       EndDialog(hDlg, TRUE);\r
7090       return TRUE;\r
7091 \r
7092     case IDCANCEL:\r
7093       ExitEvent(0);\r
7094       return TRUE;\r
7095 \r
7096     case IDM_HELPCONTENTS:\r
7097       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7098         MessageBox (GetFocus(),\r
7099                     "Unable to activate help",\r
7100                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7101       }\r
7102       break;\r
7103 \r
7104     default:\r
7105       SetStartupDialogEnables(hDlg);\r
7106       break;\r
7107     }\r
7108     break;\r
7109   }\r
7110   return FALSE;\r
7111 }\r
7112 \r
7113 /*---------------------------------------------------------------------------*\\r
7114  *\r
7115  * About box dialog functions\r
7116  *\r
7117 \*---------------------------------------------------------------------------*/\r
7118 \r
7119 /* Process messages for "About" dialog box */\r
7120 LRESULT CALLBACK\r
7121 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7122 {\r
7123   switch (message) {\r
7124   case WM_INITDIALOG: /* message: initialize dialog box */\r
7125     /* Center the dialog over the application window */\r
7126     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7127     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7128     JAWS_COPYRIGHT\r
7129     return (TRUE);\r
7130 \r
7131   case WM_COMMAND: /* message: received a command */\r
7132     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7133         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7134       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7135       return (TRUE);\r
7136     }\r
7137     break;\r
7138   }\r
7139   return (FALSE);\r
7140 }\r
7141 \r
7142 /*---------------------------------------------------------------------------*\\r
7143  *\r
7144  * Comment Dialog functions\r
7145  *\r
7146 \*---------------------------------------------------------------------------*/\r
7147 \r
7148 LRESULT CALLBACK\r
7149 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7150 {\r
7151   static HANDLE hwndText = NULL;\r
7152   int len, newSizeX, newSizeY, flags;\r
7153   static int sizeX, sizeY;\r
7154   char *str;\r
7155   RECT rect;\r
7156   MINMAXINFO *mmi;\r
7157 \r
7158   switch (message) {\r
7159   case WM_INITDIALOG: /* message: initialize dialog box */\r
7160     /* Initialize the dialog items */\r
7161     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7162     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7163     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7164     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7165     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7166     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7167     SetWindowText(hDlg, commentTitle);\r
7168     if (editComment) {\r
7169       SetFocus(hwndText);\r
7170     } else {\r
7171       SetFocus(GetDlgItem(hDlg, IDOK));\r
7172     }\r
7173     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7174                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7175                 MAKELPARAM(FALSE, 0));\r
7176     /* Size and position the dialog */\r
7177     if (!commentDialog) {\r
7178       commentDialog = hDlg;\r
7179       flags = SWP_NOZORDER;\r
7180       GetClientRect(hDlg, &rect);\r
7181       sizeX = rect.right;\r
7182       sizeY = rect.bottom;\r
7183       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7184           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7185         WINDOWPLACEMENT wp;\r
7186         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7187         wp.length = sizeof(WINDOWPLACEMENT);\r
7188         wp.flags = 0;\r
7189         wp.showCmd = SW_SHOW;\r
7190         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7191         wp.rcNormalPosition.left = commentX;\r
7192         wp.rcNormalPosition.right = commentX + commentW;\r
7193         wp.rcNormalPosition.top = commentY;\r
7194         wp.rcNormalPosition.bottom = commentY + commentH;\r
7195         SetWindowPlacement(hDlg, &wp);\r
7196 \r
7197         GetClientRect(hDlg, &rect);\r
7198         newSizeX = rect.right;\r
7199         newSizeY = rect.bottom;\r
7200         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7201                               newSizeX, newSizeY);\r
7202         sizeX = newSizeX;\r
7203         sizeY = newSizeY;\r
7204       }\r
7205     }\r
7206     return FALSE;\r
7207 \r
7208   case WM_COMMAND: /* message: received a command */\r
7209     switch (LOWORD(wParam)) {\r
7210     case IDOK:\r
7211       if (editComment) {\r
7212         char *p, *q;\r
7213         /* Read changed options from the dialog box */\r
7214         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7215         len = GetWindowTextLength(hwndText);\r
7216         str = (char *) malloc(len + 1);\r
7217         GetWindowText(hwndText, str, len + 1);\r
7218         p = q = str;\r
7219         while (*q) {\r
7220           if (*q == '\r')\r
7221             q++;\r
7222           else\r
7223             *p++ = *q++;\r
7224         }\r
7225         *p = NULLCHAR;\r
7226         ReplaceComment(commentIndex, str);\r
7227         free(str);\r
7228       }\r
7229       CommentPopDown();\r
7230       return TRUE;\r
7231 \r
7232     case IDCANCEL:\r
7233     case OPT_CancelComment:\r
7234       CommentPopDown();\r
7235       return TRUE;\r
7236 \r
7237     case OPT_ClearComment:\r
7238       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7239       break;\r
7240 \r
7241     case OPT_EditComment:\r
7242       EditCommentEvent();\r
7243       return TRUE;\r
7244 \r
7245     default:\r
7246       break;\r
7247     }\r
7248     break;\r
7249 \r
7250   case WM_SIZE:\r
7251     newSizeX = LOWORD(lParam);\r
7252     newSizeY = HIWORD(lParam);\r
7253     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7254     sizeX = newSizeX;\r
7255     sizeY = newSizeY;\r
7256     break;\r
7257 \r
7258   case WM_GETMINMAXINFO:\r
7259     /* Prevent resizing window too small */\r
7260     mmi = (MINMAXINFO *) lParam;\r
7261     mmi->ptMinTrackSize.x = 100;\r
7262     mmi->ptMinTrackSize.y = 100;\r
7263     break;\r
7264   }\r
7265   return FALSE;\r
7266 }\r
7267 \r
7268 VOID\r
7269 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7270 {\r
7271   FARPROC lpProc;\r
7272   char *p, *q;\r
7273 \r
7274   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7275 \r
7276   if (str == NULL) str = "";\r
7277   p = (char *) malloc(2 * strlen(str) + 2);\r
7278   q = p;\r
7279   while (*str) {\r
7280     if (*str == '\n') *q++ = '\r';\r
7281     *q++ = *str++;\r
7282   }\r
7283   *q = NULLCHAR;\r
7284   if (commentText != NULL) free(commentText);\r
7285 \r
7286   commentIndex = index;\r
7287   commentTitle = title;\r
7288   commentText = p;\r
7289   editComment = edit;\r
7290 \r
7291   if (commentDialog) {\r
7292     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7293     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7294   } else {\r
7295     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7296     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7297                  hwndMain, (DLGPROC)lpProc);\r
7298     FreeProcInstance(lpProc);\r
7299   }\r
7300   commentDialogUp = TRUE;\r
7301 }\r
7302 \r
7303 \r
7304 /*---------------------------------------------------------------------------*\\r
7305  *\r
7306  * Type-in move dialog functions\r
7307  * \r
7308 \*---------------------------------------------------------------------------*/\r
7309 \r
7310 LRESULT CALLBACK\r
7311 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7312 {\r
7313   char move[MSG_SIZ];\r
7314   HWND hInput;\r
7315   ChessMove moveType;\r
7316   int fromX, fromY, toX, toY;\r
7317   char promoChar;\r
7318 \r
7319   switch (message) {\r
7320   case WM_INITDIALOG:\r
7321     move[0] = (char) lParam;\r
7322     move[1] = NULLCHAR;\r
7323     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7324     hInput = GetDlgItem(hDlg, OPT_Move);\r
7325     SetWindowText(hInput, move);\r
7326     SetFocus(hInput);\r
7327     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7328     return FALSE;\r
7329 \r
7330   case WM_COMMAND:\r
7331     switch (LOWORD(wParam)) {\r
7332     case IDOK:\r
7333       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7334       { int n; Board board;\r
7335         // [HGM] FENedit\r
7336         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7337                 EditPositionPasteFEN(move);\r
7338                 EndDialog(hDlg, TRUE);\r
7339                 return TRUE;\r
7340         }\r
7341         // [HGM] movenum: allow move number to be typed in any mode\r
7342         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7343           currentMove = 2*n-1;\r
7344           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7345           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7346           EndDialog(hDlg, TRUE);\r
7347           DrawPosition(TRUE, boards[currentMove]);\r
7348           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7349           else DisplayMessage("", "");\r
7350           return TRUE;\r
7351         }\r
7352       }\r
7353       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7354         gameMode != Training) {\r
7355         DisplayMoveError("Displayed move is not current");\r
7356       } else {\r
7357 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7358         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7359           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7360         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7361         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7362           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7363           if (gameMode != Training)\r
7364               forwardMostMove = currentMove;\r
7365           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7366         } else {\r
7367           DisplayMoveError("Could not parse move");\r
7368         }\r
7369       }\r
7370       EndDialog(hDlg, TRUE);\r
7371       return TRUE;\r
7372     case IDCANCEL:\r
7373       EndDialog(hDlg, FALSE);\r
7374       return TRUE;\r
7375     default:\r
7376       break;\r
7377     }\r
7378     break;\r
7379   }\r
7380   return FALSE;\r
7381 }\r
7382 \r
7383 VOID\r
7384 PopUpMoveDialog(char firstchar)\r
7385 {\r
7386     FARPROC lpProc;\r
7387     \r
7388     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7389         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7390         gameMode == AnalyzeMode || gameMode == EditGame || \r
7391         gameMode == EditPosition || gameMode == IcsExamining ||\r
7392         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7393         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7394                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7395                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7396         gameMode == Training) {\r
7397       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7398       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7399         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7400       FreeProcInstance(lpProc);\r
7401     }\r
7402 }\r
7403 \r
7404 /*---------------------------------------------------------------------------*\\r
7405  *\r
7406  * Type-in name dialog functions\r
7407  * \r
7408 \*---------------------------------------------------------------------------*/\r
7409 \r
7410 LRESULT CALLBACK\r
7411 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7412 {\r
7413   char move[MSG_SIZ];\r
7414   HWND hInput;\r
7415 \r
7416   switch (message) {\r
7417   case WM_INITDIALOG:\r
7418     move[0] = (char) lParam;\r
7419     move[1] = NULLCHAR;\r
7420     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7421     hInput = GetDlgItem(hDlg, OPT_Name);\r
7422     SetWindowText(hInput, move);\r
7423     SetFocus(hInput);\r
7424     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7425     return FALSE;\r
7426 \r
7427   case WM_COMMAND:\r
7428     switch (LOWORD(wParam)) {\r
7429     case IDOK:\r
7430       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7431       appData.userName = strdup(move);\r
7432       SetUserLogo();\r
7433 \r
7434       EndDialog(hDlg, TRUE);\r
7435       return TRUE;\r
7436     case IDCANCEL:\r
7437       EndDialog(hDlg, FALSE);\r
7438       return TRUE;\r
7439     default:\r
7440       break;\r
7441     }\r
7442     break;\r
7443   }\r
7444   return FALSE;\r
7445 }\r
7446 \r
7447 VOID\r
7448 PopUpNameDialog(char firstchar)\r
7449 {\r
7450     FARPROC lpProc;\r
7451     \r
7452       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7453       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7454         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7455       FreeProcInstance(lpProc);\r
7456 }\r
7457 \r
7458 /*---------------------------------------------------------------------------*\\r
7459  *\r
7460  *  Error dialogs\r
7461  * \r
7462 \*---------------------------------------------------------------------------*/\r
7463 \r
7464 /* Nonmodal error box */\r
7465 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7466                              WPARAM wParam, LPARAM lParam);\r
7467 \r
7468 VOID\r
7469 ErrorPopUp(char *title, char *content)\r
7470 {\r
7471   FARPROC lpProc;\r
7472   char *p, *q;\r
7473   BOOLEAN modal = hwndMain == NULL;\r
7474 \r
7475   p = content;\r
7476   q = errorMessage;\r
7477   while (*p) {\r
7478     if (*p == '\n') {\r
7479       if (modal) {\r
7480         *q++ = ' ';\r
7481         p++;\r
7482       } else {\r
7483         *q++ = '\r';\r
7484         *q++ = *p++;\r
7485       }\r
7486     } else {\r
7487       *q++ = *p++;\r
7488     }\r
7489   }\r
7490   *q = NULLCHAR;\r
7491   strncpy(errorTitle, title, sizeof(errorTitle));\r
7492   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7493   \r
7494   if (modal) {\r
7495     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7496   } else {\r
7497     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7498     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7499                  hwndMain, (DLGPROC)lpProc);\r
7500     FreeProcInstance(lpProc);\r
7501   }\r
7502 }\r
7503 \r
7504 VOID\r
7505 ErrorPopDown()\r
7506 {\r
7507   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7508   if (errorDialog == NULL) return;\r
7509   DestroyWindow(errorDialog);\r
7510   errorDialog = NULL;\r
7511 }\r
7512 \r
7513 LRESULT CALLBACK\r
7514 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7515 {\r
7516   HANDLE hwndText;\r
7517   RECT rChild;\r
7518 \r
7519   switch (message) {\r
7520   case WM_INITDIALOG:\r
7521     GetWindowRect(hDlg, &rChild);\r
7522 \r
7523     /*\r
7524     SetWindowPos(hDlg, NULL, rChild.left,\r
7525       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7526       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7527     */\r
7528 \r
7529     /* \r
7530         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7531         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7532         and it doesn't work when you resize the dialog.\r
7533         For now, just give it a default position.\r
7534     */\r
7535     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7536 \r
7537     errorDialog = hDlg;\r
7538     SetWindowText(hDlg, errorTitle);\r
7539     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7540     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7541     return FALSE;\r
7542 \r
7543   case WM_COMMAND:\r
7544     switch (LOWORD(wParam)) {\r
7545     case IDOK:\r
7546     case IDCANCEL:\r
7547       if (errorDialog == hDlg) errorDialog = NULL;\r
7548       DestroyWindow(hDlg);\r
7549       return TRUE;\r
7550 \r
7551     default:\r
7552       break;\r
7553     }\r
7554     break;\r
7555   }\r
7556   return FALSE;\r
7557 }\r
7558 \r
7559 #ifdef GOTHIC\r
7560 HWND gothicDialog = NULL;\r
7561 \r
7562 LRESULT CALLBACK\r
7563 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7564 {\r
7565   HANDLE hwndText;\r
7566   RECT rChild;\r
7567   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7568 \r
7569   switch (message) {\r
7570   case WM_INITDIALOG:\r
7571     GetWindowRect(hDlg, &rChild);\r
7572 \r
7573     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7574                                                              SWP_NOZORDER);\r
7575 \r
7576     /* \r
7577         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7578         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7579         and it doesn't work when you resize the dialog.\r
7580         For now, just give it a default position.\r
7581     */\r
7582     gothicDialog = hDlg;\r
7583     SetWindowText(hDlg, errorTitle);\r
7584     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7585     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7586     return FALSE;\r
7587 \r
7588   case WM_COMMAND:\r
7589     switch (LOWORD(wParam)) {\r
7590     case IDOK:\r
7591     case IDCANCEL:\r
7592       if (errorDialog == hDlg) errorDialog = NULL;\r
7593       DestroyWindow(hDlg);\r
7594       return TRUE;\r
7595 \r
7596     default:\r
7597       break;\r
7598     }\r
7599     break;\r
7600   }\r
7601   return FALSE;\r
7602 }\r
7603 \r
7604 VOID\r
7605 GothicPopUp(char *title, VariantClass variant)\r
7606 {\r
7607   FARPROC lpProc;\r
7608   static char *lastTitle;\r
7609 \r
7610   strncpy(errorTitle, title, sizeof(errorTitle));\r
7611   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7612 \r
7613   if(lastTitle != title && gothicDialog != NULL) {\r
7614     DestroyWindow(gothicDialog);\r
7615     gothicDialog = NULL;\r
7616   }\r
7617   if(variant != VariantNormal && gothicDialog == NULL) {\r
7618     title = lastTitle;\r
7619     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7620     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7621                  hwndMain, (DLGPROC)lpProc);\r
7622     FreeProcInstance(lpProc);\r
7623   }\r
7624 }\r
7625 #endif\r
7626 \r
7627 /*---------------------------------------------------------------------------*\\r
7628  *\r
7629  *  Ics Interaction console functions\r
7630  *\r
7631 \*---------------------------------------------------------------------------*/\r
7632 \r
7633 #define HISTORY_SIZE 64\r
7634 static char *history[HISTORY_SIZE];\r
7635 int histIn = 0, histP = 0;\r
7636 \r
7637 VOID\r
7638 SaveInHistory(char *cmd)\r
7639 {\r
7640   if (history[histIn] != NULL) {\r
7641     free(history[histIn]);\r
7642     history[histIn] = NULL;\r
7643   }\r
7644   if (*cmd == NULLCHAR) return;\r
7645   history[histIn] = StrSave(cmd);\r
7646   histIn = (histIn + 1) % HISTORY_SIZE;\r
7647   if (history[histIn] != NULL) {\r
7648     free(history[histIn]);\r
7649     history[histIn] = NULL;\r
7650   }\r
7651   histP = histIn;\r
7652 }\r
7653 \r
7654 char *\r
7655 PrevInHistory(char *cmd)\r
7656 {\r
7657   int newhp;\r
7658   if (histP == histIn) {\r
7659     if (history[histIn] != NULL) free(history[histIn]);\r
7660     history[histIn] = StrSave(cmd);\r
7661   }\r
7662   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7663   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7664   histP = newhp;\r
7665   return history[histP];\r
7666 }\r
7667 \r
7668 char *\r
7669 NextInHistory()\r
7670 {\r
7671   if (histP == histIn) return NULL;\r
7672   histP = (histP + 1) % HISTORY_SIZE;\r
7673   return history[histP];\r
7674 }\r
7675 \r
7676 typedef struct {\r
7677   char *item;\r
7678   char *command;\r
7679   BOOLEAN getname;\r
7680   BOOLEAN immediate;\r
7681 } IcsTextMenuEntry;\r
7682 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7683 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7684 \r
7685 void\r
7686 ParseIcsTextMenu(char *icsTextMenuString)\r
7687 {\r
7688 //  int flags = 0;\r
7689   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7690   char *p = icsTextMenuString;\r
7691   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7692     free(e->item);\r
7693     e->item = NULL;\r
7694     if (e->command != NULL) {\r
7695       free(e->command);\r
7696       e->command = NULL;\r
7697     }\r
7698     e++;\r
7699   }\r
7700   e = icsTextMenuEntry;\r
7701   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7702     if (*p == ';' || *p == '\n') {\r
7703       e->item = strdup("-");\r
7704       e->command = NULL;\r
7705       p++;\r
7706     } else if (*p == '-') {\r
7707       e->item = strdup("-");\r
7708       e->command = NULL;\r
7709       p++;\r
7710       if (*p) p++;\r
7711     } else {\r
7712       char *q, *r, *s, *t;\r
7713       char c;\r
7714       q = strchr(p, ',');\r
7715       if (q == NULL) break;\r
7716       *q = NULLCHAR;\r
7717       r = strchr(q + 1, ',');\r
7718       if (r == NULL) break;\r
7719       *r = NULLCHAR;\r
7720       s = strchr(r + 1, ',');\r
7721       if (s == NULL) break;\r
7722       *s = NULLCHAR;\r
7723       c = ';';\r
7724       t = strchr(s + 1, c);\r
7725       if (t == NULL) {\r
7726         c = '\n';\r
7727         t = strchr(s + 1, c);\r
7728       }\r
7729       if (t != NULL) *t = NULLCHAR;\r
7730       e->item = strdup(p);\r
7731       e->command = strdup(q + 1);\r
7732       e->getname = *(r + 1) != '0';\r
7733       e->immediate = *(s + 1) != '0';\r
7734       *q = ',';\r
7735       *r = ',';\r
7736       *s = ',';\r
7737       if (t == NULL) break;\r
7738       *t = c;\r
7739       p = t + 1;\r
7740     }\r
7741     e++;\r
7742   } \r
7743 }\r
7744 \r
7745 HMENU\r
7746 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7747 {\r
7748   HMENU hmenu, h;\r
7749   int i = 0;\r
7750   hmenu = LoadMenu(hInst, "TextMenu");\r
7751   h = GetSubMenu(hmenu, 0);\r
7752   while (e->item) {\r
7753     if (strcmp(e->item, "-") == 0) {\r
7754       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7755     } else {\r
7756       if (e->item[0] == '|') {\r
7757         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7758                    IDM_CommandX + i, &e->item[1]);\r
7759       } else {\r
7760         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7761       }\r
7762     }\r
7763     e++;\r
7764     i++;\r
7765   } \r
7766   return hmenu;\r
7767 }\r
7768 \r
7769 WNDPROC consoleTextWindowProc;\r
7770 \r
7771 void\r
7772 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7773 {\r
7774   char buf[MSG_SIZ], name[MSG_SIZ];\r
7775   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7776   CHARRANGE sel;\r
7777 \r
7778   if (!getname) {\r
7779     SetWindowText(hInput, command);\r
7780     if (immediate) {\r
7781       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7782     } else {\r
7783       sel.cpMin = 999999;\r
7784       sel.cpMax = 999999;\r
7785       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7786       SetFocus(hInput);\r
7787     }\r
7788     return;\r
7789   }    \r
7790   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7791   if (sel.cpMin == sel.cpMax) {\r
7792     /* Expand to surrounding word */\r
7793     TEXTRANGE tr;\r
7794     do {\r
7795       tr.chrg.cpMax = sel.cpMin;\r
7796       tr.chrg.cpMin = --sel.cpMin;\r
7797       if (sel.cpMin < 0) break;\r
7798       tr.lpstrText = name;\r
7799       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7800     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7801     sel.cpMin++;\r
7802 \r
7803     do {\r
7804       tr.chrg.cpMin = sel.cpMax;\r
7805       tr.chrg.cpMax = ++sel.cpMax;\r
7806       tr.lpstrText = name;\r
7807       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7808     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7809     sel.cpMax--;\r
7810 \r
7811     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7812       MessageBeep(MB_ICONEXCLAMATION);\r
7813       return;\r
7814     }\r
7815     tr.chrg = sel;\r
7816     tr.lpstrText = name;\r
7817     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7818   } else {\r
7819     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7820       MessageBeep(MB_ICONEXCLAMATION);\r
7821       return;\r
7822     }\r
7823     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7824   }\r
7825   if (immediate) {\r
7826     sprintf(buf, "%s %s", command, name);\r
7827     SetWindowText(hInput, buf);\r
7828     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7829   } else {\r
7830     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7831     SetWindowText(hInput, buf);\r
7832     sel.cpMin = 999999;\r
7833     sel.cpMax = 999999;\r
7834     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7835     SetFocus(hInput);\r
7836   }\r
7837 }\r
7838 \r
7839 LRESULT CALLBACK \r
7840 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7841 {\r
7842   HWND hInput;\r
7843   CHARRANGE sel;\r
7844 \r
7845   switch (message) {\r
7846   case WM_KEYDOWN:\r
7847     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7848     switch (wParam) {\r
7849     case VK_PRIOR:\r
7850       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7851       return 0;\r
7852     case VK_NEXT:\r
7853       sel.cpMin = 999999;\r
7854       sel.cpMax = 999999;\r
7855       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7856       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7857       return 0;\r
7858     }\r
7859     break;\r
7860   case WM_CHAR:\r
7861    if(wParam != '\022') {\r
7862     if (wParam == '\t') {\r
7863       if (GetKeyState(VK_SHIFT) < 0) {\r
7864         /* shifted */\r
7865         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7866         if (buttonDesc[0].hwnd) {\r
7867           SetFocus(buttonDesc[0].hwnd);\r
7868         } else {\r
7869           SetFocus(hwndMain);\r
7870         }\r
7871       } else {\r
7872         /* unshifted */\r
7873         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7874       }\r
7875     } else {\r
7876       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7877       JAWS_DELETE( SetFocus(hInput); )\r
7878       SendMessage(hInput, message, wParam, lParam);\r
7879     }\r
7880     return 0;\r
7881    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7882   case WM_RBUTTONUP:\r
7883     if (GetKeyState(VK_SHIFT) & ~1) {\r
7884       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7885         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7886     } else {\r
7887       POINT pt;\r
7888       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7889       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7890       if (sel.cpMin == sel.cpMax) {\r
7891         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7892         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7893       }\r
7894       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7895         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7896       }\r
7897       pt.x = LOWORD(lParam);\r
7898       pt.y = HIWORD(lParam);\r
7899       MenuPopup(hwnd, pt, hmenu, -1);\r
7900     }\r
7901     return 0;\r
7902   case WM_PASTE:\r
7903     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7904     SetFocus(hInput);\r
7905     return SendMessage(hInput, message, wParam, lParam);\r
7906   case WM_MBUTTONDOWN:\r
7907     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7908   case WM_RBUTTONDOWN:\r
7909     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7910       /* Move selection here if it was empty */\r
7911       POINT pt;\r
7912       pt.x = LOWORD(lParam);\r
7913       pt.y = HIWORD(lParam);\r
7914       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7915       if (sel.cpMin == sel.cpMax) {\r
7916         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7917         sel.cpMax = sel.cpMin;\r
7918         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7919       }\r
7920       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7921     }\r
7922     return 0;\r
7923   case WM_COMMAND:\r
7924     switch (LOWORD(wParam)) {\r
7925     case IDM_QuickPaste:\r
7926       {\r
7927         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7928         if (sel.cpMin == sel.cpMax) {\r
7929           MessageBeep(MB_ICONEXCLAMATION);\r
7930           return 0;\r
7931         }\r
7932         SendMessage(hwnd, WM_COPY, 0, 0);\r
7933         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7934         SendMessage(hInput, WM_PASTE, 0, 0);\r
7935         SetFocus(hInput);\r
7936         return 0;\r
7937       }\r
7938     case IDM_Cut:\r
7939       SendMessage(hwnd, WM_CUT, 0, 0);\r
7940       return 0;\r
7941     case IDM_Paste:\r
7942       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7943       return 0;\r
7944     case IDM_Copy:\r
7945       SendMessage(hwnd, WM_COPY, 0, 0);\r
7946       return 0;\r
7947     default:\r
7948       {\r
7949         int i = LOWORD(wParam) - IDM_CommandX;\r
7950         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7951             icsTextMenuEntry[i].command != NULL) {\r
7952           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7953                    icsTextMenuEntry[i].getname,\r
7954                    icsTextMenuEntry[i].immediate);\r
7955           return 0;\r
7956         }\r
7957       }\r
7958       break;\r
7959     }\r
7960     break;\r
7961   }\r
7962   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7963 }\r
7964 \r
7965 WNDPROC consoleInputWindowProc;\r
7966 \r
7967 LRESULT CALLBACK\r
7968 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7969 {\r
7970   char buf[MSG_SIZ];\r
7971   char *p;\r
7972   static BOOL sendNextChar = FALSE;\r
7973   static BOOL quoteNextChar = FALSE;\r
7974   InputSource *is = consoleInputSource;\r
7975   CHARFORMAT cf;\r
7976   CHARRANGE sel;\r
7977 \r
7978   switch (message) {\r
7979   case WM_CHAR:\r
7980     if (!appData.localLineEditing || sendNextChar) {\r
7981       is->buf[0] = (CHAR) wParam;\r
7982       is->count = 1;\r
7983       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7984       sendNextChar = FALSE;\r
7985       return 0;\r
7986     }\r
7987     if (quoteNextChar) {\r
7988       buf[0] = (char) wParam;\r
7989       buf[1] = NULLCHAR;\r
7990       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7991       quoteNextChar = FALSE;\r
7992       return 0;\r
7993     }\r
7994     switch (wParam) {\r
7995     case '\r':   /* Enter key */\r
7996       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7997       if (consoleEcho) SaveInHistory(is->buf);\r
7998       is->buf[is->count++] = '\n';\r
7999       is->buf[is->count] = NULLCHAR;\r
8000       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8001       if (consoleEcho) {\r
8002         ConsoleOutput(is->buf, is->count, TRUE);\r
8003       } else if (appData.localLineEditing) {\r
8004         ConsoleOutput("\n", 1, TRUE);\r
8005       }\r
8006       /* fall thru */\r
8007     case '\033': /* Escape key */\r
8008       SetWindowText(hwnd, "");\r
8009       cf.cbSize = sizeof(CHARFORMAT);\r
8010       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8011       if (consoleEcho) {\r
8012         cf.crTextColor = textAttribs[ColorNormal].color;\r
8013       } else {\r
8014         cf.crTextColor = COLOR_ECHOOFF;\r
8015       }\r
8016       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8017       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8018       return 0;\r
8019     case '\t':   /* Tab key */\r
8020       if (GetKeyState(VK_SHIFT) < 0) {\r
8021         /* shifted */\r
8022         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8023       } else {\r
8024         /* unshifted */\r
8025         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8026         if (buttonDesc[0].hwnd) {\r
8027           SetFocus(buttonDesc[0].hwnd);\r
8028         } else {\r
8029           SetFocus(hwndMain);\r
8030         }\r
8031       }\r
8032       return 0;\r
8033     case '\023': /* Ctrl+S */\r
8034       sendNextChar = TRUE;\r
8035       return 0;\r
8036     case '\021': /* Ctrl+Q */\r
8037       quoteNextChar = TRUE;\r
8038       return 0;\r
8039     JAWS_REPLAY\r
8040     default:\r
8041       break;\r
8042     }\r
8043     break;\r
8044   case WM_KEYDOWN:\r
8045     switch (wParam) {\r
8046     case VK_UP:\r
8047       GetWindowText(hwnd, buf, MSG_SIZ);\r
8048       p = PrevInHistory(buf);\r
8049       if (p != NULL) {\r
8050         SetWindowText(hwnd, p);\r
8051         sel.cpMin = 999999;\r
8052         sel.cpMax = 999999;\r
8053         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8054         return 0;\r
8055       }\r
8056       break;\r
8057     case VK_DOWN:\r
8058       p = NextInHistory();\r
8059       if (p != NULL) {\r
8060         SetWindowText(hwnd, p);\r
8061         sel.cpMin = 999999;\r
8062         sel.cpMax = 999999;\r
8063         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8064         return 0;\r
8065       }\r
8066       break;\r
8067     case VK_HOME:\r
8068     case VK_END:\r
8069       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8070       /* fall thru */\r
8071     case VK_PRIOR:\r
8072     case VK_NEXT:\r
8073       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8074       return 0;\r
8075     }\r
8076     break;\r
8077   case WM_MBUTTONDOWN:\r
8078     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8079       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8080     break;\r
8081   case WM_RBUTTONUP:\r
8082     if (GetKeyState(VK_SHIFT) & ~1) {\r
8083       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8084         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8085     } else {\r
8086       POINT pt;\r
8087       HMENU hmenu;\r
8088       hmenu = LoadMenu(hInst, "InputMenu");\r
8089       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8090       if (sel.cpMin == sel.cpMax) {\r
8091         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8092         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8093       }\r
8094       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8095         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8096       }\r
8097       pt.x = LOWORD(lParam);\r
8098       pt.y = HIWORD(lParam);\r
8099       MenuPopup(hwnd, pt, hmenu, -1);\r
8100     }\r
8101     return 0;\r
8102   case WM_COMMAND:\r
8103     switch (LOWORD(wParam)) { \r
8104     case IDM_Undo:\r
8105       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8106       return 0;\r
8107     case IDM_SelectAll:\r
8108       sel.cpMin = 0;\r
8109       sel.cpMax = -1; /*999999?*/\r
8110       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8111       return 0;\r
8112     case IDM_Cut:\r
8113       SendMessage(hwnd, WM_CUT, 0, 0);\r
8114       return 0;\r
8115     case IDM_Paste:\r
8116       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8117       return 0;\r
8118     case IDM_Copy:\r
8119       SendMessage(hwnd, WM_COPY, 0, 0);\r
8120       return 0;\r
8121     }\r
8122     break;\r
8123   }\r
8124   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8125 }\r
8126 \r
8127 #define CO_MAX  100000\r
8128 #define CO_TRIM   1000\r
8129 \r
8130 LRESULT CALLBACK\r
8131 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8132 {\r
8133   static SnapData sd;\r
8134   HWND hText, hInput;\r
8135   RECT rect;\r
8136   static int sizeX, sizeY;\r
8137   int newSizeX, newSizeY;\r
8138   MINMAXINFO *mmi;\r
8139   WORD wMask;\r
8140 \r
8141   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8142   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8143 \r
8144   switch (message) {\r
8145   case WM_NOTIFY:\r
8146     if (((NMHDR*)lParam)->code == EN_LINK)\r
8147     {\r
8148       ENLINK *pLink = (ENLINK*)lParam;\r
8149       if (pLink->msg == WM_LBUTTONUP)\r
8150       {\r
8151         TEXTRANGE tr;\r
8152 \r
8153         tr.chrg = pLink->chrg;\r
8154         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
8155         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
8156         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
8157         free(tr.lpstrText);\r
8158       }\r
8159     }\r
8160     break;\r
8161   case WM_INITDIALOG: /* message: initialize dialog box */\r
8162     hwndConsole = hDlg;\r
8163     SetFocus(hInput);\r
8164     consoleTextWindowProc = (WNDPROC)\r
8165       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8166     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8167     consoleInputWindowProc = (WNDPROC)\r
8168       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8169     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8170     Colorize(ColorNormal, TRUE);\r
8171     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8172     ChangedConsoleFont();\r
8173     GetClientRect(hDlg, &rect);\r
8174     sizeX = rect.right;\r
8175     sizeY = rect.bottom;\r
8176     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8177         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8178       WINDOWPLACEMENT wp;\r
8179       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8180       wp.length = sizeof(WINDOWPLACEMENT);\r
8181       wp.flags = 0;\r
8182       wp.showCmd = SW_SHOW;\r
8183       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8184       wp.rcNormalPosition.left = wpConsole.x;\r
8185       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8186       wp.rcNormalPosition.top = wpConsole.y;\r
8187       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8188       SetWindowPlacement(hDlg, &wp);\r
8189     }\r
8190 \r
8191    // [HGM] Chessknight's change 2004-07-13\r
8192    else { /* Determine Defaults */\r
8193        WINDOWPLACEMENT wp;\r
8194        wpConsole.x = winWidth + 1;\r
8195        wpConsole.y = boardY;\r
8196        wpConsole.width = screenWidth -  winWidth;\r
8197        wpConsole.height = winHeight;\r
8198        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8199        wp.length = sizeof(WINDOWPLACEMENT);\r
8200        wp.flags = 0;\r
8201        wp.showCmd = SW_SHOW;\r
8202        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8203        wp.rcNormalPosition.left = wpConsole.x;\r
8204        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8205        wp.rcNormalPosition.top = wpConsole.y;\r
8206        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8207        SetWindowPlacement(hDlg, &wp);\r
8208     }\r
8209 \r
8210    // Allow hText to highlight URLs and send notifications on them\r
8211    wMask = SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
8212    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
8213    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
8214    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
8215 \r
8216     return FALSE;\r
8217 \r
8218   case WM_SETFOCUS:\r
8219     SetFocus(hInput);\r
8220     return 0;\r
8221 \r
8222   case WM_CLOSE:\r
8223     ExitEvent(0);\r
8224     /* not reached */\r
8225     break;\r
8226 \r
8227   case WM_SIZE:\r
8228     if (IsIconic(hDlg)) break;\r
8229     newSizeX = LOWORD(lParam);\r
8230     newSizeY = HIWORD(lParam);\r
8231     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8232       RECT rectText, rectInput;\r
8233       POINT pt;\r
8234       int newTextHeight, newTextWidth;\r
8235       GetWindowRect(hText, &rectText);\r
8236       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8237       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8238       if (newTextHeight < 0) {\r
8239         newSizeY += -newTextHeight;\r
8240         newTextHeight = 0;\r
8241       }\r
8242       SetWindowPos(hText, NULL, 0, 0,\r
8243         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8244       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8245       pt.x = rectInput.left;\r
8246       pt.y = rectInput.top + newSizeY - sizeY;\r
8247       ScreenToClient(hDlg, &pt);\r
8248       SetWindowPos(hInput, NULL, \r
8249         pt.x, pt.y, /* needs client coords */   \r
8250         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8251         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8252     }\r
8253     sizeX = newSizeX;\r
8254     sizeY = newSizeY;\r
8255     break;\r
8256 \r
8257   case WM_GETMINMAXINFO:\r
8258     /* Prevent resizing window too small */\r
8259     mmi = (MINMAXINFO *) lParam;\r
8260     mmi->ptMinTrackSize.x = 100;\r
8261     mmi->ptMinTrackSize.y = 100;\r
8262     break;\r
8263 \r
8264   /* [AS] Snapping */\r
8265   case WM_ENTERSIZEMOVE:\r
8266     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8267 \r
8268   case WM_SIZING:\r
8269     return OnSizing( &sd, hDlg, wParam, lParam );\r
8270 \r
8271   case WM_MOVING:\r
8272     return OnMoving( &sd, hDlg, wParam, lParam );\r
8273 \r
8274   case WM_EXITSIZEMOVE:\r
8275         UpdateICSWidth(hText);\r
8276     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8277   }\r
8278 \r
8279   return DefWindowProc(hDlg, message, wParam, lParam);\r
8280 }\r
8281 \r
8282 \r
8283 VOID\r
8284 ConsoleCreate()\r
8285 {\r
8286   HWND hCons;\r
8287   if (hwndConsole) return;\r
8288   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8289   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8290 }\r
8291 \r
8292 \r
8293 VOID\r
8294 ConsoleOutput(char* data, int length, int forceVisible)\r
8295 {\r
8296   HWND hText;\r
8297   int trim, exlen;\r
8298   char *p, *q;\r
8299   char buf[CO_MAX+1];\r
8300   POINT pEnd;\r
8301   RECT rect;\r
8302   static int delayLF = 0;\r
8303   CHARRANGE savesel, sel;\r
8304 \r
8305   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8306   p = data;\r
8307   q = buf;\r
8308   if (delayLF) {\r
8309     *q++ = '\r';\r
8310     *q++ = '\n';\r
8311     delayLF = 0;\r
8312   }\r
8313   while (length--) {\r
8314     if (*p == '\n') {\r
8315       if (*++p) {\r
8316         *q++ = '\r';\r
8317         *q++ = '\n';\r
8318       } else {\r
8319         delayLF = 1;\r
8320       }\r
8321     } else if (*p == '\007') {\r
8322        MyPlaySound(&sounds[(int)SoundBell]);\r
8323        p++;\r
8324     } else {\r
8325       *q++ = *p++;\r
8326     }\r
8327   }\r
8328   *q = NULLCHAR;\r
8329   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8330   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8331   /* Save current selection */\r
8332   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8333   exlen = GetWindowTextLength(hText);\r
8334   /* Find out whether current end of text is visible */\r
8335   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8336   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8337   /* Trim existing text if it's too long */\r
8338   if (exlen + (q - buf) > CO_MAX) {\r
8339     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8340     sel.cpMin = 0;\r
8341     sel.cpMax = trim;\r
8342     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8343     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8344     exlen -= trim;\r
8345     savesel.cpMin -= trim;\r
8346     savesel.cpMax -= trim;\r
8347     if (exlen < 0) exlen = 0;\r
8348     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8349     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8350   }\r
8351   /* Append the new text */\r
8352   sel.cpMin = exlen;\r
8353   sel.cpMax = exlen;\r
8354   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8355   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8356   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8357   if (forceVisible || exlen == 0 ||\r
8358       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8359        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8360     /* Scroll to make new end of text visible if old end of text\r
8361        was visible or new text is an echo of user typein */\r
8362     sel.cpMin = 9999999;\r
8363     sel.cpMax = 9999999;\r
8364     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8365     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8366     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8367     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8368   }\r
8369   if (savesel.cpMax == exlen || forceVisible) {\r
8370     /* Move insert point to new end of text if it was at the old\r
8371        end of text or if the new text is an echo of user typein */\r
8372     sel.cpMin = 9999999;\r
8373     sel.cpMax = 9999999;\r
8374     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8375   } else {\r
8376     /* Restore previous selection */\r
8377     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8378   }\r
8379   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8380 }\r
8381 \r
8382 /*---------*/\r
8383 \r
8384 \r
8385 void\r
8386 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8387 {\r
8388   char buf[100];\r
8389   char *str;\r
8390   COLORREF oldFg, oldBg;\r
8391   HFONT oldFont;\r
8392   RECT rect;\r
8393 \r
8394   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8395 \r
8396   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8397   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8398   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8399 \r
8400   rect.left = x;\r
8401   rect.right = x + squareSize;\r
8402   rect.top  = y;\r
8403   rect.bottom = y + squareSize;\r
8404   str = buf;\r
8405 \r
8406   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8407                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8408              y, ETO_CLIPPED|ETO_OPAQUE,\r
8409              &rect, str, strlen(str), NULL);\r
8410 \r
8411   (void) SetTextColor(hdc, oldFg);\r
8412   (void) SetBkColor(hdc, oldBg);\r
8413   (void) SelectObject(hdc, oldFont);\r
8414 }\r
8415 \r
8416 void\r
8417 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8418               RECT *rect, char *color, char *flagFell)\r
8419 {\r
8420   char buf[100];\r
8421   char *str;\r
8422   COLORREF oldFg, oldBg;\r
8423   HFONT oldFont;\r
8424 \r
8425   if (appData.clockMode) {\r
8426     if (tinyLayout)\r
8427       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8428     else\r
8429       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8430     str = buf;\r
8431   } else {\r
8432     str = color;\r
8433   }\r
8434 \r
8435   if (highlight) {\r
8436     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8437     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8438   } else {\r
8439     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8440     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8441   }\r
8442   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8443 \r
8444   JAWS_SILENCE\r
8445 \r
8446   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8447              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8448              rect, str, strlen(str), NULL);\r
8449   if(logoHeight > 0 && appData.clockMode) {\r
8450       RECT r;\r
8451       sprintf(buf, "%s %s", buf+7, flagFell);\r
8452       r.top = rect->top + logoHeight/2;\r
8453       r.left = rect->left;\r
8454       r.right = rect->right;\r
8455       r.bottom = rect->bottom;\r
8456       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8457                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8458                  &r, str, strlen(str), NULL);\r
8459   }\r
8460   (void) SetTextColor(hdc, oldFg);\r
8461   (void) SetBkColor(hdc, oldBg);\r
8462   (void) SelectObject(hdc, oldFont);\r
8463 }\r
8464 \r
8465 \r
8466 int\r
8467 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8468            OVERLAPPED *ovl)\r
8469 {\r
8470   int ok, err;\r
8471 \r
8472   /* [AS]  */\r
8473   if( count <= 0 ) {\r
8474     if (appData.debugMode) {\r
8475       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8476     }\r
8477 \r
8478     return ERROR_INVALID_USER_BUFFER;\r
8479   }\r
8480 \r
8481   ResetEvent(ovl->hEvent);\r
8482   ovl->Offset = ovl->OffsetHigh = 0;\r
8483   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8484   if (ok) {\r
8485     err = NO_ERROR;\r
8486   } else {\r
8487     err = GetLastError();\r
8488     if (err == ERROR_IO_PENDING) {\r
8489       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8490       if (ok)\r
8491         err = NO_ERROR;\r
8492       else\r
8493         err = GetLastError();\r
8494     }\r
8495   }\r
8496   return err;\r
8497 }\r
8498 \r
8499 int\r
8500 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8501             OVERLAPPED *ovl)\r
8502 {\r
8503   int ok, err;\r
8504 \r
8505   ResetEvent(ovl->hEvent);\r
8506   ovl->Offset = ovl->OffsetHigh = 0;\r
8507   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8508   if (ok) {\r
8509     err = NO_ERROR;\r
8510   } else {\r
8511     err = GetLastError();\r
8512     if (err == ERROR_IO_PENDING) {\r
8513       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8514       if (ok)\r
8515         err = NO_ERROR;\r
8516       else\r
8517         err = GetLastError();\r
8518     }\r
8519   }\r
8520   return err;\r
8521 }\r
8522 \r
8523 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8524 void CheckForInputBufferFull( InputSource * is )\r
8525 {\r
8526     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8527         /* Look for end of line */\r
8528         char * p = is->buf;\r
8529         \r
8530         while( p < is->next && *p != '\n' ) {\r
8531             p++;\r
8532         }\r
8533 \r
8534         if( p >= is->next ) {\r
8535             if (appData.debugMode) {\r
8536                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8537             }\r
8538 \r
8539             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8540             is->count = (DWORD) -1;\r
8541             is->next = is->buf;\r
8542         }\r
8543     }\r
8544 }\r
8545 \r
8546 DWORD\r
8547 InputThread(LPVOID arg)\r
8548 {\r
8549   InputSource *is;\r
8550   OVERLAPPED ovl;\r
8551 \r
8552   is = (InputSource *) arg;\r
8553   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8554   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8555   while (is->hThread != NULL) {\r
8556     is->error = DoReadFile(is->hFile, is->next,\r
8557                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8558                            &is->count, &ovl);\r
8559     if (is->error == NO_ERROR) {\r
8560       is->next += is->count;\r
8561     } else {\r
8562       if (is->error == ERROR_BROKEN_PIPE) {\r
8563         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8564         is->count = 0;\r
8565       } else {\r
8566         is->count = (DWORD) -1;\r
8567         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8568         break; \r
8569       }\r
8570     }\r
8571 \r
8572     CheckForInputBufferFull( is );\r
8573 \r
8574     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8575 \r
8576     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8577 \r
8578     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8579   }\r
8580 \r
8581   CloseHandle(ovl.hEvent);\r
8582   CloseHandle(is->hFile);\r
8583 \r
8584   if (appData.debugMode) {\r
8585     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8586   }\r
8587 \r
8588   return 0;\r
8589 }\r
8590 \r
8591 \r
8592 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8593 DWORD\r
8594 NonOvlInputThread(LPVOID arg)\r
8595 {\r
8596   InputSource *is;\r
8597   char *p, *q;\r
8598   int i;\r
8599   char prev;\r
8600 \r
8601   is = (InputSource *) arg;\r
8602   while (is->hThread != NULL) {\r
8603     is->error = ReadFile(is->hFile, is->next,\r
8604                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8605                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8606     if (is->error == NO_ERROR) {\r
8607       /* Change CRLF to LF */\r
8608       if (is->next > is->buf) {\r
8609         p = is->next - 1;\r
8610         i = is->count + 1;\r
8611       } else {\r
8612         p = is->next;\r
8613         i = is->count;\r
8614       }\r
8615       q = p;\r
8616       prev = NULLCHAR;\r
8617       while (i > 0) {\r
8618         if (prev == '\r' && *p == '\n') {\r
8619           *(q-1) = '\n';\r
8620           is->count--;\r
8621         } else { \r
8622           *q++ = *p;\r
8623         }\r
8624         prev = *p++;\r
8625         i--;\r
8626       }\r
8627       *q = NULLCHAR;\r
8628       is->next = q;\r
8629     } else {\r
8630       if (is->error == ERROR_BROKEN_PIPE) {\r
8631         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8632         is->count = 0; \r
8633       } else {\r
8634         is->count = (DWORD) -1;\r
8635       }\r
8636     }\r
8637 \r
8638     CheckForInputBufferFull( is );\r
8639 \r
8640     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8641 \r
8642     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8643 \r
8644     if (is->count < 0) break;  /* Quit on error */\r
8645   }\r
8646   CloseHandle(is->hFile);\r
8647   return 0;\r
8648 }\r
8649 \r
8650 DWORD\r
8651 SocketInputThread(LPVOID arg)\r
8652 {\r
8653   InputSource *is;\r
8654 \r
8655   is = (InputSource *) arg;\r
8656   while (is->hThread != NULL) {\r
8657     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8658     if ((int)is->count == SOCKET_ERROR) {\r
8659       is->count = (DWORD) -1;\r
8660       is->error = WSAGetLastError();\r
8661     } else {\r
8662       is->error = NO_ERROR;\r
8663       is->next += is->count;\r
8664       if (is->count == 0 && is->second == is) {\r
8665         /* End of file on stderr; quit with no message */\r
8666         break;\r
8667       }\r
8668     }\r
8669     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8670 \r
8671     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8672 \r
8673     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8674   }\r
8675   return 0;\r
8676 }\r
8677 \r
8678 VOID\r
8679 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8680 {\r
8681   InputSource *is;\r
8682 \r
8683   is = (InputSource *) lParam;\r
8684   if (is->lineByLine) {\r
8685     /* Feed in lines one by one */\r
8686     char *p = is->buf;\r
8687     char *q = p;\r
8688     while (q < is->next) {\r
8689       if (*q++ == '\n') {\r
8690         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8691         p = q;\r
8692       }\r
8693     }\r
8694     \r
8695     /* Move any partial line to the start of the buffer */\r
8696     q = is->buf;\r
8697     while (p < is->next) {\r
8698       *q++ = *p++;\r
8699     }\r
8700     is->next = q;\r
8701 \r
8702     if (is->error != NO_ERROR || is->count == 0) {\r
8703       /* Notify backend of the error.  Note: If there was a partial\r
8704          line at the end, it is not flushed through. */\r
8705       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8706     }\r
8707   } else {\r
8708     /* Feed in the whole chunk of input at once */\r
8709     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8710     is->next = is->buf;\r
8711   }\r
8712 }\r
8713 \r
8714 /*---------------------------------------------------------------------------*\\r
8715  *\r
8716  *  Menu enables. Used when setting various modes.\r
8717  *\r
8718 \*---------------------------------------------------------------------------*/\r
8719 \r
8720 typedef struct {\r
8721   int item;\r
8722   int flags;\r
8723 } Enables;\r
8724 \r
8725 VOID\r
8726 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8727 {\r
8728   while (enab->item > 0) {\r
8729     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8730     enab++;\r
8731   }\r
8732 }\r
8733 \r
8734 Enables gnuEnables[] = {\r
8735   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8736   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8737   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8738   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8739   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8740   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8741   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8742   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8743   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8744   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8745   { -1, -1 }\r
8746 };\r
8747 \r
8748 Enables icsEnables[] = {\r
8749   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8750   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8751   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8752   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8753   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8754   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8755   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8756   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8757   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8758   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8759   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8760   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8761   { -1, -1 }\r
8762 };\r
8763 \r
8764 #ifdef ZIPPY\r
8765 Enables zippyEnables[] = {\r
8766   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8767   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8768   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8769   { -1, -1 }\r
8770 };\r
8771 #endif\r
8772 \r
8773 Enables ncpEnables[] = {\r
8774   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8775   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8776   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8777   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8778   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8779   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8780   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8781   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8782   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8783   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8784   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8785   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8786   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8787   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8788   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8789   { -1, -1 }\r
8790 };\r
8791 \r
8792 Enables trainingOnEnables[] = {\r
8793   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8794   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8795   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8796   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8797   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8798   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8799   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8800   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8801   { -1, -1 }\r
8802 };\r
8803 \r
8804 Enables trainingOffEnables[] = {\r
8805   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8806   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8807   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8808   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8809   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8810   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8811   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8812   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8813   { -1, -1 }\r
8814 };\r
8815 \r
8816 /* These modify either ncpEnables or gnuEnables */\r
8817 Enables cmailEnables[] = {\r
8818   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8819   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8820   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8821   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8822   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8823   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8824   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8825   { -1, -1 }\r
8826 };\r
8827 \r
8828 Enables machineThinkingEnables[] = {\r
8829   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8830   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8831   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8832   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8833   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8834   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8835   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8836   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8837   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8838   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8839   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8840   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8841   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8842   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8843   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8844   { -1, -1 }\r
8845 };\r
8846 \r
8847 Enables userThinkingEnables[] = {\r
8848   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8849   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8850   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8851   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8852   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8853   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8854   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8855   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8856   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8857   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8858   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8859   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8860   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8861   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8862   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8863   { -1, -1 }\r
8864 };\r
8865 \r
8866 /*---------------------------------------------------------------------------*\\r
8867  *\r
8868  *  Front-end interface functions exported by XBoard.\r
8869  *  Functions appear in same order as prototypes in frontend.h.\r
8870  * \r
8871 \*---------------------------------------------------------------------------*/\r
8872 VOID\r
8873 ModeHighlight()\r
8874 {\r
8875   static UINT prevChecked = 0;\r
8876   static int prevPausing = 0;\r
8877   UINT nowChecked;\r
8878 \r
8879   if (pausing != prevPausing) {\r
8880     prevPausing = pausing;\r
8881     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8882                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8883     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8884   }\r
8885 \r
8886   switch (gameMode) {\r
8887   case BeginningOfGame:\r
8888     if (appData.icsActive)\r
8889       nowChecked = IDM_IcsClient;\r
8890     else if (appData.noChessProgram)\r
8891       nowChecked = IDM_EditGame;\r
8892     else\r
8893       nowChecked = IDM_MachineBlack;\r
8894     break;\r
8895   case MachinePlaysBlack:\r
8896     nowChecked = IDM_MachineBlack;\r
8897     break;\r
8898   case MachinePlaysWhite:\r
8899     nowChecked = IDM_MachineWhite;\r
8900     break;\r
8901   case TwoMachinesPlay:\r
8902     nowChecked = IDM_TwoMachines;\r
8903     break;\r
8904   case AnalyzeMode:\r
8905     nowChecked = IDM_AnalysisMode;\r
8906     break;\r
8907   case AnalyzeFile:\r
8908     nowChecked = IDM_AnalyzeFile;\r
8909     break;\r
8910   case EditGame:\r
8911     nowChecked = IDM_EditGame;\r
8912     break;\r
8913   case PlayFromGameFile:\r
8914     nowChecked = IDM_LoadGame;\r
8915     break;\r
8916   case EditPosition:\r
8917     nowChecked = IDM_EditPosition;\r
8918     break;\r
8919   case Training:\r
8920     nowChecked = IDM_Training;\r
8921     break;\r
8922   case IcsPlayingWhite:\r
8923   case IcsPlayingBlack:\r
8924   case IcsObserving:\r
8925   case IcsIdle:\r
8926     nowChecked = IDM_IcsClient;\r
8927     break;\r
8928   default:\r
8929   case EndOfGame:\r
8930     nowChecked = 0;\r
8931     break;\r
8932   }\r
8933   if (prevChecked != 0)\r
8934     (void) CheckMenuItem(GetMenu(hwndMain),\r
8935                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8936   if (nowChecked != 0)\r
8937     (void) CheckMenuItem(GetMenu(hwndMain),\r
8938                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8939 \r
8940   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8941     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8942                           MF_BYCOMMAND|MF_ENABLED);\r
8943   } else {\r
8944     (void) EnableMenuItem(GetMenu(hwndMain), \r
8945                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8946   }\r
8947 \r
8948   prevChecked = nowChecked;\r
8949 \r
8950   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8951   if (appData.icsActive) {\r
8952        if (appData.icsEngineAnalyze) {\r
8953                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8954                        MF_BYCOMMAND|MF_CHECKED);\r
8955        } else {\r
8956                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8957                        MF_BYCOMMAND|MF_UNCHECKED);\r
8958        }\r
8959   }\r
8960 }\r
8961 \r
8962 VOID\r
8963 SetICSMode()\r
8964 {\r
8965   HMENU hmenu = GetMenu(hwndMain);\r
8966   SetMenuEnables(hmenu, icsEnables);\r
8967   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8968     MF_BYPOSITION|MF_ENABLED);\r
8969 #ifdef ZIPPY\r
8970   if (appData.zippyPlay) {\r
8971     SetMenuEnables(hmenu, zippyEnables);\r
8972     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8973          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8974           MF_BYCOMMAND|MF_ENABLED);\r
8975   }\r
8976 #endif\r
8977 }\r
8978 \r
8979 VOID\r
8980 SetGNUMode()\r
8981 {\r
8982   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8983 }\r
8984 \r
8985 VOID\r
8986 SetNCPMode()\r
8987 {\r
8988   HMENU hmenu = GetMenu(hwndMain);\r
8989   SetMenuEnables(hmenu, ncpEnables);\r
8990   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8991     MF_BYPOSITION|MF_GRAYED);\r
8992     DrawMenuBar(hwndMain);\r
8993 }\r
8994 \r
8995 VOID\r
8996 SetCmailMode()\r
8997 {\r
8998   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8999 }\r
9000 \r
9001 VOID \r
9002 SetTrainingModeOn()\r
9003 {\r
9004   int i;\r
9005   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9006   for (i = 0; i < N_BUTTONS; i++) {\r
9007     if (buttonDesc[i].hwnd != NULL)\r
9008       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9009   }\r
9010   CommentPopDown();\r
9011 }\r
9012 \r
9013 VOID SetTrainingModeOff()\r
9014 {\r
9015   int i;\r
9016   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9017   for (i = 0; i < N_BUTTONS; i++) {\r
9018     if (buttonDesc[i].hwnd != NULL)\r
9019       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9020   }\r
9021 }\r
9022 \r
9023 \r
9024 VOID\r
9025 SetUserThinkingEnables()\r
9026 {\r
9027   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9028 }\r
9029 \r
9030 VOID\r
9031 SetMachineThinkingEnables()\r
9032 {\r
9033   HMENU hMenu = GetMenu(hwndMain);\r
9034   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9035 \r
9036   SetMenuEnables(hMenu, machineThinkingEnables);\r
9037 \r
9038   if (gameMode == MachinePlaysBlack) {\r
9039     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9040   } else if (gameMode == MachinePlaysWhite) {\r
9041     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9042   } else if (gameMode == TwoMachinesPlay) {\r
9043     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9044   }\r
9045 }\r
9046 \r
9047 \r
9048 VOID\r
9049 DisplayTitle(char *str)\r
9050 {\r
9051   char title[MSG_SIZ], *host;\r
9052   if (str[0] != NULLCHAR) {\r
9053     strcpy(title, str);\r
9054   } else if (appData.icsActive) {\r
9055     if (appData.icsCommPort[0] != NULLCHAR)\r
9056       host = "ICS";\r
9057     else \r
9058       host = appData.icsHost;\r
9059     sprintf(title, "%s: %s", szTitle, host);\r
9060   } else if (appData.noChessProgram) {\r
9061     strcpy(title, szTitle);\r
9062   } else {\r
9063     strcpy(title, szTitle);\r
9064     strcat(title, ": ");\r
9065     strcat(title, first.tidy);\r
9066   }\r
9067   SetWindowText(hwndMain, title);\r
9068 }\r
9069 \r
9070 \r
9071 VOID\r
9072 DisplayMessage(char *str1, char *str2)\r
9073 {\r
9074   HDC hdc;\r
9075   HFONT oldFont;\r
9076   int remain = MESSAGE_TEXT_MAX - 1;\r
9077   int len;\r
9078 \r
9079   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9080   messageText[0] = NULLCHAR;\r
9081   if (*str1) {\r
9082     len = strlen(str1);\r
9083     if (len > remain) len = remain;\r
9084     strncpy(messageText, str1, len);\r
9085     messageText[len] = NULLCHAR;\r
9086     remain -= len;\r
9087   }\r
9088   if (*str2 && remain >= 2) {\r
9089     if (*str1) {\r
9090       strcat(messageText, "  ");\r
9091       remain -= 2;\r
9092     }\r
9093     len = strlen(str2);\r
9094     if (len > remain) len = remain;\r
9095     strncat(messageText, str2, len);\r
9096   }\r
9097   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9098 \r
9099   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9100 \r
9101   SAYMACHINEMOVE();\r
9102 \r
9103   hdc = GetDC(hwndMain);\r
9104   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9105   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9106              &messageRect, messageText, strlen(messageText), NULL);\r
9107   (void) SelectObject(hdc, oldFont);\r
9108   (void) ReleaseDC(hwndMain, hdc);\r
9109 }\r
9110 \r
9111 VOID\r
9112 DisplayError(char *str, int error)\r
9113 {\r
9114   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9115   int len;\r
9116 \r
9117   if (error == 0) {\r
9118     strcpy(buf, str);\r
9119   } else {\r
9120     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9121                         NULL, error, LANG_NEUTRAL,\r
9122                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9123     if (len > 0) {\r
9124       sprintf(buf, "%s:\n%s", str, buf2);\r
9125     } else {\r
9126       ErrorMap *em = errmap;\r
9127       while (em->err != 0 && em->err != error) em++;\r
9128       if (em->err != 0) {\r
9129         sprintf(buf, "%s:\n%s", str, em->msg);\r
9130       } else {\r
9131         sprintf(buf, "%s:\nError code %d", str, error);\r
9132       }\r
9133     }\r
9134   }\r
9135   \r
9136   ErrorPopUp("Error", buf);\r
9137 }\r
9138 \r
9139 \r
9140 VOID\r
9141 DisplayMoveError(char *str)\r
9142 {\r
9143   fromX = fromY = -1;\r
9144   ClearHighlights();\r
9145   DrawPosition(FALSE, NULL);\r
9146   if (appData.popupMoveErrors) {\r
9147     ErrorPopUp("Error", str);\r
9148   } else {\r
9149     DisplayMessage(str, "");\r
9150     moveErrorMessageUp = TRUE;\r
9151   }\r
9152 }\r
9153 \r
9154 VOID\r
9155 DisplayFatalError(char *str, int error, int exitStatus)\r
9156 {\r
9157   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9158   int len;\r
9159   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9160 \r
9161   if (error != 0) {\r
9162     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9163                         NULL, error, LANG_NEUTRAL,\r
9164                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9165     if (len > 0) {\r
9166       sprintf(buf, "%s:\n%s", str, buf2);\r
9167     } else {\r
9168       ErrorMap *em = errmap;\r
9169       while (em->err != 0 && em->err != error) em++;\r
9170       if (em->err != 0) {\r
9171         sprintf(buf, "%s:\n%s", str, em->msg);\r
9172       } else {\r
9173         sprintf(buf, "%s:\nError code %d", str, error);\r
9174       }\r
9175     }\r
9176     str = buf;\r
9177   }\r
9178   if (appData.debugMode) {\r
9179     fprintf(debugFP, "%s: %s\n", label, str);\r
9180   }\r
9181   if (appData.popupExitMessage) {\r
9182     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9183                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9184   }\r
9185   ExitEvent(exitStatus);\r
9186 }\r
9187 \r
9188 \r
9189 VOID\r
9190 DisplayInformation(char *str)\r
9191 {\r
9192   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9193 }\r
9194 \r
9195 \r
9196 VOID\r
9197 DisplayNote(char *str)\r
9198 {\r
9199   ErrorPopUp("Note", str);\r
9200 }\r
9201 \r
9202 \r
9203 typedef struct {\r
9204   char *title, *question, *replyPrefix;\r
9205   ProcRef pr;\r
9206 } QuestionParams;\r
9207 \r
9208 LRESULT CALLBACK\r
9209 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9210 {\r
9211   static QuestionParams *qp;\r
9212   char reply[MSG_SIZ];\r
9213   int len, err;\r
9214 \r
9215   switch (message) {\r
9216   case WM_INITDIALOG:\r
9217     qp = (QuestionParams *) lParam;\r
9218     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9219     SetWindowText(hDlg, qp->title);\r
9220     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9221     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9222     return FALSE;\r
9223 \r
9224   case WM_COMMAND:\r
9225     switch (LOWORD(wParam)) {\r
9226     case IDOK:\r
9227       strcpy(reply, qp->replyPrefix);\r
9228       if (*reply) strcat(reply, " ");\r
9229       len = strlen(reply);\r
9230       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9231       strcat(reply, "\n");\r
9232       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9233       EndDialog(hDlg, TRUE);\r
9234       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9235       return TRUE;\r
9236     case IDCANCEL:\r
9237       EndDialog(hDlg, FALSE);\r
9238       return TRUE;\r
9239     default:\r
9240       break;\r
9241     }\r
9242     break;\r
9243   }\r
9244   return FALSE;\r
9245 }\r
9246 \r
9247 VOID\r
9248 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9249 {\r
9250     QuestionParams qp;\r
9251     FARPROC lpProc;\r
9252     \r
9253     qp.title = title;\r
9254     qp.question = question;\r
9255     qp.replyPrefix = replyPrefix;\r
9256     qp.pr = pr;\r
9257     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9258     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9259       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9260     FreeProcInstance(lpProc);\r
9261 }\r
9262 \r
9263 /* [AS] Pick FRC position */\r
9264 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9265 {\r
9266     static int * lpIndexFRC;\r
9267     BOOL index_is_ok;\r
9268     char buf[16];\r
9269 \r
9270     switch( message )\r
9271     {\r
9272     case WM_INITDIALOG:\r
9273         lpIndexFRC = (int *) lParam;\r
9274 \r
9275         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9276 \r
9277         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9278         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9279         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9280         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9281 \r
9282         break;\r
9283 \r
9284     case WM_COMMAND:\r
9285         switch( LOWORD(wParam) ) {\r
9286         case IDOK:\r
9287             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9288             EndDialog( hDlg, 0 );\r
9289             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9290             return TRUE;\r
9291         case IDCANCEL:\r
9292             EndDialog( hDlg, 1 );   \r
9293             return TRUE;\r
9294         case IDC_NFG_Edit:\r
9295             if( HIWORD(wParam) == EN_CHANGE ) {\r
9296                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9297 \r
9298                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9299             }\r
9300             return TRUE;\r
9301         case IDC_NFG_Random:\r
9302             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9303             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9304             return TRUE;\r
9305         }\r
9306 \r
9307         break;\r
9308     }\r
9309 \r
9310     return FALSE;\r
9311 }\r
9312 \r
9313 int NewGameFRC()\r
9314 {\r
9315     int result;\r
9316     int index = appData.defaultFrcPosition;\r
9317     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9318 \r
9319     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9320 \r
9321     if( result == 0 ) {\r
9322         appData.defaultFrcPosition = index;\r
9323     }\r
9324 \r
9325     return result;\r
9326 }\r
9327 \r
9328 /* [AS] Game list options */\r
9329 typedef struct {\r
9330     char id;\r
9331     char * name;\r
9332 } GLT_Item;\r
9333 \r
9334 static GLT_Item GLT_ItemInfo[] = {\r
9335     { GLT_EVENT,      "Event" },\r
9336     { GLT_SITE,       "Site" },\r
9337     { GLT_DATE,       "Date" },\r
9338     { GLT_ROUND,      "Round" },\r
9339     { GLT_PLAYERS,    "Players" },\r
9340     { GLT_RESULT,     "Result" },\r
9341     { GLT_WHITE_ELO,  "White Rating" },\r
9342     { GLT_BLACK_ELO,  "Black Rating" },\r
9343     { GLT_TIME_CONTROL,"Time Control" },\r
9344     { GLT_VARIANT,    "Variant" },\r
9345     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9346     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9347     { 0, 0 }\r
9348 };\r
9349 \r
9350 const char * GLT_FindItem( char id )\r
9351 {\r
9352     const char * result = 0;\r
9353 \r
9354     GLT_Item * list = GLT_ItemInfo;\r
9355 \r
9356     while( list->id != 0 ) {\r
9357         if( list->id == id ) {\r
9358             result = list->name;\r
9359             break;\r
9360         }\r
9361 \r
9362         list++;\r
9363     }\r
9364 \r
9365     return result;\r
9366 }\r
9367 \r
9368 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9369 {\r
9370     const char * name = GLT_FindItem( id );\r
9371 \r
9372     if( name != 0 ) {\r
9373         if( index >= 0 ) {\r
9374             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9375         }\r
9376         else {\r
9377             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9378         }\r
9379     }\r
9380 }\r
9381 \r
9382 void GLT_TagsToList( HWND hDlg, char * tags )\r
9383 {\r
9384     char * pc = tags;\r
9385 \r
9386     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9387 \r
9388     while( *pc ) {\r
9389         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9390         pc++;\r
9391     }\r
9392 \r
9393     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9394 \r
9395     pc = GLT_ALL_TAGS;\r
9396 \r
9397     while( *pc ) {\r
9398         if( strchr( tags, *pc ) == 0 ) {\r
9399             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9400         }\r
9401         pc++;\r
9402     }\r
9403 \r
9404     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9405 }\r
9406 \r
9407 char GLT_ListItemToTag( HWND hDlg, int index )\r
9408 {\r
9409     char result = '\0';\r
9410     char name[128];\r
9411 \r
9412     GLT_Item * list = GLT_ItemInfo;\r
9413 \r
9414     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9415         while( list->id != 0 ) {\r
9416             if( strcmp( list->name, name ) == 0 ) {\r
9417                 result = list->id;\r
9418                 break;\r
9419             }\r
9420 \r
9421             list++;\r
9422         }\r
9423     }\r
9424 \r
9425     return result;\r
9426 }\r
9427 \r
9428 void GLT_MoveSelection( HWND hDlg, int delta )\r
9429 {\r
9430     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9431     int idx2 = idx1 + delta;\r
9432     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9433 \r
9434     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9435         char buf[128];\r
9436 \r
9437         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9438         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9439         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9440         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9441     }\r
9442 }\r
9443 \r
9444 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9445 {\r
9446     static char glt[64];\r
9447     static char * lpUserGLT;\r
9448 \r
9449     switch( message )\r
9450     {\r
9451     case WM_INITDIALOG:\r
9452         lpUserGLT = (char *) lParam;\r
9453         \r
9454         strcpy( glt, lpUserGLT );\r
9455 \r
9456         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9457 \r
9458         /* Initialize list */\r
9459         GLT_TagsToList( hDlg, glt );\r
9460 \r
9461         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9462 \r
9463         break;\r
9464 \r
9465     case WM_COMMAND:\r
9466         switch( LOWORD(wParam) ) {\r
9467         case IDOK:\r
9468             {\r
9469                 char * pc = lpUserGLT;\r
9470                 int idx = 0;\r
9471 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9472                 char id;\r
9473 \r
9474                 do {\r
9475                     id = GLT_ListItemToTag( hDlg, idx );\r
9476 \r
9477                     *pc++ = id;\r
9478                     idx++;\r
9479                 } while( id != '\0' );\r
9480             }\r
9481             EndDialog( hDlg, 0 );\r
9482             return TRUE;\r
9483         case IDCANCEL:\r
9484             EndDialog( hDlg, 1 );\r
9485             return TRUE;\r
9486 \r
9487         case IDC_GLT_Default:\r
9488             strcpy( glt, GLT_DEFAULT_TAGS );\r
9489             GLT_TagsToList( hDlg, glt );\r
9490             return TRUE;\r
9491 \r
9492         case IDC_GLT_Restore:\r
9493             strcpy( glt, lpUserGLT );\r
9494             GLT_TagsToList( hDlg, glt );\r
9495             return TRUE;\r
9496 \r
9497         case IDC_GLT_Up:\r
9498             GLT_MoveSelection( hDlg, -1 );\r
9499             return TRUE;\r
9500 \r
9501         case IDC_GLT_Down:\r
9502             GLT_MoveSelection( hDlg, +1 );\r
9503             return TRUE;\r
9504         }\r
9505 \r
9506         break;\r
9507     }\r
9508 \r
9509     return FALSE;\r
9510 }\r
9511 \r
9512 int GameListOptions()\r
9513 {\r
9514     char glt[64];\r
9515     int result;\r
9516     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9517 \r
9518     strcpy( glt, appData.gameListTags );\r
9519 \r
9520     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9521 \r
9522     if( result == 0 ) {\r
9523         /* [AS] Memory leak here! */\r
9524         appData.gameListTags = strdup( glt ); \r
9525     }\r
9526 \r
9527     return result;\r
9528 }\r
9529 \r
9530 \r
9531 VOID\r
9532 DisplayIcsInteractionTitle(char *str)\r
9533 {\r
9534   char consoleTitle[MSG_SIZ];\r
9535 \r
9536   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9537   SetWindowText(hwndConsole, consoleTitle);\r
9538 }\r
9539 \r
9540 void\r
9541 DrawPosition(int fullRedraw, Board board)\r
9542 {\r
9543   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9544 }\r
9545 \r
9546 void NotifyFrontendLogin()\r
9547 {\r
9548         if (hwndConsole)\r
9549                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
9550 }\r
9551 \r
9552 VOID\r
9553 ResetFrontEnd()\r
9554 {\r
9555   fromX = fromY = -1;\r
9556   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9557     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9558     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9559     dragInfo.lastpos = dragInfo.pos;\r
9560     dragInfo.start.x = dragInfo.start.y = -1;\r
9561     dragInfo.from = dragInfo.start;\r
9562     ReleaseCapture();\r
9563     DrawPosition(TRUE, NULL);\r
9564   }\r
9565 }\r
9566 \r
9567 \r
9568 VOID\r
9569 CommentPopUp(char *title, char *str)\r
9570 {\r
9571   HWND hwnd = GetActiveWindow();\r
9572   EitherCommentPopUp(0, title, str, FALSE);\r
9573   SAY(str);\r
9574   SetActiveWindow(hwnd);\r
9575 }\r
9576 \r
9577 VOID\r
9578 CommentPopDown(void)\r
9579 {\r
9580   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9581   if (commentDialog) {\r
9582     ShowWindow(commentDialog, SW_HIDE);\r
9583   }\r
9584   commentDialogUp = FALSE;\r
9585 }\r
9586 \r
9587 VOID\r
9588 EditCommentPopUp(int index, char *title, char *str)\r
9589 {\r
9590   EitherCommentPopUp(index, title, str, TRUE);\r
9591 }\r
9592 \r
9593 \r
9594 VOID\r
9595 RingBell()\r
9596 {\r
9597   MyPlaySound(&sounds[(int)SoundMove]);\r
9598 }\r
9599 \r
9600 VOID PlayIcsWinSound()\r
9601 {\r
9602   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9603 }\r
9604 \r
9605 VOID PlayIcsLossSound()\r
9606 {\r
9607   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9608 }\r
9609 \r
9610 VOID PlayIcsDrawSound()\r
9611 {\r
9612   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9613 }\r
9614 \r
9615 VOID PlayIcsUnfinishedSound()\r
9616 {\r
9617   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9618 }\r
9619 \r
9620 VOID\r
9621 PlayAlarmSound()\r
9622 {\r
9623   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9624 }\r
9625 \r
9626 \r
9627 VOID\r
9628 EchoOn()\r
9629 {\r
9630   HWND hInput;\r
9631   consoleEcho = TRUE;\r
9632   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9633   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9634   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9635 }\r
9636 \r
9637 \r
9638 VOID\r
9639 EchoOff()\r
9640 {\r
9641   CHARFORMAT cf;\r
9642   HWND hInput;\r
9643   consoleEcho = FALSE;\r
9644   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9645   /* This works OK: set text and background both to the same color */\r
9646   cf = consoleCF;\r
9647   cf.crTextColor = COLOR_ECHOOFF;\r
9648   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9649   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9650 }\r
9651 \r
9652 /* No Raw()...? */\r
9653 \r
9654 void Colorize(ColorClass cc, int continuation)\r
9655 {\r
9656   currentColorClass = cc;\r
9657   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9658   consoleCF.crTextColor = textAttribs[cc].color;\r
9659   consoleCF.dwEffects = textAttribs[cc].effects;\r
9660   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9661 }\r
9662 \r
9663 char *\r
9664 UserName()\r
9665 {\r
9666   static char buf[MSG_SIZ];\r
9667   DWORD bufsiz = MSG_SIZ;\r
9668 \r
9669   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9670         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9671   }\r
9672   if (!GetUserName(buf, &bufsiz)) {\r
9673     /*DisplayError("Error getting user name", GetLastError());*/\r
9674     strcpy(buf, "User");\r
9675   }\r
9676   return buf;\r
9677 }\r
9678 \r
9679 char *\r
9680 HostName()\r
9681 {\r
9682   static char buf[MSG_SIZ];\r
9683   DWORD bufsiz = MSG_SIZ;\r
9684 \r
9685   if (!GetComputerName(buf, &bufsiz)) {\r
9686     /*DisplayError("Error getting host name", GetLastError());*/\r
9687     strcpy(buf, "Unknown");\r
9688   }\r
9689   return buf;\r
9690 }\r
9691 \r
9692 \r
9693 int\r
9694 ClockTimerRunning()\r
9695 {\r
9696   return clockTimerEvent != 0;\r
9697 }\r
9698 \r
9699 int\r
9700 StopClockTimer()\r
9701 {\r
9702   if (clockTimerEvent == 0) return FALSE;\r
9703   KillTimer(hwndMain, clockTimerEvent);\r
9704   clockTimerEvent = 0;\r
9705   return TRUE;\r
9706 }\r
9707 \r
9708 void\r
9709 StartClockTimer(long millisec)\r
9710 {\r
9711   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9712                              (UINT) millisec, NULL);\r
9713 }\r
9714 \r
9715 void\r
9716 DisplayWhiteClock(long timeRemaining, int highlight)\r
9717 {\r
9718   HDC hdc;\r
9719   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9720 \r
9721   if(appData.noGUI) return;\r
9722   hdc = GetDC(hwndMain);\r
9723   if (!IsIconic(hwndMain)) {\r
9724     DisplayAClock(hdc, timeRemaining, highlight, \r
9725                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9726   }\r
9727   if (highlight && iconCurrent == iconBlack) {\r
9728     iconCurrent = iconWhite;\r
9729     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9730     if (IsIconic(hwndMain)) {\r
9731       DrawIcon(hdc, 2, 2, iconCurrent);\r
9732     }\r
9733   }\r
9734   (void) ReleaseDC(hwndMain, hdc);\r
9735   if (hwndConsole)\r
9736     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9737 }\r
9738 \r
9739 void\r
9740 DisplayBlackClock(long timeRemaining, int highlight)\r
9741 {\r
9742   HDC hdc;\r
9743   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9744 \r
9745   if(appData.noGUI) return;\r
9746   hdc = GetDC(hwndMain);\r
9747   if (!IsIconic(hwndMain)) {\r
9748     DisplayAClock(hdc, timeRemaining, highlight, \r
9749                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9750   }\r
9751   if (highlight && iconCurrent == iconWhite) {\r
9752     iconCurrent = iconBlack;\r
9753     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9754     if (IsIconic(hwndMain)) {\r
9755       DrawIcon(hdc, 2, 2, iconCurrent);\r
9756     }\r
9757   }\r
9758   (void) ReleaseDC(hwndMain, hdc);\r
9759   if (hwndConsole)\r
9760     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9761 }\r
9762 \r
9763 \r
9764 int\r
9765 LoadGameTimerRunning()\r
9766 {\r
9767   return loadGameTimerEvent != 0;\r
9768 }\r
9769 \r
9770 int\r
9771 StopLoadGameTimer()\r
9772 {\r
9773   if (loadGameTimerEvent == 0) return FALSE;\r
9774   KillTimer(hwndMain, loadGameTimerEvent);\r
9775   loadGameTimerEvent = 0;\r
9776   return TRUE;\r
9777 }\r
9778 \r
9779 void\r
9780 StartLoadGameTimer(long millisec)\r
9781 {\r
9782   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9783                                 (UINT) millisec, NULL);\r
9784 }\r
9785 \r
9786 void\r
9787 AutoSaveGame()\r
9788 {\r
9789   char *defName;\r
9790   FILE *f;\r
9791   char fileTitle[MSG_SIZ];\r
9792 \r
9793   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9794   f = OpenFileDialog(hwndMain, "a", defName,\r
9795                      appData.oldSaveStyle ? "gam" : "pgn",\r
9796                      GAME_FILT, \r
9797                      "Save Game to File", NULL, fileTitle, NULL);\r
9798   if (f != NULL) {\r
9799     SaveGame(f, 0, "");\r
9800     fclose(f);\r
9801   }\r
9802 }\r
9803 \r
9804 \r
9805 void\r
9806 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9807 {\r
9808   if (delayedTimerEvent != 0) {\r
9809     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9810       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9811     }\r
9812     KillTimer(hwndMain, delayedTimerEvent);\r
9813     delayedTimerEvent = 0;\r
9814     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9815     delayedTimerCallback();\r
9816   }\r
9817   delayedTimerCallback = cb;\r
9818   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9819                                 (UINT) millisec, NULL);\r
9820 }\r
9821 \r
9822 DelayedEventCallback\r
9823 GetDelayedEvent()\r
9824 {\r
9825   if (delayedTimerEvent) {\r
9826     return delayedTimerCallback;\r
9827   } else {\r
9828     return NULL;\r
9829   }\r
9830 }\r
9831 \r
9832 void\r
9833 CancelDelayedEvent()\r
9834 {\r
9835   if (delayedTimerEvent) {\r
9836     KillTimer(hwndMain, delayedTimerEvent);\r
9837     delayedTimerEvent = 0;\r
9838   }\r
9839 }\r
9840 \r
9841 DWORD GetWin32Priority(int nice)\r
9842 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9843 /*\r
9844 REALTIME_PRIORITY_CLASS     0x00000100\r
9845 HIGH_PRIORITY_CLASS         0x00000080\r
9846 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9847 NORMAL_PRIORITY_CLASS       0x00000020\r
9848 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9849 IDLE_PRIORITY_CLASS         0x00000040\r
9850 */\r
9851         if (nice < -15) return 0x00000080;\r
9852         if (nice < 0)   return 0x00008000;\r
9853         if (nice == 0)  return 0x00000020;\r
9854         if (nice < 15)  return 0x00004000;\r
9855         return 0x00000040;\r
9856 }\r
9857 \r
9858 /* Start a child process running the given program.\r
9859    The process's standard output can be read from "from", and its\r
9860    standard input can be written to "to".\r
9861    Exit with fatal error if anything goes wrong.\r
9862    Returns an opaque pointer that can be used to destroy the process\r
9863    later.\r
9864 */\r
9865 int\r
9866 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9867 {\r
9868 #define BUFSIZE 4096\r
9869 \r
9870   HANDLE hChildStdinRd, hChildStdinWr,\r
9871     hChildStdoutRd, hChildStdoutWr;\r
9872   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9873   SECURITY_ATTRIBUTES saAttr;\r
9874   BOOL fSuccess;\r
9875   PROCESS_INFORMATION piProcInfo;\r
9876   STARTUPINFO siStartInfo;\r
9877   ChildProc *cp;\r
9878   char buf[MSG_SIZ];\r
9879   DWORD err;\r
9880 \r
9881   if (appData.debugMode) {\r
9882     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9883   }\r
9884 \r
9885   *pr = NoProc;\r
9886 \r
9887   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9888   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9889   saAttr.bInheritHandle = TRUE;\r
9890   saAttr.lpSecurityDescriptor = NULL;\r
9891 \r
9892   /*\r
9893    * The steps for redirecting child's STDOUT:\r
9894    *     1. Create anonymous pipe to be STDOUT for child.\r
9895    *     2. Create a noninheritable duplicate of read handle,\r
9896    *         and close the inheritable read handle.\r
9897    */\r
9898 \r
9899   /* Create a pipe for the child's STDOUT. */\r
9900   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9901     return GetLastError();\r
9902   }\r
9903 \r
9904   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9905   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9906                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9907                              FALSE,     /* not inherited */\r
9908                              DUPLICATE_SAME_ACCESS);\r
9909   if (! fSuccess) {\r
9910     return GetLastError();\r
9911   }\r
9912   CloseHandle(hChildStdoutRd);\r
9913 \r
9914   /*\r
9915    * The steps for redirecting child's STDIN:\r
9916    *     1. Create anonymous pipe to be STDIN for child.\r
9917    *     2. Create a noninheritable duplicate of write handle,\r
9918    *         and close the inheritable write handle.\r
9919    */\r
9920 \r
9921   /* Create a pipe for the child's STDIN. */\r
9922   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9923     return GetLastError();\r
9924   }\r
9925 \r
9926   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9927   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9928                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9929                              FALSE,     /* not inherited */\r
9930                              DUPLICATE_SAME_ACCESS);\r
9931   if (! fSuccess) {\r
9932     return GetLastError();\r
9933   }\r
9934   CloseHandle(hChildStdinWr);\r
9935 \r
9936   /* Arrange to (1) look in dir for the child .exe file, and\r
9937    * (2) have dir be the child's working directory.  Interpret\r
9938    * dir relative to the directory WinBoard loaded from. */\r
9939   GetCurrentDirectory(MSG_SIZ, buf);\r
9940   SetCurrentDirectory(installDir);\r
9941   SetCurrentDirectory(dir);\r
9942 \r
9943   /* Now create the child process. */\r
9944 \r
9945   siStartInfo.cb = sizeof(STARTUPINFO);\r
9946   siStartInfo.lpReserved = NULL;\r
9947   siStartInfo.lpDesktop = NULL;\r
9948   siStartInfo.lpTitle = NULL;\r
9949   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9950   siStartInfo.cbReserved2 = 0;\r
9951   siStartInfo.lpReserved2 = NULL;\r
9952   siStartInfo.hStdInput = hChildStdinRd;\r
9953   siStartInfo.hStdOutput = hChildStdoutWr;\r
9954   siStartInfo.hStdError = hChildStdoutWr;\r
9955 \r
9956   fSuccess = CreateProcess(NULL,\r
9957                            cmdLine,        /* command line */\r
9958                            NULL,           /* process security attributes */\r
9959                            NULL,           /* primary thread security attrs */\r
9960                            TRUE,           /* handles are inherited */\r
9961                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9962                            NULL,           /* use parent's environment */\r
9963                            NULL,\r
9964                            &siStartInfo, /* STARTUPINFO pointer */\r
9965                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9966 \r
9967   err = GetLastError();\r
9968   SetCurrentDirectory(buf); /* return to prev directory */\r
9969   if (! fSuccess) {\r
9970     return err;\r
9971   }\r
9972 \r
9973   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9974     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9975     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9976   }\r
9977 \r
9978   /* Close the handles we don't need in the parent */\r
9979   CloseHandle(piProcInfo.hThread);\r
9980   CloseHandle(hChildStdinRd);\r
9981   CloseHandle(hChildStdoutWr);\r
9982 \r
9983   /* Prepare return value */\r
9984   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9985   cp->kind = CPReal;\r
9986   cp->hProcess = piProcInfo.hProcess;\r
9987   cp->pid = piProcInfo.dwProcessId;\r
9988   cp->hFrom = hChildStdoutRdDup;\r
9989   cp->hTo = hChildStdinWrDup;\r
9990 \r
9991   *pr = (void *) cp;\r
9992 \r
9993   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9994      2000 where engines sometimes don't see the initial command(s)\r
9995      from WinBoard and hang.  I don't understand how that can happen,\r
9996      but the Sleep is harmless, so I've put it in.  Others have also\r
9997      reported what may be the same problem, so hopefully this will fix\r
9998      it for them too.  */\r
9999   Sleep(500);\r
10000 \r
10001   return NO_ERROR;\r
10002 }\r
10003 \r
10004 \r
10005 void\r
10006 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10007 {\r
10008   ChildProc *cp; int result;\r
10009 \r
10010   cp = (ChildProc *) pr;\r
10011   if (cp == NULL) return;\r
10012 \r
10013   switch (cp->kind) {\r
10014   case CPReal:\r
10015     /* TerminateProcess is considered harmful, so... */\r
10016     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10017     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10018     /* The following doesn't work because the chess program\r
10019        doesn't "have the same console" as WinBoard.  Maybe\r
10020        we could arrange for this even though neither WinBoard\r
10021        nor the chess program uses a console for stdio? */\r
10022     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10023 \r
10024     /* [AS] Special termination modes for misbehaving programs... */\r
10025     if( signal == 9 ) { \r
10026         result = TerminateProcess( cp->hProcess, 0 );\r
10027 \r
10028         if ( appData.debugMode) {\r
10029             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10030         }\r
10031     }\r
10032     else if( signal == 10 ) {\r
10033         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10034 \r
10035         if( dw != WAIT_OBJECT_0 ) {\r
10036             result = TerminateProcess( cp->hProcess, 0 );\r
10037 \r
10038             if ( appData.debugMode) {\r
10039                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10040             }\r
10041 \r
10042         }\r
10043     }\r
10044 \r
10045     CloseHandle(cp->hProcess);\r
10046     break;\r
10047 \r
10048   case CPComm:\r
10049     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10050     break;\r
10051 \r
10052   case CPSock:\r
10053     closesocket(cp->sock);\r
10054     WSACleanup();\r
10055     break;\r
10056 \r
10057   case CPRcmd:\r
10058     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10059     closesocket(cp->sock);\r
10060     closesocket(cp->sock2);\r
10061     WSACleanup();\r
10062     break;\r
10063   }\r
10064   free(cp);\r
10065 }\r
10066 \r
10067 void\r
10068 InterruptChildProcess(ProcRef pr)\r
10069 {\r
10070   ChildProc *cp;\r
10071 \r
10072   cp = (ChildProc *) pr;\r
10073   if (cp == NULL) return;\r
10074   switch (cp->kind) {\r
10075   case CPReal:\r
10076     /* The following doesn't work because the chess program\r
10077        doesn't "have the same console" as WinBoard.  Maybe\r
10078        we could arrange for this even though neither WinBoard\r
10079        nor the chess program uses a console for stdio */\r
10080     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10081     break;\r
10082 \r
10083   case CPComm:\r
10084   case CPSock:\r
10085     /* Can't interrupt */\r
10086     break;\r
10087 \r
10088   case CPRcmd:\r
10089     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10090     break;\r
10091   }\r
10092 }\r
10093 \r
10094 \r
10095 int\r
10096 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10097 {\r
10098   char cmdLine[MSG_SIZ];\r
10099 \r
10100   if (port[0] == NULLCHAR) {\r
10101     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10102   } else {\r
10103     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10104   }\r
10105   return StartChildProcess(cmdLine, "", pr);\r
10106 }\r
10107 \r
10108 \r
10109 /* Code to open TCP sockets */\r
10110 \r
10111 int\r
10112 OpenTCP(char *host, char *port, ProcRef *pr)\r
10113 {\r
10114   ChildProc *cp;\r
10115   int err;\r
10116   SOCKET s;\r
10117   struct sockaddr_in sa, mysa;\r
10118   struct hostent FAR *hp;\r
10119   unsigned short uport;\r
10120   WORD wVersionRequested;\r
10121   WSADATA wsaData;\r
10122 \r
10123   /* Initialize socket DLL */\r
10124   wVersionRequested = MAKEWORD(1, 1);\r
10125   err = WSAStartup(wVersionRequested, &wsaData);\r
10126   if (err != 0) return err;\r
10127 \r
10128   /* Make socket */\r
10129   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10130     err = WSAGetLastError();\r
10131     WSACleanup();\r
10132     return err;\r
10133   }\r
10134 \r
10135   /* Bind local address using (mostly) don't-care values.\r
10136    */\r
10137   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10138   mysa.sin_family = AF_INET;\r
10139   mysa.sin_addr.s_addr = INADDR_ANY;\r
10140   uport = (unsigned short) 0;\r
10141   mysa.sin_port = htons(uport);\r
10142   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10143       == SOCKET_ERROR) {\r
10144     err = WSAGetLastError();\r
10145     WSACleanup();\r
10146     return err;\r
10147   }\r
10148 \r
10149   /* Resolve remote host name */\r
10150   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10151   if (!(hp = gethostbyname(host))) {\r
10152     unsigned int b0, b1, b2, b3;\r
10153 \r
10154     err = WSAGetLastError();\r
10155 \r
10156     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10157       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10158       hp->h_addrtype = AF_INET;\r
10159       hp->h_length = 4;\r
10160       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10161       hp->h_addr_list[0] = (char *) malloc(4);\r
10162       hp->h_addr_list[0][0] = (char) b0;\r
10163       hp->h_addr_list[0][1] = (char) b1;\r
10164       hp->h_addr_list[0][2] = (char) b2;\r
10165       hp->h_addr_list[0][3] = (char) b3;\r
10166     } else {\r
10167       WSACleanup();\r
10168       return err;\r
10169     }\r
10170   }\r
10171   sa.sin_family = hp->h_addrtype;\r
10172   uport = (unsigned short) atoi(port);\r
10173   sa.sin_port = htons(uport);\r
10174   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10175 \r
10176   /* Make connection */\r
10177   if (connect(s, (struct sockaddr *) &sa,\r
10178               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10179     err = WSAGetLastError();\r
10180     WSACleanup();\r
10181     return err;\r
10182   }\r
10183 \r
10184   /* Prepare return value */\r
10185   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10186   cp->kind = CPSock;\r
10187   cp->sock = s;\r
10188   *pr = (ProcRef *) cp;\r
10189 \r
10190   return NO_ERROR;\r
10191 }\r
10192 \r
10193 int\r
10194 OpenCommPort(char *name, ProcRef *pr)\r
10195 {\r
10196   HANDLE h;\r
10197   COMMTIMEOUTS ct;\r
10198   ChildProc *cp;\r
10199   char fullname[MSG_SIZ];\r
10200 \r
10201   if (*name != '\\')\r
10202     sprintf(fullname, "\\\\.\\%s", name);\r
10203   else\r
10204     strcpy(fullname, name);\r
10205 \r
10206   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10207                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10208   if (h == (HANDLE) -1) {\r
10209     return GetLastError();\r
10210   }\r
10211   hCommPort = h;\r
10212 \r
10213   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10214 \r
10215   /* Accumulate characters until a 100ms pause, then parse */\r
10216   ct.ReadIntervalTimeout = 100;\r
10217   ct.ReadTotalTimeoutMultiplier = 0;\r
10218   ct.ReadTotalTimeoutConstant = 0;\r
10219   ct.WriteTotalTimeoutMultiplier = 0;\r
10220   ct.WriteTotalTimeoutConstant = 0;\r
10221   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10222 \r
10223   /* Prepare return value */\r
10224   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10225   cp->kind = CPComm;\r
10226   cp->hFrom = h;\r
10227   cp->hTo = h;\r
10228   *pr = (ProcRef *) cp;\r
10229 \r
10230   return NO_ERROR;\r
10231 }\r
10232 \r
10233 int\r
10234 OpenLoopback(ProcRef *pr)\r
10235 {\r
10236   DisplayFatalError("Not implemented", 0, 1);\r
10237   return NO_ERROR;\r
10238 }\r
10239 \r
10240 \r
10241 int\r
10242 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10243 {\r
10244   ChildProc *cp;\r
10245   int err;\r
10246   SOCKET s, s2, s3;\r
10247   struct sockaddr_in sa, mysa;\r
10248   struct hostent FAR *hp;\r
10249   unsigned short uport;\r
10250   WORD wVersionRequested;\r
10251   WSADATA wsaData;\r
10252   int fromPort;\r
10253   char stderrPortStr[MSG_SIZ];\r
10254 \r
10255   /* Initialize socket DLL */\r
10256   wVersionRequested = MAKEWORD(1, 1);\r
10257   err = WSAStartup(wVersionRequested, &wsaData);\r
10258   if (err != 0) return err;\r
10259 \r
10260   /* Resolve remote host name */\r
10261   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10262   if (!(hp = gethostbyname(host))) {\r
10263     unsigned int b0, b1, b2, b3;\r
10264 \r
10265     err = WSAGetLastError();\r
10266 \r
10267     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10268       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10269       hp->h_addrtype = AF_INET;\r
10270       hp->h_length = 4;\r
10271       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10272       hp->h_addr_list[0] = (char *) malloc(4);\r
10273       hp->h_addr_list[0][0] = (char) b0;\r
10274       hp->h_addr_list[0][1] = (char) b1;\r
10275       hp->h_addr_list[0][2] = (char) b2;\r
10276       hp->h_addr_list[0][3] = (char) b3;\r
10277     } else {\r
10278       WSACleanup();\r
10279       return err;\r
10280     }\r
10281   }\r
10282   sa.sin_family = hp->h_addrtype;\r
10283   uport = (unsigned short) 514;\r
10284   sa.sin_port = htons(uport);\r
10285   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10286 \r
10287   /* Bind local socket to unused "privileged" port address\r
10288    */\r
10289   s = INVALID_SOCKET;\r
10290   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10291   mysa.sin_family = AF_INET;\r
10292   mysa.sin_addr.s_addr = INADDR_ANY;\r
10293   for (fromPort = 1023;; fromPort--) {\r
10294     if (fromPort < 0) {\r
10295       WSACleanup();\r
10296       return WSAEADDRINUSE;\r
10297     }\r
10298     if (s == INVALID_SOCKET) {\r
10299       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10300         err = WSAGetLastError();\r
10301         WSACleanup();\r
10302         return err;\r
10303       }\r
10304     }\r
10305     uport = (unsigned short) fromPort;\r
10306     mysa.sin_port = htons(uport);\r
10307     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10308         == SOCKET_ERROR) {\r
10309       err = WSAGetLastError();\r
10310       if (err == WSAEADDRINUSE) continue;\r
10311       WSACleanup();\r
10312       return err;\r
10313     }\r
10314     if (connect(s, (struct sockaddr *) &sa,\r
10315       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10316       err = WSAGetLastError();\r
10317       if (err == WSAEADDRINUSE) {\r
10318         closesocket(s);\r
10319         s = -1;\r
10320         continue;\r
10321       }\r
10322       WSACleanup();\r
10323       return err;\r
10324     }\r
10325     break;\r
10326   }\r
10327 \r
10328   /* Bind stderr local socket to unused "privileged" port address\r
10329    */\r
10330   s2 = INVALID_SOCKET;\r
10331   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10332   mysa.sin_family = AF_INET;\r
10333   mysa.sin_addr.s_addr = INADDR_ANY;\r
10334   for (fromPort = 1023;; fromPort--) {\r
10335     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10336     if (fromPort < 0) {\r
10337       (void) closesocket(s);\r
10338       WSACleanup();\r
10339       return WSAEADDRINUSE;\r
10340     }\r
10341     if (s2 == INVALID_SOCKET) {\r
10342       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10343         err = WSAGetLastError();\r
10344         closesocket(s);\r
10345         WSACleanup();\r
10346         return err;\r
10347       }\r
10348     }\r
10349     uport = (unsigned short) fromPort;\r
10350     mysa.sin_port = htons(uport);\r
10351     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10352         == SOCKET_ERROR) {\r
10353       err = WSAGetLastError();\r
10354       if (err == WSAEADDRINUSE) continue;\r
10355       (void) closesocket(s);\r
10356       WSACleanup();\r
10357       return err;\r
10358     }\r
10359     if (listen(s2, 1) == SOCKET_ERROR) {\r
10360       err = WSAGetLastError();\r
10361       if (err == WSAEADDRINUSE) {\r
10362         closesocket(s2);\r
10363         s2 = INVALID_SOCKET;\r
10364         continue;\r
10365       }\r
10366       (void) closesocket(s);\r
10367       (void) closesocket(s2);\r
10368       WSACleanup();\r
10369       return err;\r
10370     }\r
10371     break;\r
10372   }\r
10373   prevStderrPort = fromPort; // remember port used\r
10374   sprintf(stderrPortStr, "%d", fromPort);\r
10375 \r
10376   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10377     err = WSAGetLastError();\r
10378     (void) closesocket(s);\r
10379     (void) closesocket(s2);\r
10380     WSACleanup();\r
10381     return err;\r
10382   }\r
10383 \r
10384   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10385     err = WSAGetLastError();\r
10386     (void) closesocket(s);\r
10387     (void) closesocket(s2);\r
10388     WSACleanup();\r
10389     return err;\r
10390   }\r
10391   if (*user == NULLCHAR) user = UserName();\r
10392   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10393     err = WSAGetLastError();\r
10394     (void) closesocket(s);\r
10395     (void) closesocket(s2);\r
10396     WSACleanup();\r
10397     return err;\r
10398   }\r
10399   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10400     err = WSAGetLastError();\r
10401     (void) closesocket(s);\r
10402     (void) closesocket(s2);\r
10403     WSACleanup();\r
10404     return err;\r
10405   }\r
10406 \r
10407   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10408     err = WSAGetLastError();\r
10409     (void) closesocket(s);\r
10410     (void) closesocket(s2);\r
10411     WSACleanup();\r
10412     return err;\r
10413   }\r
10414   (void) closesocket(s2);  /* Stop listening */\r
10415 \r
10416   /* Prepare return value */\r
10417   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10418   cp->kind = CPRcmd;\r
10419   cp->sock = s;\r
10420   cp->sock2 = s3;\r
10421   *pr = (ProcRef *) cp;\r
10422 \r
10423   return NO_ERROR;\r
10424 }\r
10425 \r
10426 \r
10427 InputSourceRef\r
10428 AddInputSource(ProcRef pr, int lineByLine,\r
10429                InputCallback func, VOIDSTAR closure)\r
10430 {\r
10431   InputSource *is, *is2 = NULL;\r
10432   ChildProc *cp = (ChildProc *) pr;\r
10433 \r
10434   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10435   is->lineByLine = lineByLine;\r
10436   is->func = func;\r
10437   is->closure = closure;\r
10438   is->second = NULL;\r
10439   is->next = is->buf;\r
10440   if (pr == NoProc) {\r
10441     is->kind = CPReal;\r
10442     consoleInputSource = is;\r
10443   } else {\r
10444     is->kind = cp->kind;\r
10445     /* \r
10446         [AS] Try to avoid a race condition if the thread is given control too early:\r
10447         we create all threads suspended so that the is->hThread variable can be\r
10448         safely assigned, then let the threads start with ResumeThread.\r
10449     */\r
10450     switch (cp->kind) {\r
10451     case CPReal:\r
10452       is->hFile = cp->hFrom;\r
10453       cp->hFrom = NULL; /* now owned by InputThread */\r
10454       is->hThread =\r
10455         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10456                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10457       break;\r
10458 \r
10459     case CPComm:\r
10460       is->hFile = cp->hFrom;\r
10461       cp->hFrom = NULL; /* now owned by InputThread */\r
10462       is->hThread =\r
10463         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10464                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10465       break;\r
10466 \r
10467     case CPSock:\r
10468       is->sock = cp->sock;\r
10469       is->hThread =\r
10470         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10471                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10472       break;\r
10473 \r
10474     case CPRcmd:\r
10475       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10476       *is2 = *is;\r
10477       is->sock = cp->sock;\r
10478       is->second = is2;\r
10479       is2->sock = cp->sock2;\r
10480       is2->second = is2;\r
10481       is->hThread =\r
10482         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10483                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10484       is2->hThread =\r
10485         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10486                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10487       break;\r
10488     }\r
10489 \r
10490     if( is->hThread != NULL ) {\r
10491         ResumeThread( is->hThread );\r
10492     }\r
10493 \r
10494     if( is2 != NULL && is2->hThread != NULL ) {\r
10495         ResumeThread( is2->hThread );\r
10496     }\r
10497   }\r
10498 \r
10499   return (InputSourceRef) is;\r
10500 }\r
10501 \r
10502 void\r
10503 RemoveInputSource(InputSourceRef isr)\r
10504 {\r
10505   InputSource *is;\r
10506 \r
10507   is = (InputSource *) isr;\r
10508   is->hThread = NULL;  /* tell thread to stop */\r
10509   CloseHandle(is->hThread);\r
10510   if (is->second != NULL) {\r
10511     is->second->hThread = NULL;\r
10512     CloseHandle(is->second->hThread);\r
10513   }\r
10514 }\r
10515 \r
10516 \r
10517 int\r
10518 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10519 {\r
10520   DWORD dOutCount;\r
10521   int outCount = SOCKET_ERROR;\r
10522   ChildProc *cp = (ChildProc *) pr;\r
10523   static OVERLAPPED ovl;\r
10524 \r
10525   if (pr == NoProc) {\r
10526     ConsoleOutput(message, count, FALSE);\r
10527     return count;\r
10528   } \r
10529 \r
10530   if (ovl.hEvent == NULL) {\r
10531     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10532   }\r
10533   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10534 \r
10535   switch (cp->kind) {\r
10536   case CPSock:\r
10537   case CPRcmd:\r
10538     outCount = send(cp->sock, message, count, 0);\r
10539     if (outCount == SOCKET_ERROR) {\r
10540       *outError = WSAGetLastError();\r
10541     } else {\r
10542       *outError = NO_ERROR;\r
10543     }\r
10544     break;\r
10545 \r
10546   case CPReal:\r
10547     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10548                   &dOutCount, NULL)) {\r
10549       *outError = NO_ERROR;\r
10550       outCount = (int) dOutCount;\r
10551     } else {\r
10552       *outError = GetLastError();\r
10553     }\r
10554     break;\r
10555 \r
10556   case CPComm:\r
10557     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10558                             &dOutCount, &ovl);\r
10559     if (*outError == NO_ERROR) {\r
10560       outCount = (int) dOutCount;\r
10561     }\r
10562     break;\r
10563   }\r
10564   return outCount;\r
10565 }\r
10566 \r
10567 int\r
10568 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10569                        long msdelay)\r
10570 {\r
10571   /* Ignore delay, not implemented for WinBoard */\r
10572   return OutputToProcess(pr, message, count, outError);\r
10573 }\r
10574 \r
10575 \r
10576 void\r
10577 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10578                         char *buf, int count, int error)\r
10579 {\r
10580   DisplayFatalError("Not implemented", 0, 1);\r
10581 }\r
10582 \r
10583 /* see wgamelist.c for Game List functions */\r
10584 /* see wedittags.c for Edit Tags functions */\r
10585 \r
10586 \r
10587 VOID\r
10588 ICSInitScript()\r
10589 {\r
10590   FILE *f;\r
10591   char buf[MSG_SIZ];\r
10592   char *dummy;\r
10593 \r
10594   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10595     f = fopen(buf, "r");\r
10596     if (f != NULL) {\r
10597       ProcessICSInitScript(f);\r
10598       fclose(f);\r
10599     }\r
10600   }\r
10601 }\r
10602 \r
10603 \r
10604 VOID\r
10605 StartAnalysisClock()\r
10606 {\r
10607   if (analysisTimerEvent) return;\r
10608   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10609                                         (UINT) 2000, NULL);\r
10610 }\r
10611 \r
10612 LRESULT CALLBACK\r
10613 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10614 {\r
10615   static HANDLE hwndText;\r
10616   RECT rect;\r
10617   static int sizeX, sizeY;\r
10618   int newSizeX, newSizeY, flags;\r
10619   MINMAXINFO *mmi;\r
10620 \r
10621   switch (message) {\r
10622   case WM_INITDIALOG: /* message: initialize dialog box */\r
10623     /* Initialize the dialog items */\r
10624     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10625     SetWindowText(hDlg, analysisTitle);\r
10626     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10627     /* Size and position the dialog */\r
10628     if (!analysisDialog) {\r
10629       analysisDialog = hDlg;\r
10630       flags = SWP_NOZORDER;\r
10631       GetClientRect(hDlg, &rect);\r
10632       sizeX = rect.right;\r
10633       sizeY = rect.bottom;\r
10634       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10635           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10636         WINDOWPLACEMENT wp;\r
10637         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10638         wp.length = sizeof(WINDOWPLACEMENT);\r
10639         wp.flags = 0;\r
10640         wp.showCmd = SW_SHOW;\r
10641         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10642         wp.rcNormalPosition.left = analysisX;\r
10643         wp.rcNormalPosition.right = analysisX + analysisW;\r
10644         wp.rcNormalPosition.top = analysisY;\r
10645         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10646         SetWindowPlacement(hDlg, &wp);\r
10647 \r
10648         GetClientRect(hDlg, &rect);\r
10649         newSizeX = rect.right;\r
10650         newSizeY = rect.bottom;\r
10651         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10652                               newSizeX, newSizeY);\r
10653         sizeX = newSizeX;\r
10654         sizeY = newSizeY;\r
10655       }\r
10656     }\r
10657     return FALSE;\r
10658 \r
10659   case WM_COMMAND: /* message: received a command */\r
10660     switch (LOWORD(wParam)) {\r
10661     case IDCANCEL:\r
10662       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10663           ExitAnalyzeMode();\r
10664           ModeHighlight();\r
10665           return TRUE;\r
10666       }\r
10667       EditGameEvent();\r
10668       return TRUE;\r
10669     default:\r
10670       break;\r
10671     }\r
10672     break;\r
10673 \r
10674   case WM_SIZE:\r
10675     newSizeX = LOWORD(lParam);\r
10676     newSizeY = HIWORD(lParam);\r
10677     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10678     sizeX = newSizeX;\r
10679     sizeY = newSizeY;\r
10680     break;\r
10681 \r
10682   case WM_GETMINMAXINFO:\r
10683     /* Prevent resizing window too small */\r
10684     mmi = (MINMAXINFO *) lParam;\r
10685     mmi->ptMinTrackSize.x = 100;\r
10686     mmi->ptMinTrackSize.y = 100;\r
10687     break;\r
10688   }\r
10689   return FALSE;\r
10690 }\r
10691 \r
10692 VOID\r
10693 AnalysisPopUp(char* title, char* str)\r
10694 {\r
10695   FARPROC lpProc;\r
10696   char *p, *q;\r
10697 \r
10698   /* [AS] */\r
10699   EngineOutputPopUp();\r
10700   return;\r
10701 \r
10702   if (str == NULL) str = "";\r
10703   p = (char *) malloc(2 * strlen(str) + 2);\r
10704   q = p;\r
10705   while (*str) {\r
10706     if (*str == '\n') *q++ = '\r';\r
10707     *q++ = *str++;\r
10708   }\r
10709   *q = NULLCHAR;\r
10710   if (analysisText != NULL) free(analysisText);\r
10711   analysisText = p;\r
10712 \r
10713   if (analysisDialog) {\r
10714     SetWindowText(analysisDialog, title);\r
10715     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10716     ShowWindow(analysisDialog, SW_SHOW);\r
10717   } else {\r
10718     analysisTitle = title;\r
10719     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10720     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10721                  hwndMain, (DLGPROC)lpProc);\r
10722     FreeProcInstance(lpProc);\r
10723   }\r
10724   analysisDialogUp = TRUE;  \r
10725 }\r
10726 \r
10727 VOID\r
10728 AnalysisPopDown()\r
10729 {\r
10730   if (analysisDialog) {\r
10731     ShowWindow(analysisDialog, SW_HIDE);\r
10732   }\r
10733   analysisDialogUp = FALSE;  \r
10734 }\r
10735 \r
10736 \r
10737 VOID\r
10738 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10739 {\r
10740   highlightInfo.sq[0].x = fromX;\r
10741   highlightInfo.sq[0].y = fromY;\r
10742   highlightInfo.sq[1].x = toX;\r
10743   highlightInfo.sq[1].y = toY;\r
10744 }\r
10745 \r
10746 VOID\r
10747 ClearHighlights()\r
10748 {\r
10749   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10750     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10751 }\r
10752 \r
10753 VOID\r
10754 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10755 {\r
10756   premoveHighlightInfo.sq[0].x = fromX;\r
10757   premoveHighlightInfo.sq[0].y = fromY;\r
10758   premoveHighlightInfo.sq[1].x = toX;\r
10759   premoveHighlightInfo.sq[1].y = toY;\r
10760 }\r
10761 \r
10762 VOID\r
10763 ClearPremoveHighlights()\r
10764 {\r
10765   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10766     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10767 }\r
10768 \r
10769 VOID\r
10770 ShutDownFrontEnd()\r
10771 {\r
10772   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10773   DeleteClipboardTempFiles();\r
10774 }\r
10775 \r
10776 void\r
10777 BoardToTop()\r
10778 {\r
10779     if (IsIconic(hwndMain))\r
10780       ShowWindow(hwndMain, SW_RESTORE);\r
10781 \r
10782     SetActiveWindow(hwndMain);\r
10783 }\r
10784 \r
10785 /*\r
10786  * Prototypes for animation support routines\r
10787  */\r
10788 static void ScreenSquare(int column, int row, POINT * pt);\r
10789 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10790      POINT frames[], int * nFrames);\r
10791 \r
10792 \r
10793 void\r
10794 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10795 {       // [HGM] atomic: animate blast wave\r
10796         int i;\r
10797 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10798         explodeInfo.fromX = fromX;\r
10799         explodeInfo.fromY = fromY;\r
10800         explodeInfo.toX = toX;\r
10801         explodeInfo.toY = toY;\r
10802         for(i=1; i<nFrames; i++) {\r
10803             explodeInfo.radius = (i*180)/(nFrames-1);\r
10804             DrawPosition(FALSE, NULL);\r
10805             Sleep(appData.animSpeed);\r
10806         }\r
10807         explodeInfo.radius = 0;\r
10808         DrawPosition(TRUE, NULL);\r
10809 }\r
10810 \r
10811 #define kFactor 4\r
10812 \r
10813 void\r
10814 AnimateMove(board, fromX, fromY, toX, toY)\r
10815      Board board;\r
10816      int fromX;\r
10817      int fromY;\r
10818      int toX;\r
10819      int toY;\r
10820 {\r
10821   ChessSquare piece;\r
10822   POINT start, finish, mid;\r
10823   POINT frames[kFactor * 2 + 1];\r
10824   int nFrames, n;\r
10825 \r
10826   if (!appData.animate) return;\r
10827   if (doingSizing) return;\r
10828   if (fromY < 0 || fromX < 0) return;\r
10829   piece = board[fromY][fromX];\r
10830   if (piece >= EmptySquare) return;\r
10831 \r
10832   ScreenSquare(fromX, fromY, &start);\r
10833   ScreenSquare(toX, toY, &finish);\r
10834 \r
10835   /* All pieces except knights move in straight line */\r
10836   if (piece != WhiteKnight && piece != BlackKnight) {\r
10837     mid.x = start.x + (finish.x - start.x) / 2;\r
10838     mid.y = start.y + (finish.y - start.y) / 2;\r
10839   } else {\r
10840     /* Knight: make diagonal movement then straight */\r
10841     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10842        mid.x = start.x + (finish.x - start.x) / 2;\r
10843        mid.y = finish.y;\r
10844      } else {\r
10845        mid.x = finish.x;\r
10846        mid.y = start.y + (finish.y - start.y) / 2;\r
10847      }\r
10848   }\r
10849   \r
10850   /* Don't use as many frames for very short moves */\r
10851   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10852     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10853   else\r
10854     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10855 \r
10856   animInfo.from.x = fromX;\r
10857   animInfo.from.y = fromY;\r
10858   animInfo.to.x = toX;\r
10859   animInfo.to.y = toY;\r
10860   animInfo.lastpos = start;\r
10861   animInfo.piece = piece;\r
10862   for (n = 0; n < nFrames; n++) {\r
10863     animInfo.pos = frames[n];\r
10864     DrawPosition(FALSE, NULL);\r
10865     animInfo.lastpos = animInfo.pos;\r
10866     Sleep(appData.animSpeed);\r
10867   }\r
10868   animInfo.pos = finish;\r
10869   DrawPosition(FALSE, NULL);\r
10870   animInfo.piece = EmptySquare;\r
10871   if(gameInfo.variant == VariantAtomic && \r
10872      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10873         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10874 }\r
10875 \r
10876 /*      Convert board position to corner of screen rect and color       */\r
10877 \r
10878 static void\r
10879 ScreenSquare(column, row, pt)\r
10880      int column; int row; POINT * pt;\r
10881 {\r
10882   if (flipView) {\r
10883     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10884     pt->y = lineGap + row * (squareSize + lineGap);\r
10885   } else {\r
10886     pt->x = lineGap + column * (squareSize + lineGap);\r
10887     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10888   }\r
10889 }\r
10890 \r
10891 /*      Generate a series of frame coords from start->mid->finish.\r
10892         The movement rate doubles until the half way point is\r
10893         reached, then halves back down to the final destination,\r
10894         which gives a nice slow in/out effect. The algorithmn\r
10895         may seem to generate too many intermediates for short\r
10896         moves, but remember that the purpose is to attract the\r
10897         viewers attention to the piece about to be moved and\r
10898         then to where it ends up. Too few frames would be less\r
10899         noticeable.                                             */\r
10900 \r
10901 static void\r
10902 Tween(start, mid, finish, factor, frames, nFrames)\r
10903      POINT * start; POINT * mid;\r
10904      POINT * finish; int factor;\r
10905      POINT frames[]; int * nFrames;\r
10906 {\r
10907   int n, fraction = 1, count = 0;\r
10908 \r
10909   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10910   for (n = 0; n < factor; n++)\r
10911     fraction *= 2;\r
10912   for (n = 0; n < factor; n++) {\r
10913     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10914     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10915     count ++;\r
10916     fraction = fraction / 2;\r
10917   }\r
10918   \r
10919   /* Midpoint */\r
10920   frames[count] = *mid;\r
10921   count ++;\r
10922   \r
10923   /* Slow out, stepping 1/2, then 1/4, ... */\r
10924   fraction = 2;\r
10925   for (n = 0; n < factor; n++) {\r
10926     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10927     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10928     count ++;\r
10929     fraction = fraction * 2;\r
10930   }\r
10931   *nFrames = count;\r
10932 }\r
10933 \r
10934 void\r
10935 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10936 {\r
10937     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10938 \r
10939     EvalGraphSet( first, last, current, pvInfoList );\r
10940 }\r
10941 \r
10942 void SetProgramStats( FrontEndProgramStats * stats )\r
10943 {\r
10944     EngineOutputUpdate( stats );\r
10945 }\r