fix for keepalive and chat window
[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 #define oldDialog (_winmajor < 4)\r
229 #endif\r
230 \r
231 char *defaultTextAttribs[] = \r
232 {\r
233   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
234   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
235   COLOR_NONE\r
236 };\r
237 \r
238 typedef struct {\r
239   char *name;\r
240   int squareSize;\r
241   int lineGap;\r
242   int smallLayout;\r
243   int tinyLayout;\r
244   int cliWidth, cliHeight;\r
245 } SizeInfo;\r
246 \r
247 SizeInfo sizeInfo[] = \r
248 {\r
249   { "tiny",     21, 0, 1, 1, 0, 0 },\r
250   { "teeny",    25, 1, 1, 1, 0, 0 },\r
251   { "dinky",    29, 1, 1, 1, 0, 0 },\r
252   { "petite",   33, 1, 1, 1, 0, 0 },\r
253   { "slim",     37, 2, 1, 0, 0, 0 },\r
254   { "small",    40, 2, 1, 0, 0, 0 },\r
255   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
256   { "middling", 49, 2, 0, 0, 0, 0 },\r
257   { "average",  54, 2, 0, 0, 0, 0 },\r
258   { "moderate", 58, 3, 0, 0, 0, 0 },\r
259   { "medium",   64, 3, 0, 0, 0, 0 },\r
260   { "bulky",    72, 3, 0, 0, 0, 0 },\r
261   { "large",    80, 3, 0, 0, 0, 0 },\r
262   { "big",      87, 3, 0, 0, 0, 0 },\r
263   { "huge",     95, 3, 0, 0, 0, 0 },\r
264   { "giant",    108, 3, 0, 0, 0, 0 },\r
265   { "colossal", 116, 4, 0, 0, 0, 0 },\r
266   { "titanic",  129, 4, 0, 0, 0, 0 },\r
267   { NULL, 0, 0, 0, 0, 0, 0 }\r
268 };\r
269 \r
270 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
271 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
272 {\r
273   { 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
274   { 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
275   { 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
276   { 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
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283   { 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
284   { 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
285   { 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
286   { 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
287   { 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
288   { 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
289   { 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
290   { 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
291 };\r
292 \r
293 MyFont *font[NUM_SIZES][NUM_FONTS];\r
294 \r
295 typedef struct {\r
296   char *label;\r
297   int id;\r
298   HWND hwnd;\r
299   WNDPROC wndproc;\r
300 } MyButtonDesc;\r
301 \r
302 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
303 #define N_BUTTONS 5\r
304 \r
305 MyButtonDesc buttonDesc[N_BUTTONS] =\r
306 {\r
307   {"<<", IDM_ToStart, NULL, NULL},\r
308   {"<", IDM_Backward, NULL, NULL},\r
309   {"P", IDM_Pause, NULL, NULL},\r
310   {">", IDM_Forward, NULL, NULL},\r
311   {">>", IDM_ToEnd, NULL, NULL},\r
312 };\r
313 \r
314 int tinyLayout = 0, smallLayout = 0;\r
315 #define MENU_BAR_ITEMS 7\r
316 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
317   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
318   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
319 };\r
320 \r
321 \r
322 MySound sounds[(int)NSoundClasses];\r
323 MyTextAttribs textAttribs[(int)NColorClasses];\r
324 \r
325 MyColorizeAttribs colorizeAttribs[] = {\r
326   { (COLORREF)0, 0, "Shout Text" },\r
327   { (COLORREF)0, 0, "SShout/CShout" },\r
328   { (COLORREF)0, 0, "Channel 1 Text" },\r
329   { (COLORREF)0, 0, "Channel Text" },\r
330   { (COLORREF)0, 0, "Kibitz Text" },\r
331   { (COLORREF)0, 0, "Tell Text" },\r
332   { (COLORREF)0, 0, "Challenge Text" },\r
333   { (COLORREF)0, 0, "Request Text" },\r
334   { (COLORREF)0, 0, "Seek Text" },\r
335   { (COLORREF)0, 0, "Normal Text" },\r
336   { (COLORREF)0, 0, "None" }\r
337 };\r
338 \r
339 \r
340 \r
341 static char *commentTitle;\r
342 static char *commentText;\r
343 static int commentIndex;\r
344 static Boolean editComment = FALSE;\r
345 HWND commentDialog = NULL;\r
346 BOOLEAN commentDialogUp = FALSE;\r
347 static int commentX, commentY, commentH, commentW;\r
348 \r
349 static char *analysisTitle;\r
350 static char *analysisText;\r
351 HWND analysisDialog = NULL;\r
352 BOOLEAN analysisDialogUp = FALSE;\r
353 static int analysisX, analysisY, analysisH, analysisW;\r
354 \r
355 char errorTitle[MSG_SIZ];\r
356 char errorMessage[2*MSG_SIZ];\r
357 HWND errorDialog = NULL;\r
358 BOOLEAN moveErrorMessageUp = FALSE;\r
359 BOOLEAN consoleEcho = TRUE;\r
360 CHARFORMAT consoleCF;\r
361 COLORREF consoleBackgroundColor;\r
362 \r
363 char *programVersion;\r
364 \r
365 #define CPReal 1\r
366 #define CPComm 2\r
367 #define CPSock 3\r
368 #define CPRcmd 4\r
369 typedef int CPKind;\r
370 \r
371 typedef struct {\r
372   CPKind kind;\r
373   HANDLE hProcess;\r
374   DWORD pid;\r
375   HANDLE hTo;\r
376   HANDLE hFrom;\r
377   SOCKET sock;\r
378   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
379 } ChildProc;\r
380 \r
381 #define INPUT_SOURCE_BUF_SIZE 4096\r
382 \r
383 typedef struct _InputSource {\r
384   CPKind kind;\r
385   HANDLE hFile;\r
386   SOCKET sock;\r
387   int lineByLine;\r
388   HANDLE hThread;\r
389   DWORD id;\r
390   char buf[INPUT_SOURCE_BUF_SIZE];\r
391   char *next;\r
392   DWORD count;\r
393   int error;\r
394   InputCallback func;\r
395   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
396   VOIDSTAR closure;\r
397 } InputSource;\r
398 \r
399 InputSource *consoleInputSource;\r
400 \r
401 DCB dcb;\r
402 \r
403 /* forward */\r
404 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
405 VOID ConsoleCreate();\r
406 LRESULT CALLBACK\r
407   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
408 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
409 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
410 VOID ParseCommSettings(char *arg, DCB *dcb);\r
411 LRESULT CALLBACK\r
412   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
413 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
414 void ParseIcsTextMenu(char *icsTextMenuString);\r
415 VOID PopUpMoveDialog(char firstchar);\r
416 VOID PopUpNameDialog(char firstchar);\r
417 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
418 \r
419 /* [AS] */\r
420 int NewGameFRC();\r
421 int GameListOptions();\r
422 \r
423 HWND moveHistoryDialog = NULL;\r
424 BOOLEAN moveHistoryDialogUp = FALSE;\r
425 \r
426 WindowPlacement wpMoveHistory;\r
427 \r
428 HWND evalGraphDialog = NULL;\r
429 BOOLEAN evalGraphDialogUp = FALSE;\r
430 \r
431 WindowPlacement wpEvalGraph;\r
432 \r
433 HWND engineOutputDialog = NULL;\r
434 BOOLEAN engineOutputDialogUp = FALSE;\r
435 \r
436 WindowPlacement wpEngineOutput;\r
437 WindowPlacement wpGameList;\r
438 WindowPlacement wpConsole;\r
439 \r
440 VOID MoveHistoryPopUp();\r
441 VOID MoveHistoryPopDown();\r
442 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
443 BOOL MoveHistoryIsUp();\r
444 \r
445 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
446 VOID EvalGraphPopUp();\r
447 VOID EvalGraphPopDown();\r
448 BOOL EvalGraphIsUp();\r
449 \r
450 VOID EngineOutputPopUp();\r
451 VOID EngineOutputPopDown();\r
452 BOOL EngineOutputIsUp();\r
453 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
454 \r
455 VOID EngineOptionsPopup(); // [HGM] settings\r
456 \r
457 VOID GothicPopUp(char *title, VariantClass variant);\r
458 /*\r
459  * Setting "frozen" should disable all user input other than deleting\r
460  * the window.  We do this while engines are initializing themselves.\r
461  */\r
462 static int frozen = 0;\r
463 static int oldMenuItemState[MENU_BAR_ITEMS];\r
464 void FreezeUI()\r
465 {\r
466   HMENU hmenu;\r
467   int i;\r
468 \r
469   if (frozen) return;\r
470   frozen = 1;\r
471   hmenu = GetMenu(hwndMain);\r
472   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
473     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
474   }\r
475   DrawMenuBar(hwndMain);\r
476 }\r
477 \r
478 /* Undo a FreezeUI */\r
479 void ThawUI()\r
480 {\r
481   HMENU hmenu;\r
482   int i;\r
483 \r
484   if (!frozen) return;\r
485   frozen = 0;\r
486   hmenu = GetMenu(hwndMain);\r
487   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
488     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
489   }\r
490   DrawMenuBar(hwndMain);\r
491 }\r
492 \r
493 static int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
494 \r
495 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
496 #ifdef JAWS\r
497 #include "jaws.c"\r
498 #else\r
499 #define JAWS_INIT\r
500 #define JAWS_ARGS\r
501 #define JAWS_ALT_INTERCEPT\r
502 #define JAWS_KB_NAVIGATION\r
503 #define JAWS_MENU_ITEMS\r
504 #define JAWS_SILENCE\r
505 #define JAWS_REPLAY\r
506 #define JAWS_ACCEL\r
507 #define JAWS_COPYRIGHT\r
508 #define JAWS_DELETE(X) X\r
509 #define SAYMACHINEMOVE()\r
510 #define SAY(X)\r
511 #endif\r
512 \r
513 /*---------------------------------------------------------------------------*\\r
514  *\r
515  * WinMain\r
516  *\r
517 \*---------------------------------------------------------------------------*/\r
518 \r
519 int APIENTRY\r
520 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
521         LPSTR lpCmdLine, int nCmdShow)\r
522 {\r
523   MSG msg;\r
524   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
525 //  INITCOMMONCONTROLSEX ex;\r
526 \r
527   debugFP = stderr;\r
528 \r
529   LoadLibrary("RICHED32.DLL");\r
530   consoleCF.cbSize = sizeof(CHARFORMAT);\r
531 \r
532   if (!InitApplication(hInstance)) {\r
533     return (FALSE);\r
534   }\r
535   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
536     return (FALSE);\r
537   }\r
538 \r
539   JAWS_INIT\r
540 \r
541 //  InitCommonControlsEx(&ex);\r
542   InitCommonControls();\r
543 \r
544   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
545   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
546   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
547 \r
548   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
549 \r
550   while (GetMessage(&msg, /* message structure */\r
551                     NULL, /* handle of window receiving the message */\r
552                     0,    /* lowest message to examine */\r
553                     0))   /* highest message to examine */\r
554     {\r
555 \r
556       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
557         // [HGM] navigate: switch between all windows with tab\r
558         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
559         int i, currentElement = 0;\r
560 \r
561         // first determine what element of the chain we come from (if any)\r
562         if(appData.icsActive) {\r
563             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
564             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
565         }\r
566         if(engineOutputDialog && EngineOutputIsUp()) {\r
567             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
568             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
569         }\r
570         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
571             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
572         }\r
573         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
574         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
575         if(msg.hwnd == e1)                 currentElement = 2; else\r
576         if(msg.hwnd == e2)                 currentElement = 3; else\r
577         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
578         if(msg.hwnd == mh)                currentElement = 4; else\r
579         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
580         if(msg.hwnd == hText)  currentElement = 5; else\r
581         if(msg.hwnd == hInput) currentElement = 6; else\r
582         for (i = 0; i < N_BUTTONS; i++) {\r
583             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
584         }\r
585 \r
586         // determine where to go to\r
587         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
588           do {\r
589             currentElement = (currentElement + direction) % 7;\r
590             switch(currentElement) {\r
591                 case 0:\r
592                   h = hwndMain; break; // passing this case always makes the loop exit\r
593                 case 1:\r
594                   h = buttonDesc[0].hwnd; break; // could be NULL\r
595                 case 2:\r
596                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
597                   h = e1; break;\r
598                 case 3:\r
599                   if(!EngineOutputIsUp()) continue;\r
600                   h = e2; break;\r
601                 case 4:\r
602                   if(!MoveHistoryIsUp()) continue;\r
603                   h = mh; break;\r
604 //              case 6: // input to eval graph does not seem to get here!\r
605 //                if(!EvalGraphIsUp()) continue;\r
606 //                h = evalGraphDialog; break;\r
607                 case 5:\r
608                   if(!appData.icsActive) continue;\r
609                   SAY("display");\r
610                   h = hText; break;\r
611                 case 6:\r
612                   if(!appData.icsActive) continue;\r
613                   SAY("input");\r
614                   h = hInput; break;\r
615             }\r
616           } while(h == 0);\r
617 \r
618           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
619           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
620           SetFocus(h);\r
621 \r
622           continue; // this message now has been processed\r
623         }\r
624       }\r
625 \r
626       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
627           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
628           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
629           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
630           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
631           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
632           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
633           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
634           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
635           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
636         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
637         for(i=0; i<MAX_CHAT; i++) \r
638             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
639                 done = 1; break;\r
640         }\r
641         if(done) continue; // [HGM] chat: end patch\r
642         TranslateMessage(&msg); /* Translates virtual key codes */\r
643         DispatchMessage(&msg);  /* Dispatches message to window */\r
644       }\r
645     }\r
646 \r
647 \r
648   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
649 }\r
650 \r
651 /*---------------------------------------------------------------------------*\\r
652  *\r
653  * Initialization functions\r
654  *\r
655 \*---------------------------------------------------------------------------*/\r
656 \r
657 void\r
658 SetUserLogo()\r
659 {   // update user logo if necessary\r
660     static char oldUserName[MSG_SIZ], *curName;\r
661 \r
662     if(appData.autoLogo) {\r
663           curName = UserName();\r
664           if(strcmp(curName, oldUserName)) {\r
665                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
666                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
667                 strcpy(oldUserName, curName);\r
668           }\r
669     }\r
670 }\r
671 \r
672 BOOL\r
673 InitApplication(HINSTANCE hInstance)\r
674 {\r
675   WNDCLASS wc;\r
676 \r
677   /* Fill in window class structure with parameters that describe the */\r
678   /* main window. */\r
679 \r
680   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
681   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
682   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
683   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
684   wc.hInstance     = hInstance;         /* Owner of this class */\r
685   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
686   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
687   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
688   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
689   wc.lpszClassName = szAppName;                 /* Name to register as */\r
690 \r
691   /* Register the window class and return success/failure code. */\r
692   if (!RegisterClass(&wc)) return FALSE;\r
693 \r
694   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
695   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
696   wc.cbClsExtra    = 0;\r
697   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
698   wc.hInstance     = hInstance;\r
699   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
700   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
701   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
702   wc.lpszMenuName  = NULL;\r
703   wc.lpszClassName = szConsoleName;\r
704 \r
705   if (!RegisterClass(&wc)) return FALSE;\r
706   return TRUE;\r
707 }\r
708 \r
709 \r
710 /* Set by InitInstance, used by EnsureOnScreen */\r
711 int screenHeight, screenWidth;\r
712 \r
713 void\r
714 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
715 {\r
716 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
717   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
718   if (*x > screenWidth - 32) *x = 0;\r
719   if (*y > screenHeight - 32) *y = 0;\r
720   if (*x < minX) *x = minX;\r
721   if (*y < minY) *y = minY;\r
722 }\r
723 \r
724 BOOL\r
725 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
726 {\r
727   HWND hwnd; /* Main window handle. */\r
728   int ibs;\r
729   WINDOWPLACEMENT wp;\r
730   char *filepart;\r
731 \r
732   hInst = hInstance;    /* Store instance handle in our global variable */\r
733 \r
734   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
735     *filepart = NULLCHAR;\r
736   } else {\r
737     GetCurrentDirectory(MSG_SIZ, installDir);\r
738   }\r
739   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
740   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
741   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
742   if (appData.debugMode) {\r
743     debugFP = fopen(appData.nameOfDebugFile, "w");\r
744     setbuf(debugFP, NULL);\r
745   }\r
746 \r
747   InitBackEnd1();\r
748 \r
749 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
750 //  InitEngineUCI( installDir, &second );\r
751 \r
752   /* Create a main window for this application instance. */\r
753   hwnd = CreateWindow(szAppName, szTitle,\r
754                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
755                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
756                       NULL, NULL, hInstance, NULL);\r
757   hwndMain = hwnd;\r
758 \r
759   /* If window could not be created, return "failure" */\r
760   if (!hwnd) {\r
761     return (FALSE);\r
762   }\r
763 \r
764   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
765   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
766       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
767 \r
768       if (first.programLogo == NULL && appData.debugMode) {\r
769           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
770       }\r
771   } else if(appData.autoLogo) {\r
772       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
773         char buf[MSG_SIZ];\r
774         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
775         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
776       }\r
777   }\r
778 \r
779   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
780       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
781 \r
782       if (second.programLogo == NULL && appData.debugMode) {\r
783           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
784       }\r
785   } else if(appData.autoLogo) {\r
786       char buf[MSG_SIZ];\r
787       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
788         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
789         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
790       } else\r
791       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
792         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
793         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
794       }\r
795   }\r
796 \r
797   SetUserLogo();\r
798 \r
799   iconWhite = LoadIcon(hInstance, "icon_white");\r
800   iconBlack = LoadIcon(hInstance, "icon_black");\r
801   iconCurrent = iconWhite;\r
802   InitDrawingColors();\r
803   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
804   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
805   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
806     /* Compute window size for each board size, and use the largest\r
807        size that fits on this screen as the default. */\r
808     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
809     if (boardSize == (BoardSize)-1 &&\r
810         winH <= screenHeight\r
811            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
812         && winW <= screenWidth) {\r
813       boardSize = (BoardSize)ibs;\r
814     }\r
815   }\r
816 \r
817   InitDrawingSizes(boardSize, 0);\r
818   InitMenuChecks();\r
819   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
820 \r
821   /* [AS] Load textures if specified */\r
822   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
823   \r
824   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
825       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
826       liteBackTextureMode = appData.liteBackTextureMode;\r
827 \r
828       if (liteBackTexture == NULL && appData.debugMode) {\r
829           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
830       }\r
831   }\r
832   \r
833   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
834       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
835       darkBackTextureMode = appData.darkBackTextureMode;\r
836 \r
837       if (darkBackTexture == NULL && appData.debugMode) {\r
838           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
839       }\r
840   }\r
841 \r
842   mysrandom( (unsigned) time(NULL) );\r
843 \r
844   /* [AS] Restore layout */\r
845   if( wpMoveHistory.visible ) {\r
846       MoveHistoryPopUp();\r
847   }\r
848 \r
849   if( wpEvalGraph.visible ) {\r
850       EvalGraphPopUp();\r
851   }\r
852 \r
853   if( wpEngineOutput.visible ) {\r
854       EngineOutputPopUp();\r
855   }\r
856 \r
857   InitBackEnd2();\r
858 \r
859   /* Make the window visible; update its client area; and return "success" */\r
860   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
861   wp.length = sizeof(WINDOWPLACEMENT);\r
862   wp.flags = 0;\r
863   wp.showCmd = nCmdShow;\r
864   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
865   wp.rcNormalPosition.left = boardX;\r
866   wp.rcNormalPosition.right = boardX + winWidth;\r
867   wp.rcNormalPosition.top = boardY;\r
868   wp.rcNormalPosition.bottom = boardY + winHeight;\r
869   SetWindowPlacement(hwndMain, &wp);\r
870 \r
871   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
872                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
873 \r
874   if (hwndConsole) {\r
875 #if AOT_CONSOLE\r
876     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
877                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
878 #endif\r
879     ShowWindow(hwndConsole, nCmdShow);\r
880   }\r
881   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
882 \r
883   return TRUE;\r
884 \r
885 }\r
886 \r
887 \r
888 typedef enum {\r
889   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
890   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
891   ArgSettingsFilename,\r
892   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
893 } ArgType;\r
894 \r
895 typedef struct {\r
896   char *argName;\r
897   ArgType argType;\r
898   /***\r
899   union {\r
900     String *pString;       // ArgString\r
901     int *pInt;             // ArgInt\r
902     float *pFloat;         // ArgFloat\r
903     Boolean *pBoolean;     // ArgBoolean\r
904     COLORREF *pColor;      // ArgColor\r
905     ColorClass cc;         // ArgAttribs\r
906     String *pFilename;     // ArgFilename\r
907     BoardSize *pBoardSize; // ArgBoardSize\r
908     int whichFont;         // ArgFont\r
909     DCB *pDCB;             // ArgCommSettings\r
910     String *pFilename;     // ArgSettingsFilename\r
911   } argLoc;\r
912   ***/\r
913   LPVOID argLoc;\r
914   BOOL save;\r
915 } ArgDescriptor;\r
916 \r
917 int junk;\r
918 ArgDescriptor argDescriptors[] = {\r
919   /* positional arguments */\r
920   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
921   { "", ArgNone, NULL },\r
922   /* keyword arguments */\r
923   JAWS_ARGS\r
924   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
925   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
926   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
927   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
928   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
929   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
930   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
931   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
932   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
933   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
934   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
935   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
936   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
937   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
938   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
939   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
940   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
941   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
942     FALSE },\r
943   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
944     FALSE },\r
945   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
946     FALSE },\r
947   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
948   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
949     FALSE },\r
950   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
951   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
952   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
953   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
954   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
955   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
956   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
957   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
958   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
959   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
960   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
961   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
962   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
963   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
964   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
965   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
966   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
967   /*!!bitmapDirectory?*/\r
968   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
969   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
970   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
971   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
972   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
973   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
974   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
975   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
976   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
977   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
978   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
979   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
980   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
981   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
982   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
983   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
984   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
985   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
986   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
987   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
988   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
989   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
990   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
991   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
992   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
993   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
994   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
995   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
996   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
997   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
998   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
999   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
1000   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
1001   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1002   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1003   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
1004   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
1005   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
1006   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
1007   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1008   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1009   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1010   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1011   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1012   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1013   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1014   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1015   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
1016   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1017   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1018   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1019   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1020   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1021   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1022   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1023   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1024   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1025   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1026   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1027   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1028   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1029   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1030   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1031   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1032   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1033   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1034   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1035   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1036   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1037   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1038   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1039   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1040   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1041   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1042   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1043   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1044   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1045   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1046   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1047   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1048   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1049   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1050   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1051   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1052   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1053   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1054   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1055   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1056   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1057   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1058   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1059   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1060   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1061   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1062     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1063   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1064   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1065   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1066   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1067   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1068   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1069   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1070   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1071     TRUE }, /* must come after all fonts */\r
1072   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1073   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1074     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1075   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1076   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1077   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1078   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1079   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1080   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1081   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1082   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1083   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1084   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1085   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1086   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1087   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1088   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1089   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1090   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1091   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1092   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1093   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1094   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1095   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1096   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1097   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1098   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1099   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1100   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1101   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1102   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1103 #if 0\r
1104   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1105   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1106 #endif\r
1107   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1108   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1109   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1110   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1111   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1112   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1113   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1114   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1115   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1116   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1117   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1118   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1119   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1120   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1121   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1122   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1123   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1124   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1125   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1126   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1127   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1128   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1129   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1130   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1131   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1132   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1133   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1134   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1135   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1136   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1137   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1138   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1139   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1140   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1141   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1142   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1143   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1144   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1145   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1146   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1147   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1148   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1149   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1150   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1151   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1152   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1153   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1154   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1155   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1156   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1157   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1158   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1159   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1160   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1161   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1162   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1163   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1164   { "highlightLastMove", ArgBoolean,\r
1165     (LPVOID) &appData.highlightLastMove, TRUE },\r
1166   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1167   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1168   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1169   { "highlightDragging", ArgBoolean,\r
1170     (LPVOID) &appData.highlightDragging, TRUE },\r
1171   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1172   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1173   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1174   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1175   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1176   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1177   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1178   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1179   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1180   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1181   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1182   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1183   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1184   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1185   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1186   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1187   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1188   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1189   { "soundShout", ArgFilename,\r
1190     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1191   { "soundSShout", ArgFilename,\r
1192     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1193   { "soundChannel1", ArgFilename,\r
1194     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1195   { "soundChannel", ArgFilename,\r
1196     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1197   { "soundKibitz", ArgFilename,\r
1198     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1199   { "soundTell", ArgFilename,\r
1200     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1201   { "soundChallenge", ArgFilename,\r
1202     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1203   { "soundRequest", ArgFilename,\r
1204     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1205   { "soundSeek", ArgFilename,\r
1206     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1207   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1208   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1209   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1210   { "soundIcsLoss", ArgFilename, \r
1211     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1212   { "soundIcsDraw", ArgFilename, \r
1213     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1214   { "soundIcsUnfinished", ArgFilename, \r
1215     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1216   { "soundIcsAlarm", ArgFilename, \r
1217     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1218   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1219   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1220   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1221   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1222   { "reuseChessPrograms", ArgBoolean,\r
1223     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1224   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1225   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1226   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1227   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1228   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1229   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1230   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1231   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1232   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1233   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1234   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1235   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1236   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1237   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1238   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1239     TRUE },\r
1240   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1241     TRUE },\r
1242   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1243   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1244   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1245   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1246   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1247   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1248   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1249   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1250   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1251   /* [AS] New features */\r
1252   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1253   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1254   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1255   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1256   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1257   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1258   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1259   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1260   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1261   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1262   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1263   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1264   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1265   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1266   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1267   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1268   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1269   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1270   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1271   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1272   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1273   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1274   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1275   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1276   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1277   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1278   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1279   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1280   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1281   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1282   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1283   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1284   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1285   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1286   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1287   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1288   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1289   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1290   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1291   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1292   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1293   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1294   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1295   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1296   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1297   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1298   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1299   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1300   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1301   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1302 \r
1303   /* [HGM] board-size, adjudication and misc. options */\r
1304   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1305   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1306   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1307   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1308   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1309   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1310   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1311   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1312   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1313   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1314   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1315   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1316   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1317   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1318   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1319   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1320   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1321   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1322   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1323   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1324   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1325   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1326   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1327   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1328   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1329   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1330   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1331   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1332   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1333   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1334   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1335   { "keepAlive", ArgInt, (LPVOID) &appData.keepAlive, FALSE },\r
1336   { "icstype", ArgInt, (LPVOID) &ics_type, FALSE },\r
1337 \r
1338 #ifdef ZIPPY\r
1339   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1340   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1341   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1342   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1343   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1344   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1345   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1346   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1347   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1348   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1349   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1350   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1351   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1352     FALSE },\r
1353   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1354   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1355   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1356   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1357   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1358   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1359   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1360     FALSE },\r
1361   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1362   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1363   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1364   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1365   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1366   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1367   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1368   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1369   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1370   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1371   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1372   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1373   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1374   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1375   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1376   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1377   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1378   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1379   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1380 #endif\r
1381   /* [HGM] options for broadcasting and time odds */\r
1382   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1383   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1384   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1385   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1386   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1387   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1388   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1389   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1390   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1391   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1392   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1393 \r
1394   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1395   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1396   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1397   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1398   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1399   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1400   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1401   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1402   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1403   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1404   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1405   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1406   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1407   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1408   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1409   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1410   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1411   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1412   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1413   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1414   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1415   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1416   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1417   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1418   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1419   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1420   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1421   /* [AS] Layout stuff */\r
1422   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1423   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1424   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1425   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1426   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1427 \r
1428   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1429   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1430   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1431   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1432   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1433 \r
1434   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1435   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1436   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1437   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1438   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1439 \r
1440   { NULL, ArgNone, NULL, FALSE }\r
1441 };\r
1442 \r
1443 \r
1444 /* Kludge for indirection files on command line */\r
1445 char* lastIndirectionFilename;\r
1446 ArgDescriptor argDescriptorIndirection =\r
1447 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1448 \r
1449 \r
1450 VOID\r
1451 ExitArgError(char *msg, char *badArg)\r
1452 {\r
1453   char buf[MSG_SIZ];\r
1454 \r
1455   sprintf(buf, "%s %s", msg, badArg);\r
1456   DisplayFatalError(buf, 0, 2);\r
1457   exit(2);\r
1458 }\r
1459 \r
1460 /* Command line font name parser.  NULL name means do nothing.\r
1461    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1462    For backward compatibility, syntax without the colon is also\r
1463    accepted, but font names with digits in them won't work in that case.\r
1464 */\r
1465 VOID\r
1466 ParseFontName(char *name, MyFontParams *mfp)\r
1467 {\r
1468   char *p, *q;\r
1469   if (name == NULL) return;\r
1470   p = name;\r
1471   q = strchr(p, ':');\r
1472   if (q) {\r
1473     if (q - p >= sizeof(mfp->faceName))\r
1474       ExitArgError("Font name too long:", name);\r
1475     memcpy(mfp->faceName, p, q - p);\r
1476     mfp->faceName[q - p] = NULLCHAR;\r
1477     p = q + 1;\r
1478   } else {\r
1479     q = mfp->faceName;\r
1480     while (*p && !isdigit(*p)) {\r
1481       *q++ = *p++;\r
1482       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1483         ExitArgError("Font name too long:", name);\r
1484     }\r
1485     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1486     *q = NULLCHAR;\r
1487   }\r
1488   if (!*p) ExitArgError("Font point size missing:", name);\r
1489   mfp->pointSize = (float) atof(p);\r
1490   mfp->bold = (strchr(p, 'b') != NULL);\r
1491   mfp->italic = (strchr(p, 'i') != NULL);\r
1492   mfp->underline = (strchr(p, 'u') != NULL);\r
1493   mfp->strikeout = (strchr(p, 's') != NULL);\r
1494 }\r
1495 \r
1496 /* Color name parser.\r
1497    X version accepts X color names, but this one\r
1498    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1499 COLORREF\r
1500 ParseColorName(char *name)\r
1501 {\r
1502   int red, green, blue, count;\r
1503   char buf[MSG_SIZ];\r
1504 \r
1505   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1506   if (count != 3) {\r
1507     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1508       &red, &green, &blue);\r
1509   }\r
1510   if (count != 3) {\r
1511     sprintf(buf, "Can't parse color name %s", name);\r
1512     DisplayError(buf, 0);\r
1513     return RGB(0, 0, 0);\r
1514   }\r
1515   return PALETTERGB(red, green, blue);\r
1516 }\r
1517 \r
1518 \r
1519 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1520 {\r
1521   char *e = argValue;\r
1522   int eff = 0;\r
1523 \r
1524   while (*e) {\r
1525     if (*e == 'b')      eff |= CFE_BOLD;\r
1526     else if (*e == 'i') eff |= CFE_ITALIC;\r
1527     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1528     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1529     else if (*e == '#' || isdigit(*e)) break;\r
1530     e++;\r
1531   }\r
1532   *effects = eff;\r
1533   *color   = ParseColorName(e);\r
1534 }\r
1535 \r
1536 \r
1537 BoardSize\r
1538 ParseBoardSize(char *name)\r
1539 {\r
1540   BoardSize bs = SizeTiny;\r
1541   while (sizeInfo[bs].name != NULL) {\r
1542     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1543     bs++;\r
1544   }\r
1545   ExitArgError("Unrecognized board size value", name);\r
1546   return bs; /* not reached */\r
1547 }\r
1548 \r
1549 \r
1550 char\r
1551 StringGet(void *getClosure)\r
1552 {\r
1553   char **p = (char **) getClosure;\r
1554   return *((*p)++);\r
1555 }\r
1556 \r
1557 char\r
1558 FileGet(void *getClosure)\r
1559 {\r
1560   int c;\r
1561   FILE* f = (FILE*) getClosure;\r
1562 \r
1563   c = getc(f);\r
1564   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1565   if (c == EOF)\r
1566     return NULLCHAR;\r
1567   else\r
1568     return (char) c;\r
1569 }\r
1570 \r
1571 /* Parse settings file named "name". If file found, return the\r
1572    full name in fullname and return TRUE; else return FALSE */\r
1573 BOOLEAN\r
1574 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1575 {\r
1576   char *dummy;\r
1577   FILE *f;\r
1578   int ok; char buf[MSG_SIZ];\r
1579 \r
1580   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1581   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1582     sprintf(buf, "%s.ini", name);\r
1583     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1584   }\r
1585   if (ok) {\r
1586     f = fopen(fullname, "r");\r
1587     if (f != NULL) {\r
1588       ParseArgs(FileGet, f);\r
1589       fclose(f);\r
1590       return TRUE;\r
1591     }\r
1592   }\r
1593   return FALSE;\r
1594 }\r
1595 \r
1596 VOID\r
1597 ParseArgs(GetFunc get, void *cl)\r
1598 {\r
1599   char argName[ARG_MAX];\r
1600   char argValue[ARG_MAX];\r
1601   ArgDescriptor *ad;\r
1602   char start;\r
1603   char *q;\r
1604   int i, octval;\r
1605   char ch;\r
1606   int posarg = 0;\r
1607 \r
1608   ch = get(cl);\r
1609   for (;;) {\r
1610     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1611     if (ch == NULLCHAR) break;\r
1612     if (ch == ';') {\r
1613       /* Comment to end of line */\r
1614       ch = get(cl);\r
1615       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1616       continue;\r
1617     } else if (ch == '/' || ch == '-') {\r
1618       /* Switch */\r
1619       q = argName;\r
1620       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1621              ch != '\n' && ch != '\t') {\r
1622         *q++ = ch;\r
1623         ch = get(cl);\r
1624       }\r
1625       *q = NULLCHAR;\r
1626 \r
1627       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1628         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1629 \r
1630       if (ad->argName == NULL)\r
1631         ExitArgError("Unrecognized argument", argName);\r
1632 \r
1633     } else if (ch == '@') {\r
1634       /* Indirection file */\r
1635       ad = &argDescriptorIndirection;\r
1636       ch = get(cl);\r
1637     } else {\r
1638       /* Positional argument */\r
1639       ad = &argDescriptors[posarg++];\r
1640       strcpy(argName, ad->argName);\r
1641     }\r
1642 \r
1643     if (ad->argType == ArgTrue) {\r
1644       *(Boolean *) ad->argLoc = TRUE;\r
1645       continue;\r
1646     }\r
1647     if (ad->argType == ArgFalse) {\r
1648       *(Boolean *) ad->argLoc = FALSE;\r
1649       continue;\r
1650     }\r
1651 \r
1652     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1653     if (ch == NULLCHAR || ch == '\n') {\r
1654       ExitArgError("No value provided for argument", argName);\r
1655     }\r
1656     q = argValue;\r
1657     if (ch == '{') {\r
1658       // Quoting with { }.  No characters have to (or can) be escaped.\r
1659       // Thus the string cannot contain a '}' character.\r
1660       start = ch;\r
1661       ch = get(cl);\r
1662       while (start) {\r
1663         switch (ch) {\r
1664         case NULLCHAR:\r
1665           start = NULLCHAR;\r
1666           break;\r
1667           \r
1668         case '}':\r
1669           ch = get(cl);\r
1670           start = NULLCHAR;\r
1671           break;\r
1672 \r
1673         default:\r
1674           *q++ = ch;\r
1675           ch = get(cl);\r
1676           break;\r
1677         }\r
1678       }   \r
1679     } else if (ch == '\'' || ch == '"') {\r
1680       // Quoting with ' ' or " ", with \ as escape character.\r
1681       // Inconvenient for long strings that may contain Windows filenames.\r
1682       start = ch;\r
1683       ch = get(cl);\r
1684       while (start) {\r
1685         switch (ch) {\r
1686         case NULLCHAR:\r
1687           start = NULLCHAR;\r
1688           break;\r
1689 \r
1690         default:\r
1691         not_special:\r
1692           *q++ = ch;\r
1693           ch = get(cl);\r
1694           break;\r
1695 \r
1696         case '\'':\r
1697         case '\"':\r
1698           if (ch == start) {\r
1699             ch = get(cl);\r
1700             start = NULLCHAR;\r
1701             break;\r
1702           } else {\r
1703             goto not_special;\r
1704           }\r
1705 \r
1706         case '\\':\r
1707           if (ad->argType == ArgFilename\r
1708               || ad->argType == ArgSettingsFilename) {\r
1709               goto not_special;\r
1710           }\r
1711           ch = get(cl);\r
1712           switch (ch) {\r
1713           case NULLCHAR:\r
1714             ExitArgError("Incomplete \\ escape in value for", argName);\r
1715             break;\r
1716           case 'n':\r
1717             *q++ = '\n';\r
1718             ch = get(cl);\r
1719             break;\r
1720           case 'r':\r
1721             *q++ = '\r';\r
1722             ch = get(cl);\r
1723             break;\r
1724           case 't':\r
1725             *q++ = '\t';\r
1726             ch = get(cl);\r
1727             break;\r
1728           case 'b':\r
1729             *q++ = '\b';\r
1730             ch = get(cl);\r
1731             break;\r
1732           case 'f':\r
1733             *q++ = '\f';\r
1734             ch = get(cl);\r
1735             break;\r
1736           default:\r
1737             octval = 0;\r
1738             for (i = 0; i < 3; i++) {\r
1739               if (ch >= '0' && ch <= '7') {\r
1740                 octval = octval*8 + (ch - '0');\r
1741                 ch = get(cl);\r
1742               } else {\r
1743                 break;\r
1744               }\r
1745             }\r
1746             if (i > 0) {\r
1747               *q++ = (char) octval;\r
1748             } else {\r
1749               *q++ = ch;\r
1750               ch = get(cl);\r
1751             }\r
1752             break;\r
1753           }\r
1754           break;\r
1755         }\r
1756       }\r
1757     } else {\r
1758       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1759         *q++ = ch;\r
1760         ch = get(cl);\r
1761       }\r
1762     }\r
1763     *q = NULLCHAR;\r
1764 \r
1765     switch (ad->argType) {\r
1766     case ArgInt:\r
1767       *(int *) ad->argLoc = atoi(argValue);\r
1768       break;\r
1769 \r
1770     case ArgX:\r
1771       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1772       break;\r
1773 \r
1774     case ArgY:\r
1775       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1776       break;\r
1777 \r
1778     case ArgZ:\r
1779       *(int *) ad->argLoc = atoi(argValue);\r
1780       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1781       break;\r
1782 \r
1783     case ArgFloat:\r
1784       *(float *) ad->argLoc = (float) atof(argValue);\r
1785       break;\r
1786 \r
1787     case ArgString:\r
1788     case ArgFilename:\r
1789       *(char **) ad->argLoc = strdup(argValue);\r
1790       break;\r
1791 \r
1792     case ArgSettingsFilename:\r
1793       {\r
1794         char fullname[MSG_SIZ];\r
1795         if (ParseSettingsFile(argValue, fullname)) {\r
1796           if (ad->argLoc != NULL) {\r
1797             *(char **) ad->argLoc = strdup(fullname);\r
1798           }\r
1799         } else {\r
1800           if (ad->argLoc != NULL) {\r
1801           } else {\r
1802             ExitArgError("Failed to open indirection file", argValue);\r
1803           }\r
1804         }\r
1805       }\r
1806       break;\r
1807 \r
1808     case ArgBoolean:\r
1809       switch (argValue[0]) {\r
1810       case 't':\r
1811       case 'T':\r
1812         *(Boolean *) ad->argLoc = TRUE;\r
1813         break;\r
1814       case 'f':\r
1815       case 'F':\r
1816         *(Boolean *) ad->argLoc = FALSE;\r
1817         break;\r
1818       default:\r
1819         ExitArgError("Unrecognized boolean argument value", argValue);\r
1820         break;\r
1821       }\r
1822       break;\r
1823 \r
1824     case ArgColor:\r
1825       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1826       break;\r
1827 \r
1828     case ArgAttribs: {\r
1829       ColorClass cc = (ColorClass)ad->argLoc;\r
1830       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1831       }\r
1832       break;\r
1833       \r
1834     case ArgBoardSize:\r
1835       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1836       break;\r
1837 \r
1838     case ArgFont:\r
1839       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1840       break;\r
1841 \r
1842     case ArgCommSettings:\r
1843       ParseCommSettings(argValue, &dcb);\r
1844       break;\r
1845 \r
1846     case ArgNone:\r
1847       ExitArgError("Unrecognized argument", argValue);\r
1848       break;\r
1849     case ArgTrue:\r
1850     case ArgFalse: ;\r
1851     }\r
1852   }\r
1853 }\r
1854 \r
1855 VOID\r
1856 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1857 {\r
1858   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1859   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1860   DeleteDC(hdc);\r
1861   lf->lfWidth = 0;\r
1862   lf->lfEscapement = 0;\r
1863   lf->lfOrientation = 0;\r
1864   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1865   lf->lfItalic = mfp->italic;\r
1866   lf->lfUnderline = mfp->underline;\r
1867   lf->lfStrikeOut = mfp->strikeout;\r
1868   lf->lfCharSet = DEFAULT_CHARSET;\r
1869   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1870   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1871   lf->lfQuality = DEFAULT_QUALITY;\r
1872   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1873   strcpy(lf->lfFaceName, mfp->faceName);\r
1874 }\r
1875 \r
1876 VOID\r
1877 CreateFontInMF(MyFont *mf)\r
1878 {\r
1879   LFfromMFP(&mf->lf, &mf->mfp);\r
1880   if (mf->hf) DeleteObject(mf->hf);\r
1881   mf->hf = CreateFontIndirect(&mf->lf);\r
1882 }\r
1883 \r
1884 VOID\r
1885 SetDefaultTextAttribs()\r
1886 {\r
1887   ColorClass cc;\r
1888   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1889     ParseAttribs(&textAttribs[cc].color, \r
1890                  &textAttribs[cc].effects, \r
1891                  defaultTextAttribs[cc]);\r
1892   }\r
1893 }\r
1894 \r
1895 VOID\r
1896 SetDefaultSounds()\r
1897 {\r
1898   ColorClass cc;\r
1899   SoundClass sc;\r
1900   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1901     textAttribs[cc].sound.name = strdup("");\r
1902     textAttribs[cc].sound.data = NULL;\r
1903   }\r
1904   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1905     sounds[sc].name = strdup("");\r
1906     sounds[sc].data = NULL;\r
1907   }\r
1908   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1909 }\r
1910 \r
1911 VOID\r
1912 LoadAllSounds()\r
1913 {\r
1914   ColorClass cc;\r
1915   SoundClass sc;\r
1916   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1917     MyLoadSound(&textAttribs[cc].sound);\r
1918   }\r
1919   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1920     MyLoadSound(&sounds[sc]);\r
1921   }\r
1922 }\r
1923 \r
1924 VOID\r
1925 InitAppData(LPSTR lpCmdLine)\r
1926 {\r
1927   int i, j;\r
1928   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1929   char *dummy, *p;\r
1930 \r
1931   programName = szAppName;\r
1932 \r
1933   /* Initialize to defaults */\r
1934   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1935   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1936   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1937   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1938   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1939   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1940   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1941   SetDefaultTextAttribs();\r
1942   SetDefaultSounds();\r
1943   appData.movesPerSession = MOVES_PER_SESSION;\r
1944   appData.initString = INIT_STRING;\r
1945   appData.secondInitString = INIT_STRING;\r
1946   appData.firstComputerString = COMPUTER_STRING;\r
1947   appData.secondComputerString = COMPUTER_STRING;\r
1948   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1949   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1950   appData.firstPlaysBlack = FALSE;\r
1951   appData.noChessProgram = FALSE;\r
1952   chessProgram = FALSE;\r
1953   appData.firstHost = FIRST_HOST;\r
1954   appData.secondHost = SECOND_HOST;\r
1955   appData.firstDirectory = FIRST_DIRECTORY;\r
1956   appData.secondDirectory = SECOND_DIRECTORY;\r
1957   appData.bitmapDirectory = "";\r
1958   appData.remoteShell = REMOTE_SHELL;\r
1959   appData.remoteUser = "";\r
1960   appData.timeDelay = TIME_DELAY;\r
1961   appData.timeControl = TIME_CONTROL;\r
1962   appData.timeIncrement = TIME_INCREMENT;\r
1963   appData.icsActive = FALSE;\r
1964   appData.icsHost = "";\r
1965   appData.icsPort = ICS_PORT;\r
1966   appData.icsCommPort = ICS_COMM_PORT;\r
1967   appData.icsLogon = ICS_LOGON;\r
1968   appData.icsHelper = "";\r
1969   appData.useTelnet = FALSE;\r
1970   appData.telnetProgram = TELNET_PROGRAM;\r
1971   appData.gateway = "";\r
1972   appData.loadGameFile = "";\r
1973   appData.loadGameIndex = 0;\r
1974   appData.saveGameFile = "";\r
1975   appData.autoSaveGames = FALSE;\r
1976   appData.loadPositionFile = "";\r
1977   appData.loadPositionIndex = 1;\r
1978   appData.savePositionFile = "";\r
1979   appData.matchMode = FALSE;\r
1980   appData.matchGames = 0;\r
1981   appData.monoMode = FALSE;\r
1982   appData.debugMode = FALSE;\r
1983   appData.clockMode = TRUE;\r
1984   boardSize = (BoardSize) -1; /* determine by screen size */\r
1985   appData.Iconic = FALSE; /*unused*/\r
1986   appData.searchTime = "";\r
1987   appData.searchDepth = 0;\r
1988   appData.showCoords = FALSE;\r
1989   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1990   appData.autoCallFlag = FALSE;\r
1991   appData.flipView = FALSE;\r
1992   appData.autoFlipView = TRUE;\r
1993   appData.cmailGameName = "";\r
1994   appData.alwaysPromoteToQueen = FALSE;\r
1995   appData.oldSaveStyle = FALSE;\r
1996   appData.quietPlay = FALSE;\r
1997   appData.showThinking = FALSE;\r
1998   appData.ponderNextMove = TRUE;\r
1999   appData.periodicUpdates = TRUE;\r
2000   appData.popupExitMessage = TRUE;\r
2001   appData.popupMoveErrors = FALSE;\r
2002   appData.autoObserve = FALSE;\r
2003   appData.autoComment = FALSE;\r
2004   appData.animate = TRUE;\r
2005   appData.animSpeed = 10;\r
2006   appData.animateDragging = TRUE;\r
2007   appData.highlightLastMove = TRUE;\r
2008   appData.getMoveList = TRUE;\r
2009   appData.testLegality = TRUE;\r
2010   appData.premove = TRUE;\r
2011   appData.premoveWhite = FALSE;\r
2012   appData.premoveWhiteText = "";\r
2013   appData.premoveBlack = FALSE;\r
2014   appData.premoveBlackText = "";\r
2015   appData.icsAlarm = TRUE;\r
2016   appData.icsAlarmTime = 5000;\r
2017   appData.autoRaiseBoard = TRUE;\r
2018   appData.localLineEditing = TRUE;\r
2019   appData.colorize = TRUE;\r
2020   appData.reuseFirst = TRUE;\r
2021   appData.reuseSecond = TRUE;\r
2022   appData.blindfold = FALSE;\r
2023   appData.icsEngineAnalyze = FALSE;\r
2024   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2025   dcb.DCBlength = sizeof(DCB);\r
2026   dcb.BaudRate = 9600;\r
2027   dcb.fBinary = TRUE;\r
2028   dcb.fParity = FALSE;\r
2029   dcb.fOutxCtsFlow = FALSE;\r
2030   dcb.fOutxDsrFlow = FALSE;\r
2031   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2032   dcb.fDsrSensitivity = FALSE;\r
2033   dcb.fTXContinueOnXoff = TRUE;\r
2034   dcb.fOutX = FALSE;\r
2035   dcb.fInX = FALSE;\r
2036   dcb.fNull = FALSE;\r
2037   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2038   dcb.fAbortOnError = FALSE;\r
2039   dcb.ByteSize = 7;\r
2040   dcb.Parity = SPACEPARITY;\r
2041   dcb.StopBits = ONESTOPBIT;\r
2042   settingsFileName = SETTINGS_FILE;\r
2043   saveSettingsOnExit = TRUE;\r
2044   boardX = CW_USEDEFAULT;\r
2045   boardY = CW_USEDEFAULT;\r
2046   analysisX = CW_USEDEFAULT; \r
2047   analysisY = CW_USEDEFAULT; \r
2048   analysisW = CW_USEDEFAULT;\r
2049   analysisH = CW_USEDEFAULT;\r
2050   commentX = CW_USEDEFAULT; \r
2051   commentY = CW_USEDEFAULT; \r
2052   commentW = CW_USEDEFAULT;\r
2053   commentH = CW_USEDEFAULT;\r
2054   editTagsX = CW_USEDEFAULT; \r
2055   editTagsY = CW_USEDEFAULT; \r
2056   editTagsW = CW_USEDEFAULT;\r
2057   editTagsH = CW_USEDEFAULT;\r
2058   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2059   icsNames = ICS_NAMES;\r
2060   firstChessProgramNames = FCP_NAMES;\r
2061   secondChessProgramNames = SCP_NAMES;\r
2062   appData.initialMode = "";\r
2063   appData.variant = "normal";\r
2064   appData.firstProtocolVersion = PROTOVER;\r
2065   appData.secondProtocolVersion = PROTOVER;\r
2066   appData.showButtonBar = TRUE;\r
2067 \r
2068    /* [AS] New properties (see comments in header file) */\r
2069   appData.firstScoreIsAbsolute = FALSE;\r
2070   appData.secondScoreIsAbsolute = FALSE;\r
2071   appData.saveExtendedInfoInPGN = FALSE;\r
2072   appData.hideThinkingFromHuman = FALSE;\r
2073   appData.liteBackTextureFile = "";\r
2074   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2075   appData.darkBackTextureFile = "";\r
2076   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2077   appData.renderPiecesWithFont = "";\r
2078   appData.fontToPieceTable = "";\r
2079   appData.fontBackColorWhite = 0;\r
2080   appData.fontForeColorWhite = 0;\r
2081   appData.fontBackColorBlack = 0;\r
2082   appData.fontForeColorBlack = 0;\r
2083   appData.fontPieceSize = 80;\r
2084   appData.overrideLineGap = 1;\r
2085   appData.adjudicateLossThreshold = 0;\r
2086   appData.delayBeforeQuit = 0;\r
2087   appData.delayAfterQuit = 0;\r
2088   appData.nameOfDebugFile = "winboard.debug";\r
2089   appData.pgnEventHeader = "Computer Chess Game";\r
2090   appData.defaultFrcPosition = -1;\r
2091   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2092   appData.saveOutOfBookInfo = TRUE;\r
2093   appData.showEvalInMoveHistory = TRUE;\r
2094   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2095   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2096   appData.highlightMoveWithArrow = FALSE;\r
2097   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2098   appData.useStickyWindows = TRUE;\r
2099   appData.adjudicateDrawMoves = 0;\r
2100   appData.autoDisplayComment = TRUE;\r
2101   appData.autoDisplayTags = TRUE;\r
2102   appData.firstIsUCI = FALSE;\r
2103   appData.secondIsUCI = FALSE;\r
2104   appData.firstHasOwnBookUCI = TRUE;\r
2105   appData.secondHasOwnBookUCI = TRUE;\r
2106   appData.polyglotDir = "";\r
2107   appData.usePolyglotBook = FALSE;\r
2108   appData.polyglotBook = "";\r
2109   appData.defaultHashSize = 64;\r
2110   appData.defaultCacheSizeEGTB = 4;\r
2111   appData.defaultPathEGTB = "c:\\egtb";\r
2112   appData.firstOptions = "";\r
2113   appData.secondOptions = "";\r
2114 \r
2115   InitWindowPlacement( &wpGameList );\r
2116   InitWindowPlacement( &wpMoveHistory );\r
2117   InitWindowPlacement( &wpEvalGraph );\r
2118   InitWindowPlacement( &wpEngineOutput );\r
2119   InitWindowPlacement( &wpConsole );\r
2120 \r
2121   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2122   appData.NrFiles      = -1;\r
2123   appData.NrRanks      = -1;\r
2124   appData.holdingsSize = -1;\r
2125   appData.testClaims   = FALSE;\r
2126   appData.checkMates   = FALSE;\r
2127   appData.materialDraws= FALSE;\r
2128   appData.trivialDraws = FALSE;\r
2129   appData.ruleMoves    = 51;\r
2130   appData.drawRepeats  = 6;\r
2131   appData.matchPause   = 10000;\r
2132   appData.alphaRank    = FALSE;\r
2133   appData.allWhite     = FALSE;\r
2134   appData.upsideDown   = FALSE;\r
2135   appData.serverPause  = 15;\r
2136   appData.serverMovesName   = NULL;\r
2137   appData.suppressLoadMoves = FALSE;\r
2138   appData.firstTimeOdds  = 1;\r
2139   appData.secondTimeOdds = 1;\r
2140   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2141   appData.secondAccumulateTC = 1;\r
2142   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2143   appData.secondNPS = -1;\r
2144   appData.engineComments = 1;\r
2145   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2146   appData.egtFormats = "";\r
2147 \r
2148 #ifdef ZIPPY\r
2149   appData.zippyTalk = ZIPPY_TALK;\r
2150   appData.zippyPlay = ZIPPY_PLAY;\r
2151   appData.zippyLines = ZIPPY_LINES;\r
2152   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2153   appData.zippyPassword = ZIPPY_PASSWORD;\r
2154   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2155   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2156   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2157   appData.zippyUseI = ZIPPY_USE_I;\r
2158   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2159   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2160   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2161   appData.zippyGameStart = ZIPPY_GAME_START;\r
2162   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2163   appData.zippyAbort = ZIPPY_ABORT;\r
2164   appData.zippyVariants = ZIPPY_VARIANTS;\r
2165   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2166   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2167 #endif\r
2168 \r
2169   /* Point font array elements to structures and\r
2170      parse default font names */\r
2171   for (i=0; i<NUM_FONTS; i++) {\r
2172     for (j=0; j<NUM_SIZES; j++) {\r
2173       font[j][i] = &fontRec[j][i];\r
2174       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2175     }\r
2176   }\r
2177   \r
2178   /* Parse default settings file if any */\r
2179   if (ParseSettingsFile(settingsFileName, buf)) {\r
2180     settingsFileName = strdup(buf);\r
2181   }\r
2182 \r
2183   /* Parse command line */\r
2184   ParseArgs(StringGet, &lpCmdLine);\r
2185 \r
2186   /* [HGM] make sure board size is acceptable */\r
2187   if(appData.NrFiles > BOARD_SIZE ||\r
2188      appData.NrRanks > BOARD_SIZE   )\r
2189       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2190 \r
2191   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2192    * with options from the command line, we now make an even higher priority\r
2193    * overrule by WB options attached to the engine command line. This so that\r
2194    * tournament managers can use WB options (such as /timeOdds) that follow\r
2195    * the engines.\r
2196    */\r
2197   if(appData.firstChessProgram != NULL) {\r
2198       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2199       static char *f = "first";\r
2200       char buf[MSG_SIZ], *q = buf;\r
2201       if(p != NULL) { // engine command line contains WinBoard options\r
2202           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2203           ParseArgs(StringGet, &q);\r
2204           p[-1] = 0; // cut them offengine command line\r
2205       }\r
2206   }\r
2207   // now do same for second chess program\r
2208   if(appData.secondChessProgram != NULL) {\r
2209       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2210       static char *s = "second";\r
2211       char buf[MSG_SIZ], *q = buf;\r
2212       if(p != NULL) { // engine command line contains WinBoard options\r
2213           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2214           ParseArgs(StringGet, &q);\r
2215           p[-1] = 0; // cut them offengine command line\r
2216       }\r
2217   }\r
2218 \r
2219 \r
2220   /* Propagate options that affect others */\r
2221   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2222   if (appData.icsActive || appData.noChessProgram) {\r
2223      chessProgram = FALSE;  /* not local chess program mode */\r
2224   }\r
2225 \r
2226   /* Open startup dialog if needed */\r
2227   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2228       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2229       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2230                         *appData.secondChessProgram == NULLCHAR))) {\r
2231     FARPROC lpProc;\r
2232     \r
2233     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2234     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2235     FreeProcInstance(lpProc);\r
2236   }\r
2237 \r
2238   /* Make sure save files land in the right (?) directory */\r
2239   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2240     appData.saveGameFile = strdup(buf);\r
2241   }\r
2242   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2243     appData.savePositionFile = strdup(buf);\r
2244   }\r
2245 \r
2246   /* Finish initialization for fonts and sounds */\r
2247   for (i=0; i<NUM_FONTS; i++) {\r
2248     for (j=0; j<NUM_SIZES; j++) {\r
2249       CreateFontInMF(font[j][i]);\r
2250     }\r
2251   }\r
2252   /* xboard, and older WinBoards, controlled the move sound with the\r
2253      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2254      always turn the option on (so that the backend will call us),\r
2255      then let the user turn the sound off by setting it to silence if\r
2256      desired.  To accommodate old winboard.ini files saved by old\r
2257      versions of WinBoard, we also turn off the sound if the option\r
2258      was initially set to false. */\r
2259   if (!appData.ringBellAfterMoves) {\r
2260     sounds[(int)SoundMove].name = strdup("");\r
2261     appData.ringBellAfterMoves = TRUE;\r
2262   }\r
2263   GetCurrentDirectory(MSG_SIZ, currDir);\r
2264   SetCurrentDirectory(installDir);\r
2265   LoadAllSounds();\r
2266   SetCurrentDirectory(currDir);\r
2267 \r
2268   p = icsTextMenuString;\r
2269   if (p[0] == '@') {\r
2270     FILE* f = fopen(p + 1, "r");\r
2271     if (f == NULL) {\r
2272       DisplayFatalError(p + 1, errno, 2);\r
2273       return;\r
2274     }\r
2275     i = fread(buf, 1, sizeof(buf)-1, f);\r
2276     fclose(f);\r
2277     buf[i] = NULLCHAR;\r
2278     p = buf;\r
2279   }\r
2280   ParseIcsTextMenu(strdup(p));\r
2281 }\r
2282 \r
2283 \r
2284 VOID\r
2285 InitMenuChecks()\r
2286 {\r
2287   HMENU hmenu = GetMenu(hwndMain);\r
2288 \r
2289   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2290                         MF_BYCOMMAND|((appData.icsActive &&\r
2291                                        *appData.icsCommPort != NULLCHAR) ?\r
2292                                       MF_ENABLED : MF_GRAYED));\r
2293   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2294                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2295                                      MF_CHECKED : MF_UNCHECKED));\r
2296 }\r
2297 \r
2298 \r
2299 VOID\r
2300 SaveSettings(char* name)\r
2301 {\r
2302   FILE *f;\r
2303   ArgDescriptor *ad;\r
2304   WINDOWPLACEMENT wp;\r
2305   char dir[MSG_SIZ];\r
2306 \r
2307   if (!hwndMain) return;\r
2308 \r
2309   GetCurrentDirectory(MSG_SIZ, dir);\r
2310   SetCurrentDirectory(installDir);\r
2311   f = fopen(name, "w");\r
2312   SetCurrentDirectory(dir);\r
2313   if (f == NULL) {\r
2314     DisplayError(name, errno);\r
2315     return;\r
2316   }\r
2317   fprintf(f, ";\n");\r
2318   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2319   fprintf(f, ";\n");\r
2320   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2321   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2322   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2323   fprintf(f, ";\n");\r
2324 \r
2325   wp.length = sizeof(WINDOWPLACEMENT);\r
2326   GetWindowPlacement(hwndMain, &wp);\r
2327   boardX = wp.rcNormalPosition.left;\r
2328   boardY = wp.rcNormalPosition.top;\r
2329 \r
2330   if (hwndConsole) {\r
2331     GetWindowPlacement(hwndConsole, &wp);\r
2332     wpConsole.x = wp.rcNormalPosition.left;\r
2333     wpConsole.y = wp.rcNormalPosition.top;\r
2334     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2335     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2336   }\r
2337 \r
2338   if (analysisDialog) {\r
2339     GetWindowPlacement(analysisDialog, &wp);\r
2340     analysisX = wp.rcNormalPosition.left;\r
2341     analysisY = wp.rcNormalPosition.top;\r
2342     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2343     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2344   }\r
2345 \r
2346   if (commentDialog) {\r
2347     GetWindowPlacement(commentDialog, &wp);\r
2348     commentX = wp.rcNormalPosition.left;\r
2349     commentY = wp.rcNormalPosition.top;\r
2350     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2351     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2352   }\r
2353 \r
2354   if (editTagsDialog) {\r
2355     GetWindowPlacement(editTagsDialog, &wp);\r
2356     editTagsX = wp.rcNormalPosition.left;\r
2357     editTagsY = wp.rcNormalPosition.top;\r
2358     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2359     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2360   }\r
2361 \r
2362   if (gameListDialog) {\r
2363     GetWindowPlacement(gameListDialog, &wp);\r
2364     wpGameList.x = wp.rcNormalPosition.left;\r
2365     wpGameList.y = wp.rcNormalPosition.top;\r
2366     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2367     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2368   }\r
2369 \r
2370   /* [AS] Move history */\r
2371   wpMoveHistory.visible = MoveHistoryIsUp();\r
2372   \r
2373   if( moveHistoryDialog ) {\r
2374     GetWindowPlacement(moveHistoryDialog, &wp);\r
2375     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2376     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2377     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2378     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2379   }\r
2380 \r
2381   /* [AS] Eval graph */\r
2382   wpEvalGraph.visible = EvalGraphIsUp();\r
2383 \r
2384   if( evalGraphDialog ) {\r
2385     GetWindowPlacement(evalGraphDialog, &wp);\r
2386     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2387     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2388     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2389     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2390   }\r
2391 \r
2392   /* [AS] Engine output */\r
2393   wpEngineOutput.visible = EngineOutputIsUp();\r
2394 \r
2395   if( engineOutputDialog ) {\r
2396     GetWindowPlacement(engineOutputDialog, &wp);\r
2397     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2398     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2399     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2400     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2401   }\r
2402 \r
2403   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2404     if (!ad->save) continue;\r
2405     switch (ad->argType) {\r
2406     case ArgString:\r
2407       {\r
2408         char *p = *(char **)ad->argLoc;\r
2409         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2410           /* Quote multiline values or \-containing values\r
2411              with { } if possible */\r
2412           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2413         } else {\r
2414           /* Else quote with " " */\r
2415           fprintf(f, "/%s=\"", ad->argName);\r
2416           while (*p) {\r
2417             if (*p == '\n') fprintf(f, "\n");\r
2418             else if (*p == '\r') fprintf(f, "\\r");\r
2419             else if (*p == '\t') fprintf(f, "\\t");\r
2420             else if (*p == '\b') fprintf(f, "\\b");\r
2421             else if (*p == '\f') fprintf(f, "\\f");\r
2422             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2423             else if (*p == '\"') fprintf(f, "\\\"");\r
2424             else if (*p == '\\') fprintf(f, "\\\\");\r
2425             else putc(*p, f);\r
2426             p++;\r
2427           }\r
2428           fprintf(f, "\"\n");\r
2429         }\r
2430       }\r
2431       break;\r
2432     case ArgInt:\r
2433     case ArgZ:\r
2434       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2435       break;\r
2436     case ArgX:\r
2437       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2438       break;\r
2439     case ArgY:\r
2440       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2441       break;\r
2442     case ArgFloat:\r
2443       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2444       break;\r
2445     case ArgBoolean:\r
2446       fprintf(f, "/%s=%s\n", ad->argName, \r
2447         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2448       break;\r
2449     case ArgTrue:\r
2450       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2451       break;\r
2452     case ArgFalse:\r
2453       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2454       break;\r
2455     case ArgColor:\r
2456       {\r
2457         COLORREF color = *(COLORREF *)ad->argLoc;\r
2458         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2459           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2460       }\r
2461       break;\r
2462     case ArgAttribs:\r
2463       {\r
2464         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2465         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2466           (ta->effects & CFE_BOLD) ? "b" : "",\r
2467           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2468           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2469           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2470           (ta->effects) ? " " : "",\r
2471           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2472       }\r
2473       break;\r
2474     case ArgFilename:\r
2475       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2476         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2477       } else {\r
2478         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2479       }\r
2480       break;\r
2481     case ArgBoardSize:\r
2482       fprintf(f, "/%s=%s\n", ad->argName,\r
2483               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2484       break;\r
2485     case ArgFont:\r
2486       {\r
2487         int bs;\r
2488         for (bs=0; bs<NUM_SIZES; bs++) {\r
2489           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2490           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2491           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2492             ad->argName, mfp->faceName, mfp->pointSize,\r
2493             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2494             mfp->bold ? "b" : "",\r
2495             mfp->italic ? "i" : "",\r
2496             mfp->underline ? "u" : "",\r
2497             mfp->strikeout ? "s" : "");\r
2498         }\r
2499       }\r
2500       break;\r
2501     case ArgCommSettings:\r
2502       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2503     case ArgNone:\r
2504     case ArgSettingsFilename: ;\r
2505     }\r
2506   }\r
2507   fclose(f);\r
2508 }\r
2509 \r
2510 \r
2511 \r
2512 /*---------------------------------------------------------------------------*\\r
2513  *\r
2514  * GDI board drawing routines\r
2515  *\r
2516 \*---------------------------------------------------------------------------*/\r
2517 \r
2518 /* [AS] Draw square using background texture */\r
2519 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2520 {\r
2521     XFORM   x;\r
2522 \r
2523     if( mode == 0 ) {\r
2524         return; /* Should never happen! */\r
2525     }\r
2526 \r
2527     SetGraphicsMode( dst, GM_ADVANCED );\r
2528 \r
2529     switch( mode ) {\r
2530     case 1:\r
2531         /* Identity */\r
2532         break;\r
2533     case 2:\r
2534         /* X reflection */\r
2535         x.eM11 = -1.0;\r
2536         x.eM12 = 0;\r
2537         x.eM21 = 0;\r
2538         x.eM22 = 1.0;\r
2539         x.eDx = (FLOAT) dw + dx - 1;\r
2540         x.eDy = 0;\r
2541         dx = 0;\r
2542         SetWorldTransform( dst, &x );\r
2543         break;\r
2544     case 3:\r
2545         /* Y reflection */\r
2546         x.eM11 = 1.0;\r
2547         x.eM12 = 0;\r
2548         x.eM21 = 0;\r
2549         x.eM22 = -1.0;\r
2550         x.eDx = 0;\r
2551         x.eDy = (FLOAT) dh + dy - 1;\r
2552         dy = 0;\r
2553         SetWorldTransform( dst, &x );\r
2554         break;\r
2555     case 4:\r
2556         /* X/Y flip */\r
2557         x.eM11 = 0;\r
2558         x.eM12 = 1.0;\r
2559         x.eM21 = 1.0;\r
2560         x.eM22 = 0;\r
2561         x.eDx = (FLOAT) dx;\r
2562         x.eDy = (FLOAT) dy;\r
2563         dx = 0;\r
2564         dy = 0;\r
2565         SetWorldTransform( dst, &x );\r
2566         break;\r
2567     }\r
2568 \r
2569     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2570 \r
2571     x.eM11 = 1.0;\r
2572     x.eM12 = 0;\r
2573     x.eM21 = 0;\r
2574     x.eM22 = 1.0;\r
2575     x.eDx = 0;\r
2576     x.eDy = 0;\r
2577     SetWorldTransform( dst, &x );\r
2578 \r
2579     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2580 }\r
2581 \r
2582 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2583 enum {\r
2584     PM_WP = (int) WhitePawn, \r
2585     PM_WN = (int) WhiteKnight, \r
2586     PM_WB = (int) WhiteBishop, \r
2587     PM_WR = (int) WhiteRook, \r
2588     PM_WQ = (int) WhiteQueen, \r
2589     PM_WF = (int) WhiteFerz, \r
2590     PM_WW = (int) WhiteWazir, \r
2591     PM_WE = (int) WhiteAlfil, \r
2592     PM_WM = (int) WhiteMan, \r
2593     PM_WO = (int) WhiteCannon, \r
2594     PM_WU = (int) WhiteUnicorn, \r
2595     PM_WH = (int) WhiteNightrider, \r
2596     PM_WA = (int) WhiteAngel, \r
2597     PM_WC = (int) WhiteMarshall, \r
2598     PM_WAB = (int) WhiteCardinal, \r
2599     PM_WD = (int) WhiteDragon, \r
2600     PM_WL = (int) WhiteLance, \r
2601     PM_WS = (int) WhiteCobra, \r
2602     PM_WV = (int) WhiteFalcon, \r
2603     PM_WSG = (int) WhiteSilver, \r
2604     PM_WG = (int) WhiteGrasshopper, \r
2605     PM_WK = (int) WhiteKing,\r
2606     PM_BP = (int) BlackPawn, \r
2607     PM_BN = (int) BlackKnight, \r
2608     PM_BB = (int) BlackBishop, \r
2609     PM_BR = (int) BlackRook, \r
2610     PM_BQ = (int) BlackQueen, \r
2611     PM_BF = (int) BlackFerz, \r
2612     PM_BW = (int) BlackWazir, \r
2613     PM_BE = (int) BlackAlfil, \r
2614     PM_BM = (int) BlackMan,\r
2615     PM_BO = (int) BlackCannon, \r
2616     PM_BU = (int) BlackUnicorn, \r
2617     PM_BH = (int) BlackNightrider, \r
2618     PM_BA = (int) BlackAngel, \r
2619     PM_BC = (int) BlackMarshall, \r
2620     PM_BG = (int) BlackGrasshopper, \r
2621     PM_BAB = (int) BlackCardinal,\r
2622     PM_BD = (int) BlackDragon,\r
2623     PM_BL = (int) BlackLance,\r
2624     PM_BS = (int) BlackCobra,\r
2625     PM_BV = (int) BlackFalcon,\r
2626     PM_BSG = (int) BlackSilver,\r
2627     PM_BK = (int) BlackKing\r
2628 };\r
2629 \r
2630 static HFONT hPieceFont = NULL;\r
2631 static HBITMAP hPieceMask[(int) EmptySquare];\r
2632 static HBITMAP hPieceFace[(int) EmptySquare];\r
2633 static int fontBitmapSquareSize = 0;\r
2634 static char pieceToFontChar[(int) EmptySquare] =\r
2635                               { 'p', 'n', 'b', 'r', 'q', \r
2636                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2637                       'k', 'o', 'm', 'v', 't', 'w', \r
2638                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2639                                                               'l' };\r
2640 \r
2641 extern BOOL SetCharTable( char *table, const char * map );\r
2642 /* [HGM] moved to backend.c */\r
2643 \r
2644 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2645 {\r
2646     HBRUSH hbrush;\r
2647     BYTE r1 = GetRValue( color );\r
2648     BYTE g1 = GetGValue( color );\r
2649     BYTE b1 = GetBValue( color );\r
2650     BYTE r2 = r1 / 2;\r
2651     BYTE g2 = g1 / 2;\r
2652     BYTE b2 = b1 / 2;\r
2653     RECT rc;\r
2654 \r
2655     /* Create a uniform background first */\r
2656     hbrush = CreateSolidBrush( color );\r
2657     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2658     FillRect( hdc, &rc, hbrush );\r
2659     DeleteObject( hbrush );\r
2660     \r
2661     if( mode == 1 ) {\r
2662         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2663         int steps = squareSize / 2;\r
2664         int i;\r
2665 \r
2666         for( i=0; i<steps; i++ ) {\r
2667             BYTE r = r1 - (r1-r2) * i / steps;\r
2668             BYTE g = g1 - (g1-g2) * i / steps;\r
2669             BYTE b = b1 - (b1-b2) * i / steps;\r
2670 \r
2671             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2672             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2673             FillRect( hdc, &rc, hbrush );\r
2674             DeleteObject(hbrush);\r
2675         }\r
2676     }\r
2677     else if( mode == 2 ) {\r
2678         /* Diagonal gradient, good more or less for every piece */\r
2679         POINT triangle[3];\r
2680         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2681         HBRUSH hbrush_old;\r
2682         int steps = squareSize;\r
2683         int i;\r
2684 \r
2685         triangle[0].x = squareSize - steps;\r
2686         triangle[0].y = squareSize;\r
2687         triangle[1].x = squareSize;\r
2688         triangle[1].y = squareSize;\r
2689         triangle[2].x = squareSize;\r
2690         triangle[2].y = squareSize - steps;\r
2691 \r
2692         for( i=0; i<steps; i++ ) {\r
2693             BYTE r = r1 - (r1-r2) * i / steps;\r
2694             BYTE g = g1 - (g1-g2) * i / steps;\r
2695             BYTE b = b1 - (b1-b2) * i / steps;\r
2696 \r
2697             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2698             hbrush_old = SelectObject( hdc, hbrush );\r
2699             Polygon( hdc, triangle, 3 );\r
2700             SelectObject( hdc, hbrush_old );\r
2701             DeleteObject(hbrush);\r
2702             triangle[0].x++;\r
2703             triangle[2].y++;\r
2704         }\r
2705 \r
2706         SelectObject( hdc, hpen );\r
2707     }\r
2708 }\r
2709 \r
2710 /*\r
2711     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2712     seems to work ok. The main problem here is to find the "inside" of a chess\r
2713     piece: follow the steps as explained below.\r
2714 */\r
2715 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2716 {\r
2717     HBITMAP hbm;\r
2718     HBITMAP hbm_old;\r
2719     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2720     RECT rc;\r
2721     SIZE sz;\r
2722     POINT pt;\r
2723     int backColor = whitePieceColor; \r
2724     int foreColor = blackPieceColor;\r
2725     \r
2726     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2727         backColor = appData.fontBackColorWhite;\r
2728         foreColor = appData.fontForeColorWhite;\r
2729     }\r
2730     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2731         backColor = appData.fontBackColorBlack;\r
2732         foreColor = appData.fontForeColorBlack;\r
2733     }\r
2734 \r
2735     /* Mask */\r
2736     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2737 \r
2738     hbm_old = SelectObject( hdc, hbm );\r
2739 \r
2740     rc.left = 0;\r
2741     rc.top = 0;\r
2742     rc.right = squareSize;\r
2743     rc.bottom = squareSize;\r
2744 \r
2745     /* Step 1: background is now black */\r
2746     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2747 \r
2748     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2749 \r
2750     pt.x = (squareSize - sz.cx) / 2;\r
2751     pt.y = (squareSize - sz.cy) / 2;\r
2752 \r
2753     SetBkMode( hdc, TRANSPARENT );\r
2754     SetTextColor( hdc, chroma );\r
2755     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2756     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2757 \r
2758     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2759     /* Step 3: the area outside the piece is filled with white */\r
2760 //    FloodFill( hdc, 0, 0, chroma );\r
2761     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2762     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2763     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2764     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2765     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2766     /* \r
2767         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2768         but if the start point is not inside the piece we're lost!\r
2769         There should be a better way to do this... if we could create a region or path\r
2770         from the fill operation we would be fine for example.\r
2771     */\r
2772 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2773     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2774 \r
2775     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2776         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2777         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2778 \r
2779         SelectObject( dc2, bm2 );\r
2780         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2781         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2782         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2783         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2784         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2785 \r
2786         DeleteDC( dc2 );\r
2787         DeleteObject( bm2 );\r
2788     }\r
2789 \r
2790     SetTextColor( hdc, 0 );\r
2791     /* \r
2792         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2793         draw the piece again in black for safety.\r
2794     */\r
2795     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2796 \r
2797     SelectObject( hdc, hbm_old );\r
2798 \r
2799     if( hPieceMask[index] != NULL ) {\r
2800         DeleteObject( hPieceMask[index] );\r
2801     }\r
2802 \r
2803     hPieceMask[index] = hbm;\r
2804 \r
2805     /* Face */\r
2806     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2807 \r
2808     SelectObject( hdc, hbm );\r
2809 \r
2810     {\r
2811         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2812         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2813         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2814 \r
2815         SelectObject( dc1, hPieceMask[index] );\r
2816         SelectObject( dc2, bm2 );\r
2817         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2818         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2819         \r
2820         /* \r
2821             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2822             the piece background and deletes (makes transparent) the rest.\r
2823             Thanks to that mask, we are free to paint the background with the greates\r
2824             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2825             We use this, to make gradients and give the pieces a "roundish" look.\r
2826         */\r
2827         SetPieceBackground( hdc, backColor, 2 );\r
2828         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2829 \r
2830         DeleteDC( dc2 );\r
2831         DeleteDC( dc1 );\r
2832         DeleteObject( bm2 );\r
2833     }\r
2834 \r
2835     SetTextColor( hdc, foreColor );\r
2836     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2837 \r
2838     SelectObject( hdc, hbm_old );\r
2839 \r
2840     if( hPieceFace[index] != NULL ) {\r
2841         DeleteObject( hPieceFace[index] );\r
2842     }\r
2843 \r
2844     hPieceFace[index] = hbm;\r
2845 }\r
2846 \r
2847 static int TranslatePieceToFontPiece( int piece )\r
2848 {\r
2849     switch( piece ) {\r
2850     case BlackPawn:\r
2851         return PM_BP;\r
2852     case BlackKnight:\r
2853         return PM_BN;\r
2854     case BlackBishop:\r
2855         return PM_BB;\r
2856     case BlackRook:\r
2857         return PM_BR;\r
2858     case BlackQueen:\r
2859         return PM_BQ;\r
2860     case BlackKing:\r
2861         return PM_BK;\r
2862     case WhitePawn:\r
2863         return PM_WP;\r
2864     case WhiteKnight:\r
2865         return PM_WN;\r
2866     case WhiteBishop:\r
2867         return PM_WB;\r
2868     case WhiteRook:\r
2869         return PM_WR;\r
2870     case WhiteQueen:\r
2871         return PM_WQ;\r
2872     case WhiteKing:\r
2873         return PM_WK;\r
2874 \r
2875     case BlackAngel:\r
2876         return PM_BA;\r
2877     case BlackMarshall:\r
2878         return PM_BC;\r
2879     case BlackFerz:\r
2880         return PM_BF;\r
2881     case BlackNightrider:\r
2882         return PM_BH;\r
2883     case BlackAlfil:\r
2884         return PM_BE;\r
2885     case BlackWazir:\r
2886         return PM_BW;\r
2887     case BlackUnicorn:\r
2888         return PM_BU;\r
2889     case BlackCannon:\r
2890         return PM_BO;\r
2891     case BlackGrasshopper:\r
2892         return PM_BG;\r
2893     case BlackMan:\r
2894         return PM_BM;\r
2895     case BlackSilver:\r
2896         return PM_BSG;\r
2897     case BlackLance:\r
2898         return PM_BL;\r
2899     case BlackFalcon:\r
2900         return PM_BV;\r
2901     case BlackCobra:\r
2902         return PM_BS;\r
2903     case BlackCardinal:\r
2904         return PM_BAB;\r
2905     case BlackDragon:\r
2906         return PM_BD;\r
2907 \r
2908     case WhiteAngel:\r
2909         return PM_WA;\r
2910     case WhiteMarshall:\r
2911         return PM_WC;\r
2912     case WhiteFerz:\r
2913         return PM_WF;\r
2914     case WhiteNightrider:\r
2915         return PM_WH;\r
2916     case WhiteAlfil:\r
2917         return PM_WE;\r
2918     case WhiteWazir:\r
2919         return PM_WW;\r
2920     case WhiteUnicorn:\r
2921         return PM_WU;\r
2922     case WhiteCannon:\r
2923         return PM_WO;\r
2924     case WhiteGrasshopper:\r
2925         return PM_WG;\r
2926     case WhiteMan:\r
2927         return PM_WM;\r
2928     case WhiteSilver:\r
2929         return PM_WSG;\r
2930     case WhiteLance:\r
2931         return PM_WL;\r
2932     case WhiteFalcon:\r
2933         return PM_WV;\r
2934     case WhiteCobra:\r
2935         return PM_WS;\r
2936     case WhiteCardinal:\r
2937         return PM_WAB;\r
2938     case WhiteDragon:\r
2939         return PM_WD;\r
2940     }\r
2941 \r
2942     return 0;\r
2943 }\r
2944 \r
2945 void CreatePiecesFromFont()\r
2946 {\r
2947     LOGFONT lf;\r
2948     HDC hdc_window = NULL;\r
2949     HDC hdc = NULL;\r
2950     HFONT hfont_old;\r
2951     int fontHeight;\r
2952     int i;\r
2953 \r
2954     if( fontBitmapSquareSize < 0 ) {\r
2955         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2956         return;\r
2957     }\r
2958 \r
2959     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2960         fontBitmapSquareSize = -1;\r
2961         return;\r
2962     }\r
2963 \r
2964     if( fontBitmapSquareSize != squareSize ) {\r
2965         hdc_window = GetDC( hwndMain );\r
2966         hdc = CreateCompatibleDC( hdc_window );\r
2967 \r
2968         if( hPieceFont != NULL ) {\r
2969             DeleteObject( hPieceFont );\r
2970         }\r
2971         else {\r
2972             for( i=0; i<=(int)BlackKing; i++ ) {\r
2973                 hPieceMask[i] = NULL;\r
2974                 hPieceFace[i] = NULL;\r
2975             }\r
2976         }\r
2977 \r
2978         fontHeight = 75;\r
2979 \r
2980         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2981             fontHeight = appData.fontPieceSize;\r
2982         }\r
2983 \r
2984         fontHeight = (fontHeight * squareSize) / 100;\r
2985 \r
2986         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2987         lf.lfWidth = 0;\r
2988         lf.lfEscapement = 0;\r
2989         lf.lfOrientation = 0;\r
2990         lf.lfWeight = FW_NORMAL;\r
2991         lf.lfItalic = 0;\r
2992         lf.lfUnderline = 0;\r
2993         lf.lfStrikeOut = 0;\r
2994         lf.lfCharSet = DEFAULT_CHARSET;\r
2995         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2996         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2997         lf.lfQuality = PROOF_QUALITY;\r
2998         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2999         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
3000         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
3001 \r
3002         hPieceFont = CreateFontIndirect( &lf );\r
3003 \r
3004         if( hPieceFont == NULL ) {\r
3005             fontBitmapSquareSize = -2;\r
3006         }\r
3007         else {\r
3008             /* Setup font-to-piece character table */\r
3009             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
3010                 /* No (or wrong) global settings, try to detect the font */\r
3011                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
3012                     /* Alpha */\r
3013                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
3014                 }\r
3015                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3016                     /* DiagramTT* family */\r
3017                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3018                 }\r
3019                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3020                     /* Fairy symbols */\r
3021                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3022                 }\r
3023                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3024                     /* Good Companion (Some characters get warped as literal :-( */\r
3025                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3026                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3027                     SetCharTable(pieceToFontChar, s);\r
3028                 }\r
3029                 else {\r
3030                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3031                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3032                 }\r
3033             }\r
3034 \r
3035             /* Create bitmaps */\r
3036             hfont_old = SelectObject( hdc, hPieceFont );\r
3037 #if 0\r
3038             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
3039             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
3040             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
3041             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
3042             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
3043             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
3044             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
3045             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
3046             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
3047             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
3048             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
3049             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
3050 \r
3051             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
3052             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
3053             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
3054             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
3055             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
3056             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
3057             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
3058             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
3059             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
3060             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
3061             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
3062             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
3063             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
3064             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
3065             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
3066             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
3067             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
3068             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
3069             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
3070             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
3071             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
3072             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
3073             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
3074             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
3075             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
3076             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
3077             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
3078             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
3079             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
3080             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
3081             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
3082             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
3083 #else\r
3084             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3085                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3086                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3087 #endif\r
3088             SelectObject( hdc, hfont_old );\r
3089 \r
3090             fontBitmapSquareSize = squareSize;\r
3091         }\r
3092     }\r
3093 \r
3094     if( hdc != NULL ) {\r
3095         DeleteDC( hdc );\r
3096     }\r
3097 \r
3098     if( hdc_window != NULL ) {\r
3099         ReleaseDC( hwndMain, hdc_window );\r
3100     }\r
3101 }\r
3102 \r
3103 HBITMAP\r
3104 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3105 {\r
3106   char name[128];\r
3107 \r
3108   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3109   if (gameInfo.event &&\r
3110       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3111       strcmp(name, "k80s") == 0) {\r
3112     strcpy(name, "tim");\r
3113   }\r
3114   return LoadBitmap(hinst, name);\r
3115 }\r
3116 \r
3117 \r
3118 /* Insert a color into the program's logical palette\r
3119    structure.  This code assumes the given color is\r
3120    the result of the RGB or PALETTERGB macro, and it\r
3121    knows how those macros work (which is documented).\r
3122 */\r
3123 VOID\r
3124 InsertInPalette(COLORREF color)\r
3125 {\r
3126   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3127 \r
3128   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3129     DisplayFatalError("Too many colors", 0, 1);\r
3130     pLogPal->palNumEntries--;\r
3131     return;\r
3132   }\r
3133 \r
3134   pe->peFlags = (char) 0;\r
3135   pe->peRed = (char) (0xFF & color);\r
3136   pe->peGreen = (char) (0xFF & (color >> 8));\r
3137   pe->peBlue = (char) (0xFF & (color >> 16));\r
3138   return;\r
3139 }\r
3140 \r
3141 \r
3142 VOID\r
3143 InitDrawingColors()\r
3144 {\r
3145   if (pLogPal == NULL) {\r
3146     /* Allocate enough memory for a logical palette with\r
3147      * PALETTESIZE entries and set the size and version fields\r
3148      * of the logical palette structure.\r
3149      */\r
3150     pLogPal = (NPLOGPALETTE)\r
3151       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3152                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3153     pLogPal->palVersion    = 0x300;\r
3154   }\r
3155   pLogPal->palNumEntries = 0;\r
3156 \r
3157   InsertInPalette(lightSquareColor);\r
3158   InsertInPalette(darkSquareColor);\r
3159   InsertInPalette(whitePieceColor);\r
3160   InsertInPalette(blackPieceColor);\r
3161   InsertInPalette(highlightSquareColor);\r
3162   InsertInPalette(premoveHighlightColor);\r
3163 \r
3164   /*  create a logical color palette according the information\r
3165    *  in the LOGPALETTE structure.\r
3166    */\r
3167   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3168 \r
3169   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3170   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3171   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3172   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3173   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3174   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3175   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3176   /* [AS] Force rendering of the font-based pieces */\r
3177   if( fontBitmapSquareSize > 0 ) {\r
3178     fontBitmapSquareSize = 0;\r
3179   }\r
3180 }\r
3181 \r
3182 \r
3183 int\r
3184 BoardWidth(int boardSize, int n)\r
3185 { /* [HGM] argument n added to allow different width and height */\r
3186   int lineGap = sizeInfo[boardSize].lineGap;\r
3187 \r
3188   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3189       lineGap = appData.overrideLineGap;\r
3190   }\r
3191 \r
3192   return (n + 1) * lineGap +\r
3193           n * sizeInfo[boardSize].squareSize;\r
3194 }\r
3195 \r
3196 /* Respond to board resize by dragging edge */\r
3197 VOID\r
3198 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3199 {\r
3200   BoardSize newSize = NUM_SIZES - 1;\r
3201   static int recurse = 0;\r
3202   if (IsIconic(hwndMain)) return;\r
3203   if (recurse > 0) return;\r
3204   recurse++;\r
3205   while (newSize > 0) {\r
3206         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3207         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3208            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3209     newSize--;\r
3210   } \r
3211   boardSize = newSize;\r
3212   InitDrawingSizes(boardSize, flags);\r
3213   recurse--;\r
3214 }\r
3215 \r
3216 \r
3217 \r
3218 VOID\r
3219 InitDrawingSizes(BoardSize boardSize, int flags)\r
3220 {\r
3221   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3222   ChessSquare piece;\r
3223   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3224   HDC hdc;\r
3225   SIZE clockSize, messageSize;\r
3226   HFONT oldFont;\r
3227   char buf[MSG_SIZ];\r
3228   char *str;\r
3229   HMENU hmenu = GetMenu(hwndMain);\r
3230   RECT crect, wrect, oldRect;\r
3231   int offby;\r
3232   LOGBRUSH logbrush;\r
3233 \r
3234   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3235   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3236 \r
3237   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3238   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3239 \r
3240   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3241   oldRect.top = boardY;\r
3242   oldRect.right = boardX + winWidth;\r
3243   oldRect.bottom = boardY + winHeight;\r
3244 \r
3245   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3246   smallLayout = sizeInfo[boardSize].smallLayout;\r
3247   squareSize = sizeInfo[boardSize].squareSize;\r
3248   lineGap = sizeInfo[boardSize].lineGap;\r
3249   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3250 \r
3251   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3252       lineGap = appData.overrideLineGap;\r
3253   }\r
3254 \r
3255   if (tinyLayout != oldTinyLayout) {\r
3256     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3257     if (tinyLayout) {\r
3258       style &= ~WS_SYSMENU;\r
3259       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3260                  "&Minimize\tCtrl+F4");\r
3261     } else {\r
3262       style |= WS_SYSMENU;\r
3263       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3264     }\r
3265     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3266 \r
3267     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3268       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3269         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3270     }\r
3271     DrawMenuBar(hwndMain);\r
3272   }\r
3273 \r
3274   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3275   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3276 \r
3277   /* Get text area sizes */\r
3278   hdc = GetDC(hwndMain);\r
3279   if (appData.clockMode) {\r
3280     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3281   } else {\r
3282     sprintf(buf, "White");\r
3283   }\r
3284   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3285   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3286   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3287   str = "We only care about the height here";\r
3288   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3289   SelectObject(hdc, oldFont);\r
3290   ReleaseDC(hwndMain, hdc);\r
3291 \r
3292   /* Compute where everything goes */\r
3293   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3294         /* [HGM] logo: if either logo is on, reserve space for it */\r
3295         logoHeight =  2*clockSize.cy;\r
3296         leftLogoRect.left   = OUTER_MARGIN;\r
3297         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3298         leftLogoRect.top    = OUTER_MARGIN;\r
3299         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3300 \r
3301         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3302         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3303         rightLogoRect.top    = OUTER_MARGIN;\r
3304         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3305 \r
3306 \r
3307     whiteRect.left = leftLogoRect.right;\r
3308     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3309     whiteRect.top = OUTER_MARGIN;\r
3310     whiteRect.bottom = whiteRect.top + logoHeight;\r
3311 \r
3312     blackRect.right = rightLogoRect.left;\r
3313     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3314     blackRect.top = whiteRect.top;\r
3315     blackRect.bottom = whiteRect.bottom;\r
3316   } else {\r
3317     whiteRect.left = OUTER_MARGIN;\r
3318     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3319     whiteRect.top = OUTER_MARGIN;\r
3320     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3321 \r
3322     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3323     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3324     blackRect.top = whiteRect.top;\r
3325     blackRect.bottom = whiteRect.bottom;\r
3326   }\r
3327 \r
3328   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3329   if (appData.showButtonBar) {\r
3330     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3331       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3332   } else {\r
3333     messageRect.right = OUTER_MARGIN + boardWidth;\r
3334   }\r
3335   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3336   messageRect.bottom = messageRect.top + messageSize.cy;\r
3337 \r
3338   boardRect.left = OUTER_MARGIN;\r
3339   boardRect.right = boardRect.left + boardWidth;\r
3340   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3341   boardRect.bottom = boardRect.top + boardHeight;\r
3342 \r
3343   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3344   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3345   oldBoardSize = boardSize;\r
3346   oldTinyLayout = tinyLayout;\r
3347   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3348   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3349     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3350   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3351   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3352   winHeight = winH; //       without disturbing window attachments\r
3353   GetWindowRect(hwndMain, &wrect);\r
3354   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3355                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3356 \r
3357   // [HGM] placement: let attached windows follow size change.\r
3358   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3359   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3360   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3361   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3362   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3363 \r
3364   /* compensate if menu bar wrapped */\r
3365   GetClientRect(hwndMain, &crect);\r
3366   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3367   winHeight += offby;\r
3368   switch (flags) {\r
3369   case WMSZ_TOPLEFT:\r
3370     SetWindowPos(hwndMain, NULL, \r
3371                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3372                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3373     break;\r
3374 \r
3375   case WMSZ_TOPRIGHT:\r
3376   case WMSZ_TOP:\r
3377     SetWindowPos(hwndMain, NULL, \r
3378                  wrect.left, wrect.bottom - winHeight, \r
3379                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3380     break;\r
3381 \r
3382   case WMSZ_BOTTOMLEFT:\r
3383   case WMSZ_LEFT:\r
3384     SetWindowPos(hwndMain, NULL, \r
3385                  wrect.right - winWidth, wrect.top, \r
3386                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3387     break;\r
3388 \r
3389   case WMSZ_BOTTOMRIGHT:\r
3390   case WMSZ_BOTTOM:\r
3391   case WMSZ_RIGHT:\r
3392   default:\r
3393     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3394                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3395     break;\r
3396   }\r
3397 \r
3398   hwndPause = NULL;\r
3399   for (i = 0; i < N_BUTTONS; i++) {\r
3400     if (buttonDesc[i].hwnd != NULL) {\r
3401       DestroyWindow(buttonDesc[i].hwnd);\r
3402       buttonDesc[i].hwnd = NULL;\r
3403     }\r
3404     if (appData.showButtonBar) {\r
3405       buttonDesc[i].hwnd =\r
3406         CreateWindow("BUTTON", buttonDesc[i].label,\r
3407                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3408                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3409                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3410                      (HMENU) buttonDesc[i].id,\r
3411                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3412       if (tinyLayout) {\r
3413         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3414                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3415                     MAKELPARAM(FALSE, 0));\r
3416       }\r
3417       if (buttonDesc[i].id == IDM_Pause)\r
3418         hwndPause = buttonDesc[i].hwnd;\r
3419       buttonDesc[i].wndproc = (WNDPROC)\r
3420         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3421     }\r
3422   }\r
3423   if (gridPen != NULL) DeleteObject(gridPen);\r
3424   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3425   if (premovePen != NULL) DeleteObject(premovePen);\r
3426   if (lineGap != 0) {\r
3427     logbrush.lbStyle = BS_SOLID;\r
3428     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3429     gridPen =\r
3430       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3431                    lineGap, &logbrush, 0, NULL);\r
3432     logbrush.lbColor = highlightSquareColor;\r
3433     highlightPen =\r
3434       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3435                    lineGap, &logbrush, 0, NULL);\r
3436 \r
3437     logbrush.lbColor = premoveHighlightColor; \r
3438     premovePen =\r
3439       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3440                    lineGap, &logbrush, 0, NULL);\r
3441 \r
3442     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3443     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3444       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3445       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3446         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3447       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3448         BOARD_WIDTH * (squareSize + lineGap);\r
3449       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3450     }\r
3451     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3452       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3453       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3454         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3455         lineGap / 2 + (i * (squareSize + lineGap));\r
3456       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3457         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3458       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3459     }\r
3460   }\r
3461 \r
3462   /* [HGM] Licensing requirement */\r
3463 #ifdef GOTHIC\r
3464   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3465 #endif\r
3466 #ifdef FALCON\r
3467   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3468 #endif\r
3469   GothicPopUp( "", VariantNormal);\r
3470 \r
3471 \r
3472 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3473 \r
3474   /* Load piece bitmaps for this board size */\r
3475   for (i=0; i<=2; i++) {\r
3476     for (piece = WhitePawn;\r
3477          (int) piece < (int) BlackPawn;\r
3478          piece = (ChessSquare) ((int) piece + 1)) {\r
3479       if (pieceBitmap[i][piece] != NULL)\r
3480         DeleteObject(pieceBitmap[i][piece]);\r
3481     }\r
3482   }\r
3483 \r
3484   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3485   // Orthodox Chess pieces\r
3486   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3487   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3488   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3489   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3490   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3491   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3492   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3493   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3494   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3495   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3496   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3497   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3498   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3499   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3500   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3501   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3502     // in Shogi, Hijack the unused Queen for Lance\r
3503     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3504     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3505     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3506   } else {\r
3507     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3508     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3509     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3510   }\r
3511 \r
3512   if(squareSize <= 72 && squareSize >= 33) { \r
3513     /* A & C are available in most sizes now */\r
3514     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3515       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3516       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3517       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3518       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3519       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3520       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3521       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3522       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3523       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3524       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3525       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3526       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3527     } else { // Smirf-like\r
3528       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3529       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3530       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3531     }\r
3532     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3533       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3534       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3535       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3536     } else { // WinBoard standard\r
3537       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3538       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3539       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3540     }\r
3541   }\r
3542 \r
3543 \r
3544   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3545     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3546     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3547     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3548     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3549     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3550     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3551     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3552     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3553     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3554     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3555     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3556     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3557     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3558     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3559     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3560     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3561     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3562     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3563     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3564     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3565     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3566     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3567     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3568     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3569     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3570     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3571     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3572     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3573     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3574     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3575 \r
3576     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3577       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3578       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3579       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3580       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3581       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3582       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3583       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3584       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3585       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3586       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3587       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3588       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3589     } else {\r
3590       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3591       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3592       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3593       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3594       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3595       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3596       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3597       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3598       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3599       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3600       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3601       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3602     }\r
3603 \r
3604   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3605     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3606     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3607     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3608     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3609     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3610     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3611     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3612     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3613     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3614     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3615     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3616     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3617     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3618     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3619   }\r
3620 \r
3621 \r
3622   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3623   /* special Shogi support in this size */\r
3624   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3625       for (piece = WhitePawn;\r
3626            (int) piece < (int) BlackPawn;\r
3627            piece = (ChessSquare) ((int) piece + 1)) {\r
3628         if (pieceBitmap[i][piece] != NULL)\r
3629           DeleteObject(pieceBitmap[i][piece]);\r
3630       }\r
3631     }\r
3632   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3633   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3634   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3635   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3636   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3637   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3638   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3639   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3640   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3641   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3642   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3643   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3644   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3645   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3646   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3647   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3648   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3649   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3650   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3651   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3652   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3653   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3654   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3655   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3656   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3657   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3658   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3659   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3660   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3661   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3662   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3663   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3664   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3665   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3666   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3667   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3668   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3669   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3670   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3671   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3672   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3673   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3674   minorSize = 0;\r
3675   }\r
3676 }\r
3677 \r
3678 HBITMAP\r
3679 PieceBitmap(ChessSquare p, int kind)\r
3680 {\r
3681   if ((int) p >= (int) BlackPawn)\r
3682     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3683 \r
3684   return pieceBitmap[kind][(int) p];\r
3685 }\r
3686 \r
3687 /***************************************************************/\r
3688 \r
3689 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3690 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3691 /*\r
3692 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3693 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3694 */\r
3695 \r
3696 VOID\r
3697 SquareToPos(int row, int column, int * x, int * y)\r
3698 {\r
3699   if (flipView) {\r
3700     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3701     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3702   } else {\r
3703     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3704     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3705   }\r
3706 }\r
3707 \r
3708 VOID\r
3709 DrawCoordsOnDC(HDC hdc)\r
3710 {\r
3711   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
3712   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
3713   char str[2] = { NULLCHAR, NULLCHAR };\r
3714   int oldMode, oldAlign, x, y, start, i;\r
3715   HFONT oldFont;\r
3716   HBRUSH oldBrush;\r
3717 \r
3718   if (!appData.showCoords)\r
3719     return;\r
3720 \r
3721   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3722 \r
3723   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3724   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3725   oldAlign = GetTextAlign(hdc);\r
3726   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3727 \r
3728   y = boardRect.top + lineGap;\r
3729   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3730 \r
3731   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3732   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3733     str[0] = files[start + i];\r
3734     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3735     y += squareSize + lineGap;\r
3736   }\r
3737 \r
3738   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3739 \r
3740   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3741   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3742     str[0] = ranks[start + i];\r
3743     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3744     x += squareSize + lineGap;\r
3745   }    \r
3746 \r
3747   SelectObject(hdc, oldBrush);\r
3748   SetBkMode(hdc, oldMode);\r
3749   SetTextAlign(hdc, oldAlign);\r
3750   SelectObject(hdc, oldFont);\r
3751 }\r
3752 \r
3753 VOID\r
3754 DrawGridOnDC(HDC hdc)\r
3755 {\r
3756   HPEN oldPen;\r
3757  \r
3758   if (lineGap != 0) {\r
3759     oldPen = SelectObject(hdc, gridPen);\r
3760     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3761     SelectObject(hdc, oldPen);\r
3762   }\r
3763 }\r
3764 \r
3765 #define HIGHLIGHT_PEN 0\r
3766 #define PREMOVE_PEN   1\r
3767 \r
3768 VOID\r
3769 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3770 {\r
3771   int x1, y1;\r
3772   HPEN oldPen, hPen;\r
3773   if (lineGap == 0) return;\r
3774   if (flipView) {\r
3775     x1 = boardRect.left +\r
3776       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3777     y1 = boardRect.top +\r
3778       lineGap/2 + y * (squareSize + lineGap);\r
3779   } else {\r
3780     x1 = boardRect.left +\r
3781       lineGap/2 + x * (squareSize + lineGap);\r
3782     y1 = boardRect.top +\r
3783       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3784   }\r
3785   hPen = pen ? premovePen : highlightPen;\r
3786   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3787   MoveToEx(hdc, x1, y1, NULL);\r
3788   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3789   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3790   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3791   LineTo(hdc, x1, y1);\r
3792   SelectObject(hdc, oldPen);\r
3793 }\r
3794 \r
3795 VOID\r
3796 DrawHighlightsOnDC(HDC hdc)\r
3797 {\r
3798   int i;\r
3799   for (i=0; i<2; i++) {\r
3800     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3801       DrawHighlightOnDC(hdc, TRUE,\r
3802                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3803                         HIGHLIGHT_PEN);\r
3804   }\r
3805   for (i=0; i<2; i++) {\r
3806     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3807         premoveHighlightInfo.sq[i].y >= 0) {\r
3808         DrawHighlightOnDC(hdc, TRUE,\r
3809                           premoveHighlightInfo.sq[i].x, \r
3810                           premoveHighlightInfo.sq[i].y,\r
3811                           PREMOVE_PEN);\r
3812     }\r
3813   }\r
3814 }\r
3815 \r
3816 /* Note: sqcolor is used only in monoMode */\r
3817 /* Note that this code is largely duplicated in woptions.c,\r
3818    function DrawSampleSquare, so that needs to be updated too */\r
3819 VOID\r
3820 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3821 {\r
3822   HBITMAP oldBitmap;\r
3823   HBRUSH oldBrush;\r
3824   int tmpSize;\r
3825 \r
3826   if (appData.blindfold) return;\r
3827 \r
3828   /* [AS] Use font-based pieces if needed */\r
3829   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3830     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3831     CreatePiecesFromFont();\r
3832 \r
3833     if( fontBitmapSquareSize == squareSize ) {\r
3834         int index = TranslatePieceToFontPiece(piece);\r
3835 \r
3836         SelectObject( tmphdc, hPieceMask[ index ] );\r
3837 \r
3838         BitBlt( hdc,\r
3839             x, y,\r
3840             squareSize, squareSize,\r
3841             tmphdc,\r
3842             0, 0,\r
3843             SRCAND );\r
3844 \r
3845         SelectObject( tmphdc, hPieceFace[ index ] );\r
3846 \r
3847         BitBlt( hdc,\r
3848             x, y,\r
3849             squareSize, squareSize,\r
3850             tmphdc,\r
3851             0, 0,\r
3852             SRCPAINT );\r
3853 \r
3854         return;\r
3855     }\r
3856   }\r
3857 \r
3858   if (appData.monoMode) {\r
3859     SelectObject(tmphdc, PieceBitmap(piece, \r
3860       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3861     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3862            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3863   } else {\r
3864     tmpSize = squareSize;\r
3865     if(minorSize &&\r
3866         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3867          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3868       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3869       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3870       x += (squareSize - minorSize)>>1;\r
3871       y += squareSize - minorSize - 2;\r
3872       tmpSize = minorSize;\r
3873     }\r
3874     if (color || appData.allWhite ) {\r
3875       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3876       if( color )\r
3877               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3878       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3879       if(appData.upsideDown && color==flipView)\r
3880         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3881       else\r
3882         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3883 #if 0\r
3884       /* Use black piece color for outline of white pieces */\r
3885       /* Not sure this looks really good (though xboard does it).\r
3886          Maybe better to have another selectable color, default black */\r
3887       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3888       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3889       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3890 #else\r
3891       /* Use black for outline of white pieces */\r
3892       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3893       if(appData.upsideDown && color==flipView)\r
3894         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3895       else\r
3896         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3897 #endif\r
3898     } else {\r
3899 #if 0\r
3900       /* Use white piece color for details of black pieces */\r
3901       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3902          WHITE_PIECE ones aren't always the right shape. */\r
3903       /* Not sure this looks really good (though xboard does it).\r
3904          Maybe better to have another selectable color, default medium gray? */\r
3905       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3906       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3907       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3908       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3909       SelectObject(hdc, blackPieceBrush);\r
3910       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3911 #else\r
3912       /* Use square color for details of black pieces */\r
3913       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3914       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3915       if(appData.upsideDown && !flipView)\r
3916         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3917       else\r
3918         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3919 #endif\r
3920     }\r
3921     SelectObject(hdc, oldBrush);\r
3922     SelectObject(tmphdc, oldBitmap);\r
3923   }\r
3924 }\r
3925 \r
3926 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3927 int GetBackTextureMode( int algo )\r
3928 {\r
3929     int result = BACK_TEXTURE_MODE_DISABLED;\r
3930 \r
3931     switch( algo ) \r
3932     {\r
3933         case BACK_TEXTURE_MODE_PLAIN:\r
3934             result = 1; /* Always use identity map */\r
3935             break;\r
3936         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3937             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3938             break;\r
3939     }\r
3940 \r
3941     return result;\r
3942 }\r
3943 \r
3944 /* \r
3945     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3946     to handle redraws cleanly (as random numbers would always be different).\r
3947 */\r
3948 VOID RebuildTextureSquareInfo()\r
3949 {\r
3950     BITMAP bi;\r
3951     int lite_w = 0;\r
3952     int lite_h = 0;\r
3953     int dark_w = 0;\r
3954     int dark_h = 0;\r
3955     int row;\r
3956     int col;\r
3957 \r
3958     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3959 \r
3960     if( liteBackTexture != NULL ) {\r
3961         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3962             lite_w = bi.bmWidth;\r
3963             lite_h = bi.bmHeight;\r
3964         }\r
3965     }\r
3966 \r
3967     if( darkBackTexture != NULL ) {\r
3968         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3969             dark_w = bi.bmWidth;\r
3970             dark_h = bi.bmHeight;\r
3971         }\r
3972     }\r
3973 \r
3974     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3975         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3976             if( (col + row) & 1 ) {\r
3977                 /* Lite square */\r
3978                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3979                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3980                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3981                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3982                 }\r
3983             }\r
3984             else {\r
3985                 /* Dark square */\r
3986                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3987                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3988                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3989                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3990                 }\r
3991             }\r
3992         }\r
3993     }\r
3994 }\r
3995 \r
3996 /* [AS] Arrow highlighting support */\r
3997 \r
3998 static int A_WIDTH = 5; /* Width of arrow body */\r
3999 \r
4000 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
4001 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
4002 \r
4003 static double Sqr( double x )\r
4004 {\r
4005     return x*x;\r
4006 }\r
4007 \r
4008 static int Round( double x )\r
4009 {\r
4010     return (int) (x + 0.5);\r
4011 }\r
4012 \r
4013 /* Draw an arrow between two points using current settings */\r
4014 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
4015 {\r
4016     POINT arrow[7];\r
4017     double dx, dy, j, k, x, y;\r
4018 \r
4019     if( d_x == s_x ) {\r
4020         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4021 \r
4022         arrow[0].x = s_x + A_WIDTH;\r
4023         arrow[0].y = s_y;\r
4024 \r
4025         arrow[1].x = s_x + A_WIDTH;\r
4026         arrow[1].y = d_y - h;\r
4027 \r
4028         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
4029         arrow[2].y = d_y - h;\r
4030 \r
4031         arrow[3].x = d_x;\r
4032         arrow[3].y = d_y;\r
4033 \r
4034         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
4035         arrow[4].y = d_y - h;\r
4036 \r
4037         arrow[5].x = s_x - A_WIDTH;\r
4038         arrow[5].y = d_y - h;\r
4039 \r
4040         arrow[6].x = s_x - A_WIDTH;\r
4041         arrow[6].y = s_y;\r
4042     }\r
4043     else if( d_y == s_y ) {\r
4044         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4045 \r
4046         arrow[0].x = s_x;\r
4047         arrow[0].y = s_y + A_WIDTH;\r
4048 \r
4049         arrow[1].x = d_x - w;\r
4050         arrow[1].y = s_y + A_WIDTH;\r
4051 \r
4052         arrow[2].x = d_x - w;\r
4053         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
4054 \r
4055         arrow[3].x = d_x;\r
4056         arrow[3].y = d_y;\r
4057 \r
4058         arrow[4].x = d_x - w;\r
4059         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
4060 \r
4061         arrow[5].x = d_x - w;\r
4062         arrow[5].y = s_y - A_WIDTH;\r
4063 \r
4064         arrow[6].x = s_x;\r
4065         arrow[6].y = s_y - A_WIDTH;\r
4066     }\r
4067     else {\r
4068         /* [AS] Needed a lot of paper for this! :-) */\r
4069         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4070         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4071   \r
4072         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4073 \r
4074         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4075 \r
4076         x = s_x;\r
4077         y = s_y;\r
4078 \r
4079         arrow[0].x = Round(x - j);\r
4080         arrow[0].y = Round(y + j*dx);\r
4081 \r
4082         arrow[1].x = Round(x + j);\r
4083         arrow[1].y = Round(y - j*dx);\r
4084 \r
4085         if( d_x > s_x ) {\r
4086             x = (double) d_x - k;\r
4087             y = (double) d_y - k*dy;\r
4088         }\r
4089         else {\r
4090             x = (double) d_x + k;\r
4091             y = (double) d_y + k*dy;\r
4092         }\r
4093 \r
4094         arrow[2].x = Round(x + j);\r
4095         arrow[2].y = Round(y - j*dx);\r
4096 \r
4097         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4098         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4099 \r
4100         arrow[4].x = d_x;\r
4101         arrow[4].y = d_y;\r
4102 \r
4103         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4104         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4105 \r
4106         arrow[6].x = Round(x - j);\r
4107         arrow[6].y = Round(y + j*dx);\r
4108     }\r
4109 \r
4110     Polygon( hdc, arrow, 7 );\r
4111 }\r
4112 \r
4113 /* [AS] Draw an arrow between two squares */\r
4114 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4115 {\r
4116     int s_x, s_y, d_x, d_y;\r
4117     HPEN hpen;\r
4118     HPEN holdpen;\r
4119     HBRUSH hbrush;\r
4120     HBRUSH holdbrush;\r
4121     LOGBRUSH stLB;\r
4122 \r
4123     if( s_col == d_col && s_row == d_row ) {\r
4124         return;\r
4125     }\r
4126 \r
4127     /* Get source and destination points */\r
4128     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4129     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4130 \r
4131     if( d_y > s_y ) {\r
4132         d_y += squareSize / 4;\r
4133     }\r
4134     else if( d_y < s_y ) {\r
4135         d_y += 3 * squareSize / 4;\r
4136     }\r
4137     else {\r
4138         d_y += squareSize / 2;\r
4139     }\r
4140 \r
4141     if( d_x > s_x ) {\r
4142         d_x += squareSize / 4;\r
4143     }\r
4144     else if( d_x < s_x ) {\r
4145         d_x += 3 * squareSize / 4;\r
4146     }\r
4147     else {\r
4148         d_x += squareSize / 2;\r
4149     }\r
4150 \r
4151     s_x += squareSize / 2;\r
4152     s_y += squareSize / 2;\r
4153 \r
4154     /* Adjust width */\r
4155     A_WIDTH = squareSize / 14;\r
4156 \r
4157     /* Draw */\r
4158     stLB.lbStyle = BS_SOLID;\r
4159     stLB.lbColor = appData.highlightArrowColor;\r
4160     stLB.lbHatch = 0;\r
4161 \r
4162     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4163     holdpen = SelectObject( hdc, hpen );\r
4164     hbrush = CreateBrushIndirect( &stLB );\r
4165     holdbrush = SelectObject( hdc, hbrush );\r
4166 \r
4167     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4168 \r
4169     SelectObject( hdc, holdpen );\r
4170     SelectObject( hdc, holdbrush );\r
4171     DeleteObject( hpen );\r
4172     DeleteObject( hbrush );\r
4173 }\r
4174 \r
4175 BOOL HasHighlightInfo()\r
4176 {\r
4177     BOOL result = FALSE;\r
4178 \r
4179     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4180         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4181     {\r
4182         result = TRUE;\r
4183     }\r
4184 \r
4185     return result;\r
4186 }\r
4187 \r
4188 BOOL IsDrawArrowEnabled()\r
4189 {\r
4190     BOOL result = FALSE;\r
4191 \r
4192     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4193         result = TRUE;\r
4194     }\r
4195 \r
4196     return result;\r
4197 }\r
4198 \r
4199 VOID DrawArrowHighlight( HDC hdc )\r
4200 {\r
4201     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4202         DrawArrowBetweenSquares( hdc,\r
4203             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4204             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4205     }\r
4206 }\r
4207 \r
4208 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4209 {\r
4210     HRGN result = NULL;\r
4211 \r
4212     if( HasHighlightInfo() ) {\r
4213         int x1, y1, x2, y2;\r
4214         int sx, sy, dx, dy;\r
4215 \r
4216         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4217         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4218 \r
4219         sx = MIN( x1, x2 );\r
4220         sy = MIN( y1, y2 );\r
4221         dx = MAX( x1, x2 ) + squareSize;\r
4222         dy = MAX( y1, y2 ) + squareSize;\r
4223 \r
4224         result = CreateRectRgn( sx, sy, dx, dy );\r
4225     }\r
4226 \r
4227     return result;\r
4228 }\r
4229 \r
4230 /*\r
4231     Warning: this function modifies the behavior of several other functions. \r
4232     \r
4233     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4234     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4235     repaint is scattered all over the place, which is not good for features such as\r
4236     "arrow highlighting" that require a full repaint of the board.\r
4237 \r
4238     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4239     user interaction, when speed is not so important) but especially to avoid errors\r
4240     in the displayed graphics.\r
4241 \r
4242     In such patched places, I always try refer to this function so there is a single\r
4243     place to maintain knowledge.\r
4244     \r
4245     To restore the original behavior, just return FALSE unconditionally.\r
4246 */\r
4247 BOOL IsFullRepaintPreferrable()\r
4248 {\r
4249     BOOL result = FALSE;\r
4250 \r
4251     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4252         /* Arrow may appear on the board */\r
4253         result = TRUE;\r
4254     }\r
4255 \r
4256     return result;\r
4257 }\r
4258 \r
4259 /* \r
4260     This function is called by DrawPosition to know whether a full repaint must\r
4261     be forced or not.\r
4262 \r
4263     Only DrawPosition may directly call this function, which makes use of \r
4264     some state information. Other function should call DrawPosition specifying \r
4265     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4266 */\r
4267 BOOL DrawPositionNeedsFullRepaint()\r
4268 {\r
4269     BOOL result = FALSE;\r
4270 \r
4271     /* \r
4272         Probably a slightly better policy would be to trigger a full repaint\r
4273         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4274         but animation is fast enough that it's difficult to notice.\r
4275     */\r
4276     if( animInfo.piece == EmptySquare ) {\r
4277         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4278             result = TRUE;\r
4279         }\r
4280     }\r
4281 \r
4282     return result;\r
4283 }\r
4284 \r
4285 VOID\r
4286 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4287 {\r
4288   int row, column, x, y, square_color, piece_color;\r
4289   ChessSquare piece;\r
4290   HBRUSH oldBrush;\r
4291   HDC texture_hdc = NULL;\r
4292 \r
4293   /* [AS] Initialize background textures if needed */\r
4294   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4295       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4296       if( backTextureSquareSize != squareSize \r
4297        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4298           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4299           backTextureSquareSize = squareSize;\r
4300           RebuildTextureSquareInfo();\r
4301       }\r
4302 \r
4303       texture_hdc = CreateCompatibleDC( hdc );\r
4304   }\r
4305 \r
4306   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4307     for (column = 0; column < BOARD_WIDTH; column++) {\r
4308   \r
4309       SquareToPos(row, column, &x, &y);\r
4310 \r
4311       piece = board[row][column];\r
4312 \r
4313       square_color = ((column + row) % 2) == 1;\r
4314       if( gameInfo.variant == VariantXiangqi ) {\r
4315           square_color = !InPalace(row, column);\r
4316           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4317           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4318       }\r
4319       piece_color = (int) piece < (int) BlackPawn;\r
4320 \r
4321 \r
4322       /* [HGM] holdings file: light square or black */\r
4323       if(column == BOARD_LEFT-2) {\r
4324             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4325                 square_color = 1;\r
4326             else {\r
4327                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4328                 continue;\r
4329             }\r
4330       } else\r
4331       if(column == BOARD_RGHT + 1 ) {\r
4332             if( row < gameInfo.holdingsSize )\r
4333                 square_color = 1;\r
4334             else {\r
4335                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4336                 continue;\r
4337             }\r
4338       }\r
4339       if(column == BOARD_LEFT-1 ) /* left align */\r
4340             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4341       else if( column == BOARD_RGHT) /* right align */\r
4342             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4343       else\r
4344       if (appData.monoMode) {\r
4345         if (piece == EmptySquare) {\r
4346           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4347                  square_color ? WHITENESS : BLACKNESS);\r
4348         } else {\r
4349           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4350         }\r
4351       } \r
4352       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4353           /* [AS] Draw the square using a texture bitmap */\r
4354           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4355           int r = row, c = column; // [HGM] do not flip board in flipView\r
4356           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4357 \r
4358           DrawTile( x, y, \r
4359               squareSize, squareSize, \r
4360               hdc, \r
4361               texture_hdc,\r
4362               backTextureSquareInfo[r][c].mode,\r
4363               backTextureSquareInfo[r][c].x,\r
4364               backTextureSquareInfo[r][c].y );\r
4365 \r
4366           SelectObject( texture_hdc, hbm );\r
4367 \r
4368           if (piece != EmptySquare) {\r
4369               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4370           }\r
4371       }\r
4372       else {\r
4373         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4374 \r
4375         oldBrush = SelectObject(hdc, brush );\r
4376         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4377         SelectObject(hdc, oldBrush);\r
4378         if (piece != EmptySquare)\r
4379           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4380       }\r
4381     }\r
4382   }\r
4383 \r
4384   if( texture_hdc != NULL ) {\r
4385     DeleteDC( texture_hdc );\r
4386   }\r
4387 }\r
4388 \r
4389 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4390 void fputDW(FILE *f, int x)\r
4391 {\r
4392         fputc(x     & 255, f);\r
4393         fputc(x>>8  & 255, f);\r
4394         fputc(x>>16 & 255, f);\r
4395         fputc(x>>24 & 255, f);\r
4396 }\r
4397 \r
4398 #define MAX_CLIPS 200   /* more than enough */\r
4399 \r
4400 VOID\r
4401 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4402 {\r
4403 //  HBITMAP bufferBitmap;\r
4404   BITMAP bi;\r
4405 //  RECT Rect;\r
4406   HDC tmphdc;\r
4407   HBITMAP hbm;\r
4408   int w = 100, h = 50;\r
4409 \r
4410   if(logo == NULL) return;\r
4411 //  GetClientRect(hwndMain, &Rect);\r
4412 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4413 //                                      Rect.bottom-Rect.top+1);\r
4414   tmphdc = CreateCompatibleDC(hdc);\r
4415   hbm = SelectObject(tmphdc, logo);\r
4416   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4417             w = bi.bmWidth;\r
4418             h = bi.bmHeight;\r
4419   }\r
4420   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4421                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4422   SelectObject(tmphdc, hbm);\r
4423   DeleteDC(tmphdc);\r
4424 }\r
4425 \r
4426 VOID\r
4427 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4428 {\r
4429   static Board lastReq, lastDrawn;\r
4430   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4431   static int lastDrawnFlipView = 0;\r
4432   static int lastReqValid = 0, lastDrawnValid = 0;\r
4433   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4434   HDC tmphdc;\r
4435   HDC hdcmem;\r
4436   HBITMAP bufferBitmap;\r
4437   HBITMAP oldBitmap;\r
4438   RECT Rect;\r
4439   HRGN clips[MAX_CLIPS];\r
4440   ChessSquare dragged_piece = EmptySquare;\r
4441 \r
4442   /* I'm undecided on this - this function figures out whether a full\r
4443    * repaint is necessary on its own, so there's no real reason to have the\r
4444    * caller tell it that.  I think this can safely be set to FALSE - but\r
4445    * if we trust the callers not to request full repaints unnessesarily, then\r
4446    * we could skip some clipping work.  In other words, only request a full\r
4447    * redraw when the majority of pieces have changed positions (ie. flip, \r
4448    * gamestart and similar)  --Hawk\r
4449    */\r
4450   Boolean fullrepaint = repaint;\r
4451 \r
4452   if( DrawPositionNeedsFullRepaint() ) {\r
4453       fullrepaint = TRUE;\r
4454   }\r
4455 \r
4456 #if 0\r
4457   if( fullrepaint ) {\r
4458       static int repaint_count = 0;\r
4459       char buf[128];\r
4460 \r
4461       repaint_count++;\r
4462       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4463       OutputDebugString( buf );\r
4464   }\r
4465 #endif\r
4466 \r
4467   if (board == NULL) {\r
4468     if (!lastReqValid) {\r
4469       return;\r
4470     }\r
4471     board = lastReq;\r
4472   } else {\r
4473     CopyBoard(lastReq, board);\r
4474     lastReqValid = 1;\r
4475   }\r
4476 \r
4477   if (doingSizing) {\r
4478     return;\r
4479   }\r
4480 \r
4481   if (IsIconic(hwndMain)) {\r
4482     return;\r
4483   }\r
4484 \r
4485   if (hdc == NULL) {\r
4486     hdc = GetDC(hwndMain);\r
4487     if (!appData.monoMode) {\r
4488       SelectPalette(hdc, hPal, FALSE);\r
4489       RealizePalette(hdc);\r
4490     }\r
4491     releaseDC = TRUE;\r
4492   } else {\r
4493     releaseDC = FALSE;\r
4494   }\r
4495 \r
4496 #if 0\r
4497   fprintf(debugFP, "*******************************\n"\r
4498                    "repaint = %s\n"\r
4499                    "dragInfo.from (%d,%d)\n"\r
4500                    "dragInfo.start (%d,%d)\n"\r
4501                    "dragInfo.pos (%d,%d)\n"\r
4502                    "dragInfo.lastpos (%d,%d)\n", \r
4503                     repaint ? "TRUE" : "FALSE",\r
4504                     dragInfo.from.x, dragInfo.from.y, \r
4505                     dragInfo.start.x, dragInfo.start.y,\r
4506                     dragInfo.pos.x, dragInfo.pos.y,\r
4507                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4508   fprintf(debugFP, "prev:  ");\r
4509   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4510     for (column = 0; column < BOARD_WIDTH; column++) {\r
4511       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4512     }\r
4513   }\r
4514   fprintf(debugFP, "\n");\r
4515   fprintf(debugFP, "board: ");\r
4516   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4517     for (column = 0; column < BOARD_WIDTH; column++) {\r
4518       fprintf(debugFP, "%d ", board[row][column]);\r
4519     }\r
4520   }\r
4521   fprintf(debugFP, "\n");\r
4522   fflush(debugFP);\r
4523 #endif\r
4524 \r
4525   /* Create some work-DCs */\r
4526   hdcmem = CreateCompatibleDC(hdc);\r
4527   tmphdc = CreateCompatibleDC(hdc);\r
4528 \r
4529   /* If dragging is in progress, we temporarely remove the piece */\r
4530   /* [HGM] or temporarily decrease count if stacked              */\r
4531   /*       !! Moved to before board compare !!                   */\r
4532   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4533     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4534     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4535             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4536         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4537     } else \r
4538     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4539             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4540         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4541     } else \r
4542         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4543   }\r
4544 \r
4545   /* Figure out which squares need updating by comparing the \r
4546    * newest board with the last drawn board and checking if\r
4547    * flipping has changed.\r
4548    */\r
4549   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4550     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4551       for (column = 0; column < BOARD_WIDTH; column++) {\r
4552         if (lastDrawn[row][column] != board[row][column]) {\r
4553           SquareToPos(row, column, &x, &y);\r
4554           clips[num_clips++] =\r
4555             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4556         }\r
4557       }\r
4558     }\r
4559     for (i=0; i<2; i++) {\r
4560       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4561           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4562         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4563             lastDrawnHighlight.sq[i].y >= 0) {\r
4564           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4565                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4566           clips[num_clips++] =\r
4567             CreateRectRgn(x - lineGap, y - lineGap, \r
4568                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4569         }\r
4570         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4571           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4572           clips[num_clips++] =\r
4573             CreateRectRgn(x - lineGap, y - lineGap, \r
4574                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4575         }\r
4576       }\r
4577     }\r
4578     for (i=0; i<2; i++) {\r
4579       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4580           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4581         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4582             lastDrawnPremove.sq[i].y >= 0) {\r
4583           SquareToPos(lastDrawnPremove.sq[i].y,\r
4584                       lastDrawnPremove.sq[i].x, &x, &y);\r
4585           clips[num_clips++] =\r
4586             CreateRectRgn(x - lineGap, y - lineGap, \r
4587                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4588         }\r
4589         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4590             premoveHighlightInfo.sq[i].y >= 0) {\r
4591           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4592                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4593           clips[num_clips++] =\r
4594             CreateRectRgn(x - lineGap, y - lineGap, \r
4595                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4596         }\r
4597       }\r
4598     }\r
4599   } else {\r
4600     fullrepaint = TRUE;\r
4601   }\r
4602 \r
4603   /* Create a buffer bitmap - this is the actual bitmap\r
4604    * being written to.  When all the work is done, we can\r
4605    * copy it to the real DC (the screen).  This avoids\r
4606    * the problems with flickering.\r
4607    */\r
4608   GetClientRect(hwndMain, &Rect);\r
4609   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4610                                         Rect.bottom-Rect.top+1);\r
4611   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4612   if (!appData.monoMode) {\r
4613     SelectPalette(hdcmem, hPal, FALSE);\r
4614   }\r
4615 \r
4616   /* Create clips for dragging */\r
4617   if (!fullrepaint) {\r
4618     if (dragInfo.from.x >= 0) {\r
4619       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4620       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4621     }\r
4622     if (dragInfo.start.x >= 0) {\r
4623       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4624       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4625     }\r
4626     if (dragInfo.pos.x >= 0) {\r
4627       x = dragInfo.pos.x - squareSize / 2;\r
4628       y = dragInfo.pos.y - squareSize / 2;\r
4629       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4630     }\r
4631     if (dragInfo.lastpos.x >= 0) {\r
4632       x = dragInfo.lastpos.x - squareSize / 2;\r
4633       y = dragInfo.lastpos.y - squareSize / 2;\r
4634       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4635     }\r
4636   }\r
4637 \r
4638   /* Are we animating a move?  \r
4639    * If so, \r
4640    *   - remove the piece from the board (temporarely)\r
4641    *   - calculate the clipping region\r
4642    */\r
4643   if (!fullrepaint) {\r
4644     if (animInfo.piece != EmptySquare) {\r
4645       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4646       x = boardRect.left + animInfo.lastpos.x;\r
4647       y = boardRect.top + animInfo.lastpos.y;\r
4648       x2 = boardRect.left + animInfo.pos.x;\r
4649       y2 = boardRect.top + animInfo.pos.y;\r
4650       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4651       /* Slight kludge.  The real problem is that after AnimateMove is\r
4652          done, the position on the screen does not match lastDrawn.\r
4653          This currently causes trouble only on e.p. captures in\r
4654          atomic, where the piece moves to an empty square and then\r
4655          explodes.  The old and new positions both had an empty square\r
4656          at the destination, but animation has drawn a piece there and\r
4657          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4658       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4659     }\r
4660   }\r
4661 \r
4662   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4663   if (num_clips == 0)\r
4664     fullrepaint = TRUE;\r
4665 \r
4666   /* Set clipping on the memory DC */\r
4667   if (!fullrepaint) {\r
4668     SelectClipRgn(hdcmem, clips[0]);\r
4669     for (x = 1; x < num_clips; x++) {\r
4670       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4671         abort();  // this should never ever happen!\r
4672     }\r
4673   }\r
4674 \r
4675   /* Do all the drawing to the memory DC */\r
4676   if(explodeInfo.radius) { // [HGM] atomic\r
4677         HBRUSH oldBrush;\r
4678         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4679         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4680         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4681         x += squareSize/2;\r
4682         y += squareSize/2;\r
4683         if(!fullrepaint) {\r
4684           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4685           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4686         }\r
4687         DrawGridOnDC(hdcmem);\r
4688         DrawHighlightsOnDC(hdcmem);\r
4689         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4690         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4691         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4692         SelectObject(hdcmem, oldBrush);\r
4693   } else {\r
4694     DrawGridOnDC(hdcmem);\r
4695     DrawHighlightsOnDC(hdcmem);\r
4696     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4697   }\r
4698   if(logoHeight) {\r
4699         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4700         if(appData.autoLogo) {\r
4701           \r
4702           switch(gameMode) { // pick logos based on game mode\r
4703             case IcsObserving:\r
4704                 whiteLogo = second.programLogo; // ICS logo\r
4705                 blackLogo = second.programLogo;\r
4706             default:\r
4707                 break;\r
4708             case IcsPlayingWhite:\r
4709                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4710                 blackLogo = second.programLogo; // ICS logo\r
4711                 break;\r
4712             case IcsPlayingBlack:\r
4713                 whiteLogo = second.programLogo; // ICS logo\r
4714                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4715                 break;\r
4716             case TwoMachinesPlay:\r
4717                 if(first.twoMachinesColor[0] == 'b') {\r
4718                     whiteLogo = second.programLogo;\r
4719                     blackLogo = first.programLogo;\r
4720                 }\r
4721                 break;\r
4722             case MachinePlaysWhite:\r
4723                 blackLogo = userLogo;\r
4724                 break;\r
4725             case MachinePlaysBlack:\r
4726                 whiteLogo = userLogo;\r
4727                 blackLogo = first.programLogo;\r
4728           }\r
4729         }\r
4730         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4731         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4732   }\r
4733 \r
4734   if( appData.highlightMoveWithArrow ) {\r
4735     DrawArrowHighlight(hdcmem);\r
4736   }\r
4737 \r
4738   DrawCoordsOnDC(hdcmem);\r
4739 \r
4740   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4741                  /* to make sure lastDrawn contains what is actually drawn */\r
4742 \r
4743   /* Put the dragged piece back into place and draw it (out of place!) */\r
4744     if (dragged_piece != EmptySquare) {\r
4745     /* [HGM] or restack */\r
4746     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4747                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4748     else\r
4749     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4750                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4751     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4752     x = dragInfo.pos.x - squareSize / 2;\r
4753     y = dragInfo.pos.y - squareSize / 2;\r
4754     DrawPieceOnDC(hdcmem, dragged_piece,\r
4755                   ((int) dragged_piece < (int) BlackPawn), \r
4756                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4757   }   \r
4758   \r
4759   /* Put the animated piece back into place and draw it */\r
4760   if (animInfo.piece != EmptySquare) {\r
4761     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4762     x = boardRect.left + animInfo.pos.x;\r
4763     y = boardRect.top + animInfo.pos.y;\r
4764     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4765                   ((int) animInfo.piece < (int) BlackPawn),\r
4766                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4767   }\r
4768 \r
4769   /* Release the bufferBitmap by selecting in the old bitmap \r
4770    * and delete the memory DC\r
4771    */\r
4772   SelectObject(hdcmem, oldBitmap);\r
4773   DeleteDC(hdcmem);\r
4774 \r
4775   /* Set clipping on the target DC */\r
4776   if (!fullrepaint) {\r
4777     SelectClipRgn(hdc, clips[0]);\r
4778     for (x = 1; x < num_clips; x++) {\r
4779       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4780         abort();   // this should never ever happen!\r
4781     } \r
4782   }\r
4783 \r
4784   /* Copy the new bitmap onto the screen in one go.\r
4785    * This way we avoid any flickering\r
4786    */\r
4787   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4788   BitBlt(hdc, boardRect.left, boardRect.top,\r
4789          boardRect.right - boardRect.left,\r
4790          boardRect.bottom - boardRect.top,\r
4791          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4792   if(saveDiagFlag) { \r
4793     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4794     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4795 \r
4796     GetObject(bufferBitmap, sizeof(b), &b);\r
4797     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4798         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4799         bih.biWidth = b.bmWidth;\r
4800         bih.biHeight = b.bmHeight;\r
4801         bih.biPlanes = 1;\r
4802         bih.biBitCount = b.bmBitsPixel;\r
4803         bih.biCompression = 0;\r
4804         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4805         bih.biXPelsPerMeter = 0;\r
4806         bih.biYPelsPerMeter = 0;\r
4807         bih.biClrUsed = 0;\r
4808         bih.biClrImportant = 0;\r
4809 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4810 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4811         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4812 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4813 \r
4814 #if 1\r
4815         wb = b.bmWidthBytes;\r
4816         // count colors\r
4817         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4818                 int k = ((int*) pData)[i];\r
4819                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4820                 if(j >= 16) break;\r
4821                 color[j] = k;\r
4822                 if(j >= nrColors) nrColors = j+1;\r
4823         }\r
4824         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4825                 INT p = 0;\r
4826                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4827                     for(w=0; w<(wb>>2); w+=2) {\r
4828                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4829                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4830                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4831                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4832                         pData[p++] = m | j<<4;\r
4833                     }\r
4834                     while(p&3) pData[p++] = 0;\r
4835                 }\r
4836                 fac = 3;\r
4837                 wb = ((wb+31)>>5)<<2;\r
4838         }\r
4839         // write BITMAPFILEHEADER\r
4840         fprintf(diagFile, "BM");\r
4841         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4842         fputDW(diagFile, 0);\r
4843         fputDW(diagFile, 0x36 + (fac?64:0));\r
4844         // write BITMAPINFOHEADER\r
4845         fputDW(diagFile, 40);\r
4846         fputDW(diagFile, b.bmWidth);\r
4847         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4848         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4849         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4850         fputDW(diagFile, 0);\r
4851         fputDW(diagFile, 0);\r
4852         fputDW(diagFile, 0);\r
4853         fputDW(diagFile, 0);\r
4854         fputDW(diagFile, 0);\r
4855         fputDW(diagFile, 0);\r
4856         // write color table\r
4857         if(fac)\r
4858         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4859         // write bitmap data\r
4860         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4861                 fputc(pData[i], diagFile);\r
4862 #endif\r
4863      }\r
4864   }\r
4865 \r
4866   SelectObject(tmphdc, oldBitmap);\r
4867 \r
4868   /* Massive cleanup */\r
4869   for (x = 0; x < num_clips; x++)\r
4870     DeleteObject(clips[x]);\r
4871 \r
4872   DeleteDC(tmphdc);\r
4873   DeleteObject(bufferBitmap);\r
4874 \r
4875   if (releaseDC) \r
4876     ReleaseDC(hwndMain, hdc);\r
4877   \r
4878   if (lastDrawnFlipView != flipView) {\r
4879     if (flipView)\r
4880       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4881     else\r
4882       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4883   }\r
4884 \r
4885 /*  CopyBoard(lastDrawn, board);*/\r
4886   lastDrawnHighlight = highlightInfo;\r
4887   lastDrawnPremove   = premoveHighlightInfo;\r
4888   lastDrawnFlipView = flipView;\r
4889   lastDrawnValid = 1;\r
4890 }\r
4891 \r
4892 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4893 int\r
4894 SaveDiagram(f)\r
4895      FILE *f;\r
4896 {\r
4897     saveDiagFlag = 1; diagFile = f;\r
4898     HDCDrawPosition(NULL, TRUE, NULL);\r
4899 \r
4900     saveDiagFlag = 0;\r
4901 \r
4902 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4903     \r
4904     fclose(f);\r
4905     return TRUE;\r
4906 }\r
4907 \r
4908 \r
4909 /*---------------------------------------------------------------------------*\\r
4910 | CLIENT PAINT PROCEDURE\r
4911 |   This is the main event-handler for the WM_PAINT message.\r
4912 |\r
4913 \*---------------------------------------------------------------------------*/\r
4914 VOID\r
4915 PaintProc(HWND hwnd)\r
4916 {\r
4917   HDC         hdc;\r
4918   PAINTSTRUCT ps;\r
4919   HFONT       oldFont;\r
4920 \r
4921   if((hdc = BeginPaint(hwnd, &ps))) {\r
4922     if (IsIconic(hwnd)) {\r
4923       DrawIcon(hdc, 2, 2, iconCurrent);\r
4924     } else {\r
4925       if (!appData.monoMode) {\r
4926         SelectPalette(hdc, hPal, FALSE);\r
4927         RealizePalette(hdc);\r
4928       }\r
4929       HDCDrawPosition(hdc, 1, NULL);\r
4930       oldFont =\r
4931         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4932       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4933                  ETO_CLIPPED|ETO_OPAQUE,\r
4934                  &messageRect, messageText, strlen(messageText), NULL);\r
4935       SelectObject(hdc, oldFont);\r
4936       DisplayBothClocks();\r
4937     }\r
4938     EndPaint(hwnd,&ps);\r
4939   }\r
4940 \r
4941   return;\r
4942 }\r
4943 \r
4944 \r
4945 /*\r
4946  * If the user selects on a border boundary, return -1; if off the board,\r
4947  *   return -2.  Otherwise map the event coordinate to the square.\r
4948  * The offset boardRect.left or boardRect.top must already have been\r
4949  *   subtracted from x.\r
4950  */\r
4951 int\r
4952 EventToSquare(int x)\r
4953 {\r
4954   if (x <= 0)\r
4955     return -2;\r
4956   if (x < lineGap)\r
4957     return -1;\r
4958   x -= lineGap;\r
4959   if ((x % (squareSize + lineGap)) >= squareSize)\r
4960     return -1;\r
4961   x /= (squareSize + lineGap);\r
4962   if (x >= BOARD_SIZE)\r
4963     return -2;\r
4964   return x;\r
4965 }\r
4966 \r
4967 typedef struct {\r
4968   char piece;\r
4969   int command;\r
4970   char* name;\r
4971 } DropEnable;\r
4972 \r
4973 DropEnable dropEnables[] = {\r
4974   { 'P', DP_Pawn, "Pawn" },\r
4975   { 'N', DP_Knight, "Knight" },\r
4976   { 'B', DP_Bishop, "Bishop" },\r
4977   { 'R', DP_Rook, "Rook" },\r
4978   { 'Q', DP_Queen, "Queen" },\r
4979 };\r
4980 \r
4981 VOID\r
4982 SetupDropMenu(HMENU hmenu)\r
4983 {\r
4984   int i, count, enable;\r
4985   char *p;\r
4986   extern char white_holding[], black_holding[];\r
4987   char item[MSG_SIZ];\r
4988 \r
4989   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4990     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4991                dropEnables[i].piece);\r
4992     count = 0;\r
4993     while (p && *p++ == dropEnables[i].piece) count++;\r
4994     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4995     enable = count > 0 || !appData.testLegality\r
4996       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4997                       && !appData.icsActive);\r
4998     ModifyMenu(hmenu, dropEnables[i].command,\r
4999                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
5000                dropEnables[i].command, item);\r
5001   }\r
5002 }\r
5003 \r
5004 /* Event handler for mouse messages */\r
5005 VOID\r
5006 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5007 {\r
5008   int x, y;\r
5009   POINT pt;\r
5010   static int recursive = 0;\r
5011   HMENU hmenu;\r
5012 //  BOOLEAN needsRedraw = FALSE;\r
5013   BOOLEAN saveAnimate;\r
5014   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
5015   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
5016   ChessMove moveType;\r
5017 \r
5018   if (recursive) {\r
5019     if (message == WM_MBUTTONUP) {\r
5020       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
5021          to the middle button: we simulate pressing the left button too!\r
5022          */\r
5023       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
5024       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
5025     }\r
5026     return;\r
5027   }\r
5028   recursive++;\r
5029   \r
5030   pt.x = LOWORD(lParam);\r
5031   pt.y = HIWORD(lParam);\r
5032   x = EventToSquare(pt.x - boardRect.left);\r
5033   y = EventToSquare(pt.y - boardRect.top);\r
5034   if (!flipView && y >= 0) {\r
5035     y = BOARD_HEIGHT - 1 - y;\r
5036   }\r
5037   if (flipView && x >= 0) {\r
5038     x = BOARD_WIDTH - 1 - x;\r
5039   }\r
5040 \r
5041   switch (message) {\r
5042   case WM_LBUTTONDOWN:\r
5043     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
5044         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
5045         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
5046         if(gameInfo.holdingsWidth && \r
5047                 (WhiteOnMove(currentMove) \r
5048                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
5049                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
5050             // click in right holdings, for determining promotion piece\r
5051             ChessSquare p = boards[currentMove][y][x];\r
5052             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
5053             if(p != EmptySquare) {\r
5054                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
5055                 fromX = fromY = -1;\r
5056                 break;\r
5057             }\r
5058         }\r
5059         DrawPosition(FALSE, boards[currentMove]);\r
5060         break;\r
5061     }\r
5062     ErrorPopDown();\r
5063     sameAgain = FALSE;\r
5064     if (y == -2) {\r
5065       /* Downclick vertically off board; check if on clock */\r
5066       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5067         if (gameMode == EditPosition) {\r
5068           SetWhiteToPlayEvent();\r
5069         } else if (gameMode == IcsPlayingBlack ||\r
5070                    gameMode == MachinePlaysWhite) {\r
5071           CallFlagEvent();\r
5072         } else if (gameMode == EditGame) {\r
5073           AdjustClock(flipClock, -1);\r
5074         }\r
5075       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5076         if (gameMode == EditPosition) {\r
5077           SetBlackToPlayEvent();\r
5078         } else if (gameMode == IcsPlayingWhite ||\r
5079                    gameMode == MachinePlaysBlack) {\r
5080           CallFlagEvent();\r
5081         } else if (gameMode == EditGame) {\r
5082           AdjustClock(!flipClock, -1);\r
5083         }\r
5084       }\r
5085       if (!appData.highlightLastMove) {\r
5086         ClearHighlights();\r
5087         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
5088       }\r
5089       fromX = fromY = -1;\r
5090       dragInfo.start.x = dragInfo.start.y = -1;\r
5091       dragInfo.from = dragInfo.start;\r
5092       break;\r
5093     } else if (x < 0 || y < 0\r
5094       /* [HGM] block clicks between board and holdings */\r
5095               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
5096               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
5097               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
5098         /* EditPosition, empty square, or different color piece;\r
5099            click-click move is possible */\r
5100                                ) {\r
5101       break;\r
5102     } else if (fromX == x && fromY == y) {\r
5103       /* Downclick on same square again */\r
5104       ClearHighlights();\r
5105       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5106       sameAgain = TRUE;  \r
5107     } else if (fromX != -1 &&\r
5108                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5109                                                                         ) {\r
5110       /* Downclick on different square. */\r
5111       /* [HGM] if on holdings file, should count as new first click ! */\r
5112       /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5113         toX = x;\r
5114         toY = y;\r
5115         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5116            to make sure move is legal before showing promotion popup */\r
5117         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5118         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5119                 fromX = fromY = -1; \r
5120                 ClearHighlights();\r
5121                 DrawPosition(FALSE, boards[currentMove]);\r
5122                 break; \r
5123         } else \r
5124         if(moveType != ImpossibleMove) {\r
5125           if(moveType == IllegalMove) {\r
5126                 ;\r
5127           } else\r
5128           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5129           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5130             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5131               appData.alwaysPromoteToQueen)) {\r
5132                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5133                   if (!appData.highlightLastMove) {\r
5134                       ClearHighlights();\r
5135                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5136                   }\r
5137           } else\r
5138           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5139                   SetHighlights(fromX, fromY, toX, toY);\r
5140                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5141                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5142                      If promotion to Q is legal, all are legal! */\r
5143                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5144                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5145                     // kludge to temporarily execute move on display, without promoting yet\r
5146                     promotionChoice = TRUE;\r
5147                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5148                     boards[currentMove][toY][toX] = p;\r
5149                     DrawPosition(FALSE, boards[currentMove]);\r
5150                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5151                     boards[currentMove][toY][toX] = q;\r
5152                   } else\r
5153                   PromotionPopup(hwnd);\r
5154           } else {       /* not a promotion */\r
5155              if (appData.animate || appData.highlightLastMove) {\r
5156                  SetHighlights(fromX, fromY, toX, toY);\r
5157              } else {\r
5158                  ClearHighlights();\r
5159              }\r
5160              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5161              if (appData.animate && !appData.highlightLastMove) {\r
5162                   ClearHighlights();\r
5163                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5164              }\r
5165           }\r
5166           fromX = fromY = -1;\r
5167           break;\r
5168         }\r
5169         if (gotPremove) {\r
5170             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5171             DrawPosition(forceFullRepaint || FALSE, NULL);\r
5172         } else ClearHighlights();\r
5173         fromX = fromY = -1;\r
5174         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5175     }\r
5176     /* First downclick, or restart on a square with same color piece */\r
5177     if (!frozen && OKToStartUserMove(x, y)) {\r
5178       fromX = x;\r
5179       fromY = y;\r
5180       dragInfo.lastpos = pt;\r
5181       dragInfo.from.x = fromX;\r
5182       dragInfo.from.y = fromY;\r
5183       dragInfo.start = dragInfo.from;\r
5184       SetCapture(hwndMain);\r
5185     } else {\r
5186       fromX = fromY = -1;\r
5187       dragInfo.start.x = dragInfo.start.y = -1;\r
5188       dragInfo.from = dragInfo.start;\r
5189       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5190     }\r
5191     break;\r
5192 \r
5193   case WM_LBUTTONUP:\r
5194     ReleaseCapture();\r
5195     if (fromX == -1) break;\r
5196     if (x == fromX && y == fromY) {\r
5197       dragInfo.from.x = dragInfo.from.y = -1;\r
5198       /* Upclick on same square */\r
5199       if (sameAgain) {\r
5200         /* Clicked same square twice: abort click-click move */\r
5201         fromX = fromY = -1;\r
5202         gotPremove = 0;\r
5203         ClearPremoveHighlights();\r
5204       } else {\r
5205         /* First square clicked: start click-click move */\r
5206         SetHighlights(fromX, fromY, -1, -1);\r
5207       }\r
5208       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5209     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5210       /* Errant click; ignore */\r
5211       break;\r
5212     } else {\r
5213       /* Finish drag move. */\r
5214     if (appData.debugMode) {\r
5215         fprintf(debugFP, "release\n");\r
5216     }\r
5217       dragInfo.from.x = dragInfo.from.y = -1;\r
5218       toX = x;\r
5219       toY = y;\r
5220       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5221       appData.animate = appData.animate && !appData.animateDragging;\r
5222       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5223       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5224                 fromX = fromY = -1; \r
5225                 ClearHighlights();\r
5226                 DrawPosition(FALSE, boards[currentMove]);\r
5227                 appData.animate = saveAnimate;\r
5228                 break; \r
5229       } else \r
5230       if(moveType != ImpossibleMove) {\r
5231           /* [HGM] use move type to determine if move is promotion.\r
5232              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5233           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5234             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5235               appData.alwaysPromoteToQueen)) \r
5236                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5237           else \r
5238           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5239                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5240                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5241                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5242                     // kludge to temporarily execute move on display, wthout promotng yet\r
5243                     promotionChoice = TRUE;\r
5244                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5245                     boards[currentMove][toY][toX] = p;\r
5246                     DrawPosition(FALSE, boards[currentMove]);\r
5247                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5248                     boards[currentMove][toY][toX] = q;\r
5249                     appData.animate = saveAnimate;\r
5250                     break;\r
5251                   } else\r
5252                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5253           } else {\r
5254             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5255                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5256                                         moveType == WhiteCapturesEnPassant || \r
5257                                         moveType == BlackCapturesEnPassant   ) )\r
5258                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5259             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5260           }\r
5261       }\r
5262       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5263       appData.animate = saveAnimate;\r
5264       fromX = fromY = -1;\r
5265       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5266         ClearHighlights();\r
5267       }\r
5268       if (appData.animate || appData.animateDragging ||\r
5269           appData.highlightDragging || gotPremove) {\r
5270         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5271       }\r
5272     }\r
5273     dragInfo.start.x = dragInfo.start.y = -1; \r
5274     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5275     break;\r
5276 \r
5277   case WM_MOUSEMOVE:\r
5278     if ((appData.animateDragging || appData.highlightDragging)\r
5279         && (wParam & MK_LBUTTON)\r
5280         && dragInfo.from.x >= 0) \r
5281     {\r
5282       BOOL full_repaint = FALSE;\r
5283 \r
5284       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5285       if (appData.animateDragging) {\r
5286         dragInfo.pos = pt;\r
5287       }\r
5288       if (appData.highlightDragging) {\r
5289         SetHighlights(fromX, fromY, x, y);\r
5290         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5291             full_repaint = TRUE;\r
5292         }\r
5293       }\r
5294       \r
5295       DrawPosition( full_repaint, NULL);\r
5296       \r
5297       dragInfo.lastpos = dragInfo.pos;\r
5298     }\r
5299     break;\r
5300 \r
5301   case WM_MOUSEWHEEL: // [DM]\r
5302     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5303        /* Mouse Wheel is being rolled forward\r
5304         * Play moves forward\r
5305         */\r
5306        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5307                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5308        /* Mouse Wheel is being rolled backward\r
5309         * Play moves backward\r
5310         */\r
5311        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5312                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5313     }\r
5314     break;\r
5315 \r
5316   case WM_MBUTTONDOWN:\r
5317   case WM_RBUTTONDOWN:\r
5318     ErrorPopDown();\r
5319     ReleaseCapture();\r
5320     fromX = fromY = -1;\r
5321     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5322     dragInfo.start.x = dragInfo.start.y = -1;\r
5323     dragInfo.from = dragInfo.start;\r
5324     dragInfo.lastpos = dragInfo.pos;\r
5325     if (appData.highlightDragging) {\r
5326       ClearHighlights();\r
5327     }\r
5328     if(y == -2) {\r
5329       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5330       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5331           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5332       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5333           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5334       }\r
5335     }\r
5336     DrawPosition(TRUE, NULL);\r
5337 \r
5338     switch (gameMode) {\r
5339     case EditPosition:\r
5340     case IcsExamining:\r
5341       if (x < 0 || y < 0) break;\r
5342       fromX = x;\r
5343       fromY = y;\r
5344       if (message == WM_MBUTTONDOWN) {\r
5345         buttonCount = 3;  /* even if system didn't think so */\r
5346         if (wParam & MK_SHIFT) \r
5347           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5348         else\r
5349           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5350       } else { /* message == WM_RBUTTONDOWN */\r
5351 #if 0\r
5352         if (buttonCount == 3) {\r
5353           if (wParam & MK_SHIFT) \r
5354             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5355           else\r
5356             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5357         } else {\r
5358           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5359         }\r
5360 #else\r
5361         /* Just have one menu, on the right button.  Windows users don't\r
5362            think to try the middle one, and sometimes other software steals\r
5363            it, or it doesn't really exist. */\r
5364         if(gameInfo.variant != VariantShogi)\r
5365             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5366         else\r
5367             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5368 #endif\r
5369       }\r
5370       break;\r
5371     case IcsPlayingWhite:\r
5372     case IcsPlayingBlack:\r
5373     case EditGame:\r
5374     case MachinePlaysWhite:\r
5375     case MachinePlaysBlack:\r
5376       if (appData.testLegality &&\r
5377           gameInfo.variant != VariantBughouse &&\r
5378           gameInfo.variant != VariantCrazyhouse) break;\r
5379       if (x < 0 || y < 0) break;\r
5380       fromX = x;\r
5381       fromY = y;\r
5382       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5383       SetupDropMenu(hmenu);\r
5384       MenuPopup(hwnd, pt, hmenu, -1);\r
5385       break;\r
5386     default:\r
5387       break;\r
5388     }\r
5389     break;\r
5390   }\r
5391 \r
5392   recursive--;\r
5393 }\r
5394 \r
5395 /* Preprocess messages for buttons in main window */\r
5396 LRESULT CALLBACK\r
5397 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5398 {\r
5399   int id = GetWindowLong(hwnd, GWL_ID);\r
5400   int i, dir;\r
5401 \r
5402   for (i=0; i<N_BUTTONS; i++) {\r
5403     if (buttonDesc[i].id == id) break;\r
5404   }\r
5405   if (i == N_BUTTONS) return 0;\r
5406   switch (message) {\r
5407   case WM_KEYDOWN:\r
5408     switch (wParam) {\r
5409     case VK_LEFT:\r
5410     case VK_RIGHT:\r
5411       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5412       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5413       return TRUE;\r
5414     }\r
5415     break;\r
5416   case WM_CHAR:\r
5417     switch (wParam) {\r
5418     case '\r':\r
5419       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5420       return TRUE;\r
5421     default:\r
5422       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5423         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5424         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5425         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5426         SetFocus(h);\r
5427         SendMessage(h, WM_CHAR, wParam, lParam);\r
5428         return TRUE;\r
5429       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5430         PopUpMoveDialog((char)wParam);\r
5431       }\r
5432       break;\r
5433     }\r
5434     break;\r
5435   }\r
5436   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5437 }\r
5438 \r
5439 /* Process messages for Promotion dialog box */\r
5440 LRESULT CALLBACK\r
5441 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5442 {\r
5443   char promoChar;\r
5444 \r
5445   switch (message) {\r
5446   case WM_INITDIALOG: /* message: initialize dialog box */\r
5447     /* Center the dialog over the application window */\r
5448     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5449     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5450       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5451        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5452                SW_SHOW : SW_HIDE);\r
5453     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5454     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5455        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5456          PieceToChar(WhiteAngel) != '~') ||\r
5457         (PieceToChar(BlackAngel) >= 'A' &&\r
5458          PieceToChar(BlackAngel) != '~')   ) ?\r
5459                SW_SHOW : SW_HIDE);\r
5460     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5461        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5462          PieceToChar(WhiteMarshall) != '~') ||\r
5463         (PieceToChar(BlackMarshall) >= 'A' &&\r
5464          PieceToChar(BlackMarshall) != '~')   ) ?\r
5465                SW_SHOW : SW_HIDE);\r
5466     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5467     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5468        gameInfo.variant != VariantShogi ?\r
5469                SW_SHOW : SW_HIDE);\r
5470     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5471        gameInfo.variant != VariantShogi ?\r
5472                SW_SHOW : SW_HIDE);\r
5473     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5474        gameInfo.variant == VariantShogi ?\r
5475                SW_SHOW : SW_HIDE);\r
5476     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5477        gameInfo.variant == VariantShogi ?\r
5478                SW_SHOW : SW_HIDE);\r
5479     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5480        gameInfo.variant == VariantSuper ?\r
5481                SW_SHOW : SW_HIDE);\r
5482     return TRUE;\r
5483 \r
5484   case WM_COMMAND: /* message: received a command */\r
5485     switch (LOWORD(wParam)) {\r
5486     case IDCANCEL:\r
5487       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5488       ClearHighlights();\r
5489       DrawPosition(FALSE, NULL);\r
5490       return TRUE;\r
5491     case PB_King:\r
5492       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5493       break;\r
5494     case PB_Queen:\r
5495       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5496       break;\r
5497     case PB_Rook:\r
5498       promoChar = PieceToChar(BlackRook);\r
5499       break;\r
5500     case PB_Bishop:\r
5501       promoChar = PieceToChar(BlackBishop);\r
5502       break;\r
5503     case PB_Chancellor:\r
5504       promoChar = PieceToChar(BlackMarshall);\r
5505       break;\r
5506     case PB_Archbishop:\r
5507       promoChar = PieceToChar(BlackAngel);\r
5508       break;\r
5509     case PB_Knight:\r
5510       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5511       break;\r
5512     default:\r
5513       return FALSE;\r
5514     }\r
5515     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5516     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5517        only show the popup when we are already sure the move is valid or\r
5518        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5519        will figure out it is a promotion from the promoChar. */\r
5520     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5521     if (!appData.highlightLastMove) {\r
5522       ClearHighlights();\r
5523       DrawPosition(FALSE, NULL);\r
5524     }\r
5525     return TRUE;\r
5526   }\r
5527   return FALSE;\r
5528 }\r
5529 \r
5530 /* Pop up promotion dialog */\r
5531 VOID\r
5532 PromotionPopup(HWND hwnd)\r
5533 {\r
5534   FARPROC lpProc;\r
5535 \r
5536   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5537   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5538     hwnd, (DLGPROC)lpProc);\r
5539   FreeProcInstance(lpProc);\r
5540 }\r
5541 \r
5542 /* Toggle ShowThinking */\r
5543 VOID\r
5544 ToggleShowThinking()\r
5545 {\r
5546   appData.showThinking = !appData.showThinking;\r
5547   ShowThinkingEvent();\r
5548 }\r
5549 \r
5550 VOID\r
5551 LoadGameDialog(HWND hwnd, char* title)\r
5552 {\r
5553   UINT number = 0;\r
5554   FILE *f;\r
5555   char fileTitle[MSG_SIZ];\r
5556   f = OpenFileDialog(hwnd, "rb", "",\r
5557                      appData.oldSaveStyle ? "gam" : "pgn",\r
5558                      GAME_FILT,\r
5559                      title, &number, fileTitle, NULL);\r
5560   if (f != NULL) {\r
5561     cmailMsgLoaded = FALSE;\r
5562     if (number == 0) {\r
5563       int error = GameListBuild(f);\r
5564       if (error) {\r
5565         DisplayError("Cannot build game list", error);\r
5566       } else if (!ListEmpty(&gameList) &&\r
5567                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5568         GameListPopUp(f, fileTitle);\r
5569         return;\r
5570       }\r
5571       GameListDestroy();\r
5572       number = 1;\r
5573     }\r
5574     LoadGame(f, number, fileTitle, FALSE);\r
5575   }\r
5576 }\r
5577 \r
5578 VOID\r
5579 ChangedConsoleFont()\r
5580 {\r
5581   CHARFORMAT cfmt;\r
5582   CHARRANGE tmpsel, sel;\r
5583   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5584   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5585   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5586   PARAFORMAT paraf;\r
5587 \r
5588   cfmt.cbSize = sizeof(CHARFORMAT);\r
5589   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5590   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5591   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5592    * size.  This was undocumented in the version of MSVC++ that I had\r
5593    * when I wrote the code, but is apparently documented now.\r
5594    */\r
5595   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5596   cfmt.bCharSet = f->lf.lfCharSet;\r
5597   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5598   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5599   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5600   /* Why are the following seemingly needed too? */\r
5601   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5602   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5603   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5604   tmpsel.cpMin = 0;\r
5605   tmpsel.cpMax = -1; /*999999?*/\r
5606   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5607   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5608   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5609    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5610    */\r
5611   paraf.cbSize = sizeof(paraf);\r
5612   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5613   paraf.dxStartIndent = 0;\r
5614   paraf.dxOffset = WRAP_INDENT;\r
5615   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5616   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5617 }\r
5618 \r
5619 /*---------------------------------------------------------------------------*\\r
5620  *\r
5621  * Window Proc for main window\r
5622  *\r
5623 \*---------------------------------------------------------------------------*/\r
5624 \r
5625 /* Process messages for main window, etc. */\r
5626 LRESULT CALLBACK\r
5627 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5628 {\r
5629   FARPROC lpProc;\r
5630   int wmId, wmEvent;\r
5631   char *defName;\r
5632   FILE *f;\r
5633   UINT number;\r
5634   char fileTitle[MSG_SIZ];\r
5635   char buf[MSG_SIZ];\r
5636   static SnapData sd;\r
5637 \r
5638   switch (message) {\r
5639 \r
5640   case WM_PAINT: /* message: repaint portion of window */\r
5641     PaintProc(hwnd);\r
5642     break;\r
5643 \r
5644   case WM_ERASEBKGND:\r
5645     if (IsIconic(hwnd)) {\r
5646       /* Cheat; change the message */\r
5647       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5648     } else {\r
5649       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5650     }\r
5651     break;\r
5652 \r
5653   case WM_LBUTTONDOWN:\r
5654   case WM_MBUTTONDOWN:\r
5655   case WM_RBUTTONDOWN:\r
5656   case WM_LBUTTONUP:\r
5657   case WM_MBUTTONUP:\r
5658   case WM_RBUTTONUP:\r
5659   case WM_MOUSEMOVE:\r
5660   case WM_MOUSEWHEEL:\r
5661     MouseEvent(hwnd, message, wParam, lParam);\r
5662     break;\r
5663 \r
5664   JAWS_KB_NAVIGATION\r
5665 \r
5666   case WM_CHAR:\r
5667     \r
5668     JAWS_ALT_INTERCEPT\r
5669 \r
5670     if (appData.icsActive && (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9')) { \r
5671         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5672         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5673         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5674         SetFocus(h);\r
5675         SendMessage(h, message, wParam, lParam);\r
5676     } else if(lParam != KF_REPEAT) {\r
5677         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5678                 PopUpMoveDialog((char)wParam);\r
5679         } else if((char)wParam == 003) CopyGameToClipboard();\r
5680          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5681     }\r
5682 \r
5683     break;\r
5684 \r
5685   case WM_PALETTECHANGED:\r
5686     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5687       int nnew;\r
5688       HDC hdc = GetDC(hwndMain);\r
5689       SelectPalette(hdc, hPal, TRUE);\r
5690       nnew = RealizePalette(hdc);\r
5691       if (nnew > 0) {\r
5692         paletteChanged = TRUE;\r
5693 #if 0\r
5694         UpdateColors(hdc);\r
5695 #else\r
5696         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5697 #endif\r
5698       }\r
5699       ReleaseDC(hwnd, hdc);\r
5700     }\r
5701     break;\r
5702 \r
5703   case WM_QUERYNEWPALETTE:\r
5704     if (!appData.monoMode /*&& paletteChanged*/) {\r
5705       int nnew;\r
5706       HDC hdc = GetDC(hwndMain);\r
5707       paletteChanged = FALSE;\r
5708       SelectPalette(hdc, hPal, FALSE);\r
5709       nnew = RealizePalette(hdc);\r
5710       if (nnew > 0) {\r
5711         InvalidateRect(hwnd, &boardRect, FALSE);\r
5712       }\r
5713       ReleaseDC(hwnd, hdc);\r
5714       return TRUE;\r
5715     }\r
5716     return FALSE;\r
5717 \r
5718   case WM_COMMAND: /* message: command from application menu */\r
5719     wmId    = LOWORD(wParam);\r
5720     wmEvent = HIWORD(wParam);\r
5721 \r
5722     switch (wmId) {\r
5723     case IDM_NewGame:\r
5724       ResetGameEvent();\r
5725       AnalysisPopDown();\r
5726       SAY("new game enter a move to play against the computer with white");\r
5727       break;\r
5728 \r
5729     case IDM_NewGameFRC:\r
5730       if( NewGameFRC() == 0 ) {\r
5731         ResetGameEvent();\r
5732         AnalysisPopDown();\r
5733       }\r
5734       break;\r
5735 \r
5736     case IDM_NewVariant:\r
5737       NewVariantPopup(hwnd);\r
5738       break;\r
5739 \r
5740     case IDM_LoadGame:\r
5741       LoadGameDialog(hwnd, "Load Game from File");\r
5742       break;\r
5743 \r
5744     case IDM_LoadNextGame:\r
5745       ReloadGame(1);\r
5746       break;\r
5747 \r
5748     case IDM_LoadPrevGame:\r
5749       ReloadGame(-1);\r
5750       break;\r
5751 \r
5752     case IDM_ReloadGame:\r
5753       ReloadGame(0);\r
5754       break;\r
5755 \r
5756     case IDM_LoadPosition:\r
5757       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5758         Reset(FALSE, TRUE);\r
5759       }\r
5760       number = 1;\r
5761       f = OpenFileDialog(hwnd, "rb", "",\r
5762                          appData.oldSaveStyle ? "pos" : "fen",\r
5763                          POSITION_FILT,\r
5764                          "Load Position from File", &number, fileTitle, NULL);\r
5765       if (f != NULL) {\r
5766         LoadPosition(f, number, fileTitle);\r
5767       }\r
5768       break;\r
5769 \r
5770     case IDM_LoadNextPosition:\r
5771       ReloadPosition(1);\r
5772       break;\r
5773 \r
5774     case IDM_LoadPrevPosition:\r
5775       ReloadPosition(-1);\r
5776       break;\r
5777 \r
5778     case IDM_ReloadPosition:\r
5779       ReloadPosition(0);\r
5780       break;\r
5781 \r
5782     case IDM_SaveGame:\r
5783       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5784       f = OpenFileDialog(hwnd, "a", defName,\r
5785                          appData.oldSaveStyle ? "gam" : "pgn",\r
5786                          GAME_FILT,\r
5787                          "Save Game to File", NULL, fileTitle, NULL);\r
5788       if (f != NULL) {\r
5789         SaveGame(f, 0, "");\r
5790       }\r
5791       break;\r
5792 \r
5793     case IDM_SavePosition:\r
5794       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5795       f = OpenFileDialog(hwnd, "a", defName,\r
5796                          appData.oldSaveStyle ? "pos" : "fen",\r
5797                          POSITION_FILT,\r
5798                          "Save Position to File", NULL, fileTitle, NULL);\r
5799       if (f != NULL) {\r
5800         SavePosition(f, 0, "");\r
5801       }\r
5802       break;\r
5803 \r
5804     case IDM_SaveDiagram:\r
5805       defName = "diagram";\r
5806       f = OpenFileDialog(hwnd, "wb", defName,\r
5807                          "bmp",\r
5808                          DIAGRAM_FILT,\r
5809                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5810       if (f != NULL) {\r
5811         SaveDiagram(f);\r
5812       }\r
5813       break;\r
5814 \r
5815     case IDM_CopyGame:\r
5816       CopyGameToClipboard();\r
5817       break;\r
5818 \r
5819     case IDM_PasteGame:\r
5820       PasteGameFromClipboard();\r
5821       break;\r
5822 \r
5823     case IDM_CopyGameListToClipboard:\r
5824       CopyGameListToClipboard();\r
5825       break;\r
5826 \r
5827     /* [AS] Autodetect FEN or PGN data */\r
5828     case IDM_PasteAny:\r
5829       PasteGameOrFENFromClipboard();\r
5830       break;\r
5831 \r
5832     /* [AS] Move history */\r
5833     case IDM_ShowMoveHistory:\r
5834         if( MoveHistoryIsUp() ) {\r
5835             MoveHistoryPopDown();\r
5836         }\r
5837         else {\r
5838             MoveHistoryPopUp();\r
5839         }\r
5840         break;\r
5841 \r
5842     /* [AS] Eval graph */\r
5843     case IDM_ShowEvalGraph:\r
5844         if( EvalGraphIsUp() ) {\r
5845             EvalGraphPopDown();\r
5846         }\r
5847         else {\r
5848             EvalGraphPopUp();\r
5849             SetFocus(hwndMain);\r
5850         }\r
5851         break;\r
5852 \r
5853     /* [AS] Engine output */\r
5854     case IDM_ShowEngineOutput:\r
5855         if( EngineOutputIsUp() ) {\r
5856             EngineOutputPopDown();\r
5857         }\r
5858         else {\r
5859             EngineOutputPopUp();\r
5860         }\r
5861         break;\r
5862 \r
5863     /* [AS] User adjudication */\r
5864     case IDM_UserAdjudication_White:\r
5865         UserAdjudicationEvent( +1 );\r
5866         break;\r
5867 \r
5868     case IDM_UserAdjudication_Black:\r
5869         UserAdjudicationEvent( -1 );\r
5870         break;\r
5871 \r
5872     case IDM_UserAdjudication_Draw:\r
5873         UserAdjudicationEvent( 0 );\r
5874         break;\r
5875 \r
5876     /* [AS] Game list options dialog */\r
5877     case IDM_GameListOptions:\r
5878       GameListOptions();\r
5879       break;\r
5880 \r
5881     case IDM_NewChat:\r
5882       ChatPopUp();\r
5883       break;\r
5884 \r
5885     case IDM_CopyPosition:\r
5886       CopyFENToClipboard();\r
5887       break;\r
5888 \r
5889     case IDM_PastePosition:\r
5890       PasteFENFromClipboard();\r
5891       break;\r
5892 \r
5893     case IDM_MailMove:\r
5894       MailMoveEvent();\r
5895       break;\r
5896 \r
5897     case IDM_ReloadCMailMsg:\r
5898       Reset(TRUE, TRUE);\r
5899       ReloadCmailMsgEvent(FALSE);\r
5900       break;\r
5901 \r
5902     case IDM_Minimize:\r
5903       ShowWindow(hwnd, SW_MINIMIZE);\r
5904       break;\r
5905 \r
5906     case IDM_Exit:\r
5907       ExitEvent(0);\r
5908       break;\r
5909 \r
5910     case IDM_MachineWhite:\r
5911       MachineWhiteEvent();\r
5912       /*\r
5913        * refresh the tags dialog only if it's visible\r
5914        */\r
5915       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5916           char *tags;\r
5917           tags = PGNTags(&gameInfo);\r
5918           TagsPopUp(tags, CmailMsg());\r
5919           free(tags);\r
5920       }\r
5921       SAY("computer starts playing white");\r
5922       break;\r
5923 \r
5924     case IDM_MachineBlack:\r
5925       MachineBlackEvent();\r
5926       /*\r
5927        * refresh the tags dialog only if it's visible\r
5928        */\r
5929       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5930           char *tags;\r
5931           tags = PGNTags(&gameInfo);\r
5932           TagsPopUp(tags, CmailMsg());\r
5933           free(tags);\r
5934       }\r
5935       SAY("computer starts playing black");\r
5936       break;\r
5937 \r
5938     case IDM_TwoMachines:\r
5939       TwoMachinesEvent();\r
5940       /*\r
5941        * refresh the tags dialog only if it's visible\r
5942        */\r
5943       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5944           char *tags;\r
5945           tags = PGNTags(&gameInfo);\r
5946           TagsPopUp(tags, CmailMsg());\r
5947           free(tags);\r
5948       }\r
5949       SAY("programs start playing each other");\r
5950       break;\r
5951 \r
5952     case IDM_AnalysisMode:\r
5953       if (!first.analysisSupport) {\r
5954         sprintf(buf, "%s does not support analysis", first.tidy);\r
5955         DisplayError(buf, 0);\r
5956       } else {\r
5957         SAY("analyzing current position");\r
5958         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5959         if (appData.icsActive) {\r
5960                if (gameMode != IcsObserving) {\r
5961                        sprintf(buf, "You are not observing a game");\r
5962                        DisplayError(buf, 0);\r
5963                        /* secure check */\r
5964                        if (appData.icsEngineAnalyze) {\r
5965                                if (appData.debugMode) \r
5966                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5967                                ExitAnalyzeMode();\r
5968                                ModeHighlight();\r
5969                                break;\r
5970                        }\r
5971                        break;\r
5972                } else {\r
5973                        /* if enable, user want disable icsEngineAnalyze */\r
5974                        if (appData.icsEngineAnalyze) {\r
5975                                ExitAnalyzeMode();\r
5976                                ModeHighlight();\r
5977                                break;\r
5978                        }\r
5979                        appData.icsEngineAnalyze = TRUE;\r
5980                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5981                }\r
5982         } \r
5983         if (!appData.showThinking) ToggleShowThinking();\r
5984         AnalyzeModeEvent();\r
5985       }\r
5986       break;\r
5987 \r
5988     case IDM_AnalyzeFile:\r
5989       if (!first.analysisSupport) {\r
5990         char buf[MSG_SIZ];\r
5991         sprintf(buf, "%s does not support analysis", first.tidy);\r
5992         DisplayError(buf, 0);\r
5993       } else {\r
5994         if (!appData.showThinking) ToggleShowThinking();\r
5995         AnalyzeFileEvent();\r
5996         LoadGameDialog(hwnd, "Analyze Game from File");\r
5997         AnalysisPeriodicEvent(1);\r
5998       }\r
5999       break;\r
6000 \r
6001     case IDM_IcsClient:\r
6002       IcsClientEvent();\r
6003       break;\r
6004 \r
6005     case IDM_EditGame:\r
6006       EditGameEvent();\r
6007       SAY("edit game");\r
6008       break;\r
6009 \r
6010     case IDM_EditPosition:\r
6011       EditPositionEvent();\r
6012       SAY("to set up a position type a FEN");\r
6013       break;\r
6014 \r
6015     case IDM_Training:\r
6016       TrainingEvent();\r
6017       break;\r
6018 \r
6019     case IDM_ShowGameList:\r
6020       ShowGameListProc();\r
6021       break;\r
6022 \r
6023     case IDM_EditTags:\r
6024       EditTagsProc();\r
6025       break;\r
6026 \r
6027     case IDM_EditComment:\r
6028       if (commentDialogUp && editComment) {\r
6029         CommentPopDown();\r
6030       } else {\r
6031         EditCommentEvent();\r
6032       }\r
6033       break;\r
6034 \r
6035     case IDM_Pause:\r
6036       PauseEvent();\r
6037       break;\r
6038 \r
6039     case IDM_Accept:\r
6040       AcceptEvent();\r
6041       break;\r
6042 \r
6043     case IDM_Decline:\r
6044       DeclineEvent();\r
6045       break;\r
6046 \r
6047     case IDM_Rematch:\r
6048       RematchEvent();\r
6049       break;\r
6050 \r
6051     case IDM_CallFlag:\r
6052       CallFlagEvent();\r
6053       break;\r
6054 \r
6055     case IDM_Draw:\r
6056       DrawEvent();\r
6057       break;\r
6058 \r
6059     case IDM_Adjourn:\r
6060       AdjournEvent();\r
6061       break;\r
6062 \r
6063     case IDM_Abort:\r
6064       AbortEvent();\r
6065       break;\r
6066 \r
6067     case IDM_Resign:\r
6068       ResignEvent();\r
6069       break;\r
6070 \r
6071     case IDM_StopObserving:\r
6072       StopObservingEvent();\r
6073       break;\r
6074 \r
6075     case IDM_StopExamining:\r
6076       StopExaminingEvent();\r
6077       break;\r
6078 \r
6079     case IDM_TypeInMove:\r
6080       PopUpMoveDialog('\000');\r
6081       break;\r
6082 \r
6083     case IDM_TypeInName:\r
6084       PopUpNameDialog('\000');\r
6085       break;\r
6086 \r
6087     case IDM_Backward:\r
6088       BackwardEvent();\r
6089       SetFocus(hwndMain);\r
6090       break;\r
6091 \r
6092     JAWS_MENU_ITEMS\r
6093 \r
6094     case IDM_Forward:\r
6095       ForwardEvent();\r
6096       SetFocus(hwndMain);\r
6097       break;\r
6098 \r
6099     case IDM_ToStart:\r
6100       ToStartEvent();\r
6101       SetFocus(hwndMain);\r
6102       break;\r
6103 \r
6104     case IDM_ToEnd:\r
6105       ToEndEvent();\r
6106       SetFocus(hwndMain);\r
6107       break;\r
6108 \r
6109     case IDM_Revert:\r
6110       RevertEvent();\r
6111       break;\r
6112 \r
6113     case IDM_TruncateGame:\r
6114       TruncateGameEvent();\r
6115       break;\r
6116 \r
6117     case IDM_MoveNow:\r
6118       MoveNowEvent();\r
6119       break;\r
6120 \r
6121     case IDM_RetractMove:\r
6122       RetractMoveEvent();\r
6123       break;\r
6124 \r
6125     case IDM_FlipView:\r
6126       flipView = !flipView;\r
6127       DrawPosition(FALSE, NULL);\r
6128       break;\r
6129 \r
6130     case IDM_FlipClock:\r
6131       flipClock = !flipClock;\r
6132       DisplayBothClocks();\r
6133       DrawPosition(FALSE, NULL);\r
6134       break;\r
6135 \r
6136     case IDM_GeneralOptions:\r
6137       GeneralOptionsPopup(hwnd);\r
6138       DrawPosition(TRUE, NULL);\r
6139       break;\r
6140 \r
6141     case IDM_BoardOptions:\r
6142       BoardOptionsPopup(hwnd);\r
6143       break;\r
6144 \r
6145     case IDM_EnginePlayOptions:\r
6146       EnginePlayOptionsPopup(hwnd);\r
6147       break;\r
6148 \r
6149     case IDM_Engine1Options:\r
6150       EngineOptionsPopup(hwnd, &first);\r
6151       break;\r
6152 \r
6153     case IDM_Engine2Options:\r
6154       EngineOptionsPopup(hwnd, &second);\r
6155       break;\r
6156 \r
6157     case IDM_OptionsUCI:\r
6158       UciOptionsPopup(hwnd);\r
6159       break;\r
6160 \r
6161     case IDM_IcsOptions:\r
6162       IcsOptionsPopup(hwnd);\r
6163       break;\r
6164 \r
6165     case IDM_Fonts:\r
6166       FontsOptionsPopup(hwnd);\r
6167       break;\r
6168 \r
6169     case IDM_Sounds:\r
6170       SoundOptionsPopup(hwnd);\r
6171       break;\r
6172 \r
6173     case IDM_CommPort:\r
6174       CommPortOptionsPopup(hwnd);\r
6175       break;\r
6176 \r
6177     case IDM_LoadOptions:\r
6178       LoadOptionsPopup(hwnd);\r
6179       break;\r
6180 \r
6181     case IDM_SaveOptions:\r
6182       SaveOptionsPopup(hwnd);\r
6183       break;\r
6184 \r
6185     case IDM_TimeControl:\r
6186       TimeControlOptionsPopup(hwnd);\r
6187       break;\r
6188 \r
6189     case IDM_SaveSettings:\r
6190       SaveSettings(settingsFileName);\r
6191       break;\r
6192 \r
6193     case IDM_SaveSettingsOnExit:\r
6194       saveSettingsOnExit = !saveSettingsOnExit;\r
6195       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6196                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6197                                          MF_CHECKED : MF_UNCHECKED));\r
6198       break;\r
6199 \r
6200     case IDM_Hint:\r
6201       HintEvent();\r
6202       break;\r
6203 \r
6204     case IDM_Book:\r
6205       BookEvent();\r
6206       break;\r
6207 \r
6208     case IDM_AboutGame:\r
6209       AboutGameEvent();\r
6210       break;\r
6211 \r
6212     case IDM_Debug:\r
6213       appData.debugMode = !appData.debugMode;\r
6214       if (appData.debugMode) {\r
6215         char dir[MSG_SIZ];\r
6216         GetCurrentDirectory(MSG_SIZ, dir);\r
6217         SetCurrentDirectory(installDir);\r
6218         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6219         SetCurrentDirectory(dir);\r
6220         setbuf(debugFP, NULL);\r
6221       } else {\r
6222         fclose(debugFP);\r
6223         debugFP = NULL;\r
6224       }\r
6225       break;\r
6226 \r
6227     case IDM_HELPCONTENTS:\r
6228       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6229           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6230           MessageBox (GetFocus(),\r
6231                     "Unable to activate help",\r
6232                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6233       }\r
6234       break;\r
6235 \r
6236     case IDM_HELPSEARCH:\r
6237         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6238             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6239         MessageBox (GetFocus(),\r
6240                     "Unable to activate help",\r
6241                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6242       }\r
6243       break;\r
6244 \r
6245     case IDM_HELPHELP:\r
6246       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6247         MessageBox (GetFocus(),\r
6248                     "Unable to activate help",\r
6249                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6250       }\r
6251       break;\r
6252 \r
6253     case IDM_ABOUT:\r
6254       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6255       DialogBox(hInst, \r
6256         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6257         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6258       FreeProcInstance(lpProc);\r
6259       break;\r
6260 \r
6261     case IDM_DirectCommand1:\r
6262       AskQuestionEvent("Direct Command",\r
6263                        "Send to chess program:", "", "1");\r
6264       break;\r
6265     case IDM_DirectCommand2:\r
6266       AskQuestionEvent("Direct Command",\r
6267                        "Send to second chess program:", "", "2");\r
6268       break;\r
6269 \r
6270     case EP_WhitePawn:\r
6271       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6272       fromX = fromY = -1;\r
6273       break;\r
6274 \r
6275     case EP_WhiteKnight:\r
6276       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6277       fromX = fromY = -1;\r
6278       break;\r
6279 \r
6280     case EP_WhiteBishop:\r
6281       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6282       fromX = fromY = -1;\r
6283       break;\r
6284 \r
6285     case EP_WhiteRook:\r
6286       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6287       fromX = fromY = -1;\r
6288       break;\r
6289 \r
6290     case EP_WhiteQueen:\r
6291       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6292       fromX = fromY = -1;\r
6293       break;\r
6294 \r
6295     case EP_WhiteFerz:\r
6296       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6297       fromX = fromY = -1;\r
6298       break;\r
6299 \r
6300     case EP_WhiteWazir:\r
6301       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6302       fromX = fromY = -1;\r
6303       break;\r
6304 \r
6305     case EP_WhiteAlfil:\r
6306       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6307       fromX = fromY = -1;\r
6308       break;\r
6309 \r
6310     case EP_WhiteCannon:\r
6311       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6312       fromX = fromY = -1;\r
6313       break;\r
6314 \r
6315     case EP_WhiteCardinal:\r
6316       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6317       fromX = fromY = -1;\r
6318       break;\r
6319 \r
6320     case EP_WhiteMarshall:\r
6321       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6322       fromX = fromY = -1;\r
6323       break;\r
6324 \r
6325     case EP_WhiteKing:\r
6326       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6327       fromX = fromY = -1;\r
6328       break;\r
6329 \r
6330     case EP_BlackPawn:\r
6331       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6332       fromX = fromY = -1;\r
6333       break;\r
6334 \r
6335     case EP_BlackKnight:\r
6336       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6337       fromX = fromY = -1;\r
6338       break;\r
6339 \r
6340     case EP_BlackBishop:\r
6341       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6342       fromX = fromY = -1;\r
6343       break;\r
6344 \r
6345     case EP_BlackRook:\r
6346       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6347       fromX = fromY = -1;\r
6348       break;\r
6349 \r
6350     case EP_BlackQueen:\r
6351       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6352       fromX = fromY = -1;\r
6353       break;\r
6354 \r
6355     case EP_BlackFerz:\r
6356       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6357       fromX = fromY = -1;\r
6358       break;\r
6359 \r
6360     case EP_BlackWazir:\r
6361       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6362       fromX = fromY = -1;\r
6363       break;\r
6364 \r
6365     case EP_BlackAlfil:\r
6366       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6367       fromX = fromY = -1;\r
6368       break;\r
6369 \r
6370     case EP_BlackCannon:\r
6371       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6372       fromX = fromY = -1;\r
6373       break;\r
6374 \r
6375     case EP_BlackCardinal:\r
6376       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6377       fromX = fromY = -1;\r
6378       break;\r
6379 \r
6380     case EP_BlackMarshall:\r
6381       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6382       fromX = fromY = -1;\r
6383       break;\r
6384 \r
6385     case EP_BlackKing:\r
6386       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6387       fromX = fromY = -1;\r
6388       break;\r
6389 \r
6390     case EP_EmptySquare:\r
6391       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6392       fromX = fromY = -1;\r
6393       break;\r
6394 \r
6395     case EP_ClearBoard:\r
6396       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6397       fromX = fromY = -1;\r
6398       break;\r
6399 \r
6400     case EP_White:\r
6401       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6402       fromX = fromY = -1;\r
6403       break;\r
6404 \r
6405     case EP_Black:\r
6406       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6407       fromX = fromY = -1;\r
6408       break;\r
6409 \r
6410     case EP_Promote:\r
6411       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6412       fromX = fromY = -1;\r
6413       break;\r
6414 \r
6415     case EP_Demote:\r
6416       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6417       fromX = fromY = -1;\r
6418       break;\r
6419 \r
6420     case DP_Pawn:\r
6421       DropMenuEvent(WhitePawn, fromX, fromY);\r
6422       fromX = fromY = -1;\r
6423       break;\r
6424 \r
6425     case DP_Knight:\r
6426       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6427       fromX = fromY = -1;\r
6428       break;\r
6429 \r
6430     case DP_Bishop:\r
6431       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6432       fromX = fromY = -1;\r
6433       break;\r
6434 \r
6435     case DP_Rook:\r
6436       DropMenuEvent(WhiteRook, fromX, fromY);\r
6437       fromX = fromY = -1;\r
6438       break;\r
6439 \r
6440     case DP_Queen:\r
6441       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6442       fromX = fromY = -1;\r
6443       break;\r
6444 \r
6445     default:\r
6446       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6447     }\r
6448     break;\r
6449 \r
6450   case WM_TIMER:\r
6451     switch (wParam) {\r
6452     case CLOCK_TIMER_ID:\r
6453       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6454       clockTimerEvent = 0;\r
6455       DecrementClocks(); /* call into back end */\r
6456       break;\r
6457     case LOAD_GAME_TIMER_ID:\r
6458       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6459       loadGameTimerEvent = 0;\r
6460       AutoPlayGameLoop(); /* call into back end */\r
6461       break;\r
6462     case ANALYSIS_TIMER_ID:\r
6463       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6464                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6465         AnalysisPeriodicEvent(0);\r
6466       } else {\r
6467         KillTimer(hwnd, analysisTimerEvent);\r
6468         analysisTimerEvent = 0;\r
6469       }\r
6470       break;\r
6471     case DELAYED_TIMER_ID:\r
6472       KillTimer(hwnd, delayedTimerEvent);\r
6473       delayedTimerEvent = 0;\r
6474       delayedTimerCallback();\r
6475       break;\r
6476     }\r
6477     break;\r
6478 \r
6479   case WM_USER_Input:\r
6480     InputEvent(hwnd, message, wParam, lParam);\r
6481     break;\r
6482 \r
6483   /* [AS] Also move "attached" child windows */\r
6484   case WM_WINDOWPOSCHANGING:\r
6485 \r
6486     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6487         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6488 \r
6489         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6490             /* Window is moving */\r
6491             RECT rcMain;\r
6492 \r
6493 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6494             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6495             rcMain.right  = boardX + winWidth;\r
6496             rcMain.top    = boardY;\r
6497             rcMain.bottom = boardY + winHeight;\r
6498             \r
6499             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6500             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6501             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6502             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6503             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6504             boardX = lpwp->x;\r
6505             boardY = lpwp->y;\r
6506         }\r
6507     }\r
6508     break;\r
6509 \r
6510   /* [AS] Snapping */\r
6511   case WM_ENTERSIZEMOVE:\r
6512     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6513     if (hwnd == hwndMain) {\r
6514       doingSizing = TRUE;\r
6515       lastSizing = 0;\r
6516     }\r
6517     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6518     break;\r
6519 \r
6520   case WM_SIZING:\r
6521     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6522     if (hwnd == hwndMain) {\r
6523       lastSizing = wParam;\r
6524     }\r
6525     break;\r
6526 \r
6527   case WM_MOVING:\r
6528     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6529       return OnMoving( &sd, hwnd, wParam, lParam );\r
6530 \r
6531   case WM_EXITSIZEMOVE:\r
6532     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6533     if (hwnd == hwndMain) {\r
6534       RECT client;\r
6535       doingSizing = FALSE;\r
6536       InvalidateRect(hwnd, &boardRect, FALSE);\r
6537       GetClientRect(hwnd, &client);\r
6538       ResizeBoard(client.right, client.bottom, lastSizing);\r
6539       lastSizing = 0;\r
6540       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6541     }\r
6542     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6543     break;\r
6544 \r
6545   case WM_DESTROY: /* message: window being destroyed */\r
6546     PostQuitMessage(0);\r
6547     break;\r
6548 \r
6549   case WM_CLOSE:\r
6550     if (hwnd == hwndMain) {\r
6551       ExitEvent(0);\r
6552     }\r
6553     break;\r
6554 \r
6555   default:      /* Passes it on if unprocessed */\r
6556     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6557   }\r
6558   return 0;\r
6559 }\r
6560 \r
6561 /*---------------------------------------------------------------------------*\\r
6562  *\r
6563  * Misc utility routines\r
6564  *\r
6565 \*---------------------------------------------------------------------------*/\r
6566 \r
6567 /*\r
6568  * Decent random number generator, at least not as bad as Windows\r
6569  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6570  */\r
6571 unsigned int randstate;\r
6572 \r
6573 int\r
6574 myrandom(void)\r
6575 {\r
6576   randstate = randstate * 1664525 + 1013904223;\r
6577   return (int) randstate & 0x7fffffff;\r
6578 }\r
6579 \r
6580 void\r
6581 mysrandom(unsigned int seed)\r
6582 {\r
6583   randstate = seed;\r
6584 }\r
6585 \r
6586 \r
6587 /* \r
6588  * returns TRUE if user selects a different color, FALSE otherwise \r
6589  */\r
6590 \r
6591 BOOL\r
6592 ChangeColor(HWND hwnd, COLORREF *which)\r
6593 {\r
6594   static BOOL firstTime = TRUE;\r
6595   static DWORD customColors[16];\r
6596   CHOOSECOLOR cc;\r
6597   COLORREF newcolor;\r
6598   int i;\r
6599   ColorClass ccl;\r
6600 \r
6601   if (firstTime) {\r
6602     /* Make initial colors in use available as custom colors */\r
6603     /* Should we put the compiled-in defaults here instead? */\r
6604     i = 0;\r
6605     customColors[i++] = lightSquareColor & 0xffffff;\r
6606     customColors[i++] = darkSquareColor & 0xffffff;\r
6607     customColors[i++] = whitePieceColor & 0xffffff;\r
6608     customColors[i++] = blackPieceColor & 0xffffff;\r
6609     customColors[i++] = highlightSquareColor & 0xffffff;\r
6610     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6611 \r
6612     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6613       customColors[i++] = textAttribs[ccl].color;\r
6614     }\r
6615     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6616     firstTime = FALSE;\r
6617   }\r
6618 \r
6619   cc.lStructSize = sizeof(cc);\r
6620   cc.hwndOwner = hwnd;\r
6621   cc.hInstance = NULL;\r
6622   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6623   cc.lpCustColors = (LPDWORD) customColors;\r
6624   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6625 \r
6626   if (!ChooseColor(&cc)) return FALSE;\r
6627 \r
6628   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6629   if (newcolor == *which) return FALSE;\r
6630   *which = newcolor;\r
6631   return TRUE;\r
6632 \r
6633   /*\r
6634   InitDrawingColors();\r
6635   InvalidateRect(hwnd, &boardRect, FALSE);\r
6636   */\r
6637 }\r
6638 \r
6639 BOOLEAN\r
6640 MyLoadSound(MySound *ms)\r
6641 {\r
6642   BOOL ok = FALSE;\r
6643   struct stat st;\r
6644   FILE *f;\r
6645 \r
6646   if (ms->data) free(ms->data);\r
6647   ms->data = NULL;\r
6648 \r
6649   switch (ms->name[0]) {\r
6650   case NULLCHAR:\r
6651     /* Silence */\r
6652     ok = TRUE;\r
6653     break;\r
6654   case '$':\r
6655     /* System sound from Control Panel.  Don't preload here. */\r
6656     ok = TRUE;\r
6657     break;\r
6658   case '!':\r
6659     if (ms->name[1] == NULLCHAR) {\r
6660       /* "!" alone = silence */\r
6661       ok = TRUE;\r
6662     } else {\r
6663       /* Builtin wave resource.  Error if not found. */\r
6664       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6665       if (h == NULL) break;\r
6666       ms->data = (void *)LoadResource(hInst, h);\r
6667       if (h == NULL) break;\r
6668       ok = TRUE;\r
6669     }\r
6670     break;\r
6671   default:\r
6672     /* .wav file.  Error if not found. */\r
6673     f = fopen(ms->name, "rb");\r
6674     if (f == NULL) break;\r
6675     if (fstat(fileno(f), &st) < 0) break;\r
6676     ms->data = malloc(st.st_size);\r
6677     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6678     fclose(f);\r
6679     ok = TRUE;\r
6680     break;\r
6681   }\r
6682   if (!ok) {\r
6683     char buf[MSG_SIZ];\r
6684     sprintf(buf, "Error loading sound %s", ms->name);\r
6685     DisplayError(buf, GetLastError());\r
6686   }\r
6687   return ok;\r
6688 }\r
6689 \r
6690 BOOLEAN\r
6691 MyPlaySound(MySound *ms)\r
6692 {\r
6693   BOOLEAN ok = FALSE;\r
6694 \r
6695   switch (ms->name[0]) {\r
6696   case NULLCHAR:\r
6697         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6698     /* Silence */\r
6699     ok = TRUE;\r
6700     break;\r
6701   case '$':\r
6702     /* System sound from Control Panel (deprecated feature).\r
6703        "$" alone or an unset sound name gets default beep (still in use). */\r
6704     if (ms->name[1]) {\r
6705       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6706     }\r
6707     if (!ok) ok = MessageBeep(MB_OK);\r
6708     break; \r
6709   case '!':\r
6710     /* Builtin wave resource, or "!" alone for silence */\r
6711     if (ms->name[1]) {\r
6712       if (ms->data == NULL) return FALSE;\r
6713       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6714     } else {\r
6715       ok = TRUE;\r
6716     }\r
6717     break;\r
6718   default:\r
6719     /* .wav file.  Error if not found. */\r
6720     if (ms->data == NULL) return FALSE;\r
6721     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6722     break;\r
6723   }\r
6724   /* Don't print an error: this can happen innocently if the sound driver\r
6725      is busy; for instance, if another instance of WinBoard is playing\r
6726      a sound at about the same time. */\r
6727 #if 0\r
6728   if (!ok) {\r
6729     char buf[MSG_SIZ];\r
6730     sprintf(buf, "Error playing sound %s", ms->name);\r
6731     DisplayError(buf, GetLastError());\r
6732   }\r
6733 #endif\r
6734   return ok;\r
6735 }\r
6736 \r
6737 \r
6738 LRESULT CALLBACK\r
6739 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6740 {\r
6741   BOOL ok;\r
6742   OPENFILENAME *ofn;\r
6743   static UINT *number; /* gross that this is static */\r
6744 \r
6745   switch (message) {\r
6746   case WM_INITDIALOG: /* message: initialize dialog box */\r
6747     /* Center the dialog over the application window */\r
6748     ofn = (OPENFILENAME *) lParam;\r
6749     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6750       number = (UINT *) ofn->lCustData;\r
6751       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6752     } else {\r
6753       number = NULL;\r
6754     }\r
6755     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6756     return FALSE;  /* Allow for further processing */\r
6757 \r
6758   case WM_COMMAND:\r
6759     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6760       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6761     }\r
6762     return FALSE;  /* Allow for further processing */\r
6763   }\r
6764   return FALSE;\r
6765 }\r
6766 \r
6767 UINT APIENTRY\r
6768 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6769 {\r
6770   static UINT *number;\r
6771   OPENFILENAME *ofname;\r
6772   OFNOTIFY *ofnot;\r
6773   switch (uiMsg) {\r
6774   case WM_INITDIALOG:\r
6775     ofname = (OPENFILENAME *)lParam;\r
6776     number = (UINT *)(ofname->lCustData);\r
6777     break;\r
6778   case WM_NOTIFY:\r
6779     ofnot = (OFNOTIFY *)lParam;\r
6780     if (ofnot->hdr.code == CDN_FILEOK) {\r
6781       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6782     }\r
6783     break;\r
6784   }\r
6785   return 0;\r
6786 }\r
6787 \r
6788 \r
6789 FILE *\r
6790 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6791                char *nameFilt, char *dlgTitle, UINT *number,\r
6792                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6793 {\r
6794   OPENFILENAME openFileName;\r
6795   char buf1[MSG_SIZ];\r
6796   FILE *f;\r
6797 \r
6798   if (fileName == NULL) fileName = buf1;\r
6799   if (defName == NULL) {\r
6800     strcpy(fileName, "*.");\r
6801     strcat(fileName, defExt);\r
6802   } else {\r
6803     strcpy(fileName, defName);\r
6804   }\r
6805   if (fileTitle) strcpy(fileTitle, "");\r
6806   if (number) *number = 0;\r
6807 \r
6808   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6809   openFileName.hwndOwner         = hwnd;\r
6810   openFileName.hInstance         = (HANDLE) hInst;\r
6811   openFileName.lpstrFilter       = nameFilt;\r
6812   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6813   openFileName.nMaxCustFilter    = 0L;\r
6814   openFileName.nFilterIndex      = 1L;\r
6815   openFileName.lpstrFile         = fileName;\r
6816   openFileName.nMaxFile          = MSG_SIZ;\r
6817   openFileName.lpstrFileTitle    = fileTitle;\r
6818   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6819   openFileName.lpstrInitialDir   = NULL;\r
6820   openFileName.lpstrTitle        = dlgTitle;\r
6821   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6822     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6823     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6824     | (oldDialog ? 0 : OFN_EXPLORER);\r
6825   openFileName.nFileOffset       = 0;\r
6826   openFileName.nFileExtension    = 0;\r
6827   openFileName.lpstrDefExt       = defExt;\r
6828   openFileName.lCustData         = (LONG) number;\r
6829   openFileName.lpfnHook          = oldDialog ?\r
6830     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6831   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6832 \r
6833   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6834                         GetOpenFileName(&openFileName)) {\r
6835     /* open the file */\r
6836     f = fopen(openFileName.lpstrFile, write);\r
6837     if (f == NULL) {\r
6838       MessageBox(hwnd, "File open failed", NULL,\r
6839                  MB_OK|MB_ICONEXCLAMATION);\r
6840       return NULL;\r
6841     }\r
6842   } else {\r
6843     int err = CommDlgExtendedError();\r
6844     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6845     return FALSE;\r
6846   }\r
6847   return f;\r
6848 }\r
6849 \r
6850 \r
6851 \r
6852 VOID APIENTRY\r
6853 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6854 {\r
6855   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6856 \r
6857   /*\r
6858    * Get the first pop-up menu in the menu template. This is the\r
6859    * menu that TrackPopupMenu displays.\r
6860    */\r
6861   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6862 \r
6863   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6864 \r
6865   /*\r
6866    * TrackPopup uses screen coordinates, so convert the\r
6867    * coordinates of the mouse click to screen coordinates.\r
6868    */\r
6869   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6870 \r
6871   /* Draw and track the floating pop-up menu. */\r
6872   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6873                  pt.x, pt.y, 0, hwnd, NULL);\r
6874 \r
6875   /* Destroy the menu.*/\r
6876   DestroyMenu(hmenu);\r
6877 }\r
6878    \r
6879 typedef struct {\r
6880   HWND hDlg, hText;\r
6881   int sizeX, sizeY, newSizeX, newSizeY;\r
6882   HDWP hdwp;\r
6883 } ResizeEditPlusButtonsClosure;\r
6884 \r
6885 BOOL CALLBACK\r
6886 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6887 {\r
6888   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6889   RECT rect;\r
6890   POINT pt;\r
6891 \r
6892   if (hChild == cl->hText) return TRUE;\r
6893   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6894   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6895   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6896   ScreenToClient(cl->hDlg, &pt);\r
6897   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6898     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6899   return TRUE;\r
6900 }\r
6901 \r
6902 /* Resize a dialog that has a (rich) edit field filling most of\r
6903    the top, with a row of buttons below */\r
6904 VOID\r
6905 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6906 {\r
6907   RECT rectText;\r
6908   int newTextHeight, newTextWidth;\r
6909   ResizeEditPlusButtonsClosure cl;\r
6910   \r
6911   /*if (IsIconic(hDlg)) return;*/\r
6912   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6913   \r
6914   cl.hdwp = BeginDeferWindowPos(8);\r
6915 \r
6916   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6917   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6918   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6919   if (newTextHeight < 0) {\r
6920     newSizeY += -newTextHeight;\r
6921     newTextHeight = 0;\r
6922   }\r
6923   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6924     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6925 \r
6926   cl.hDlg = hDlg;\r
6927   cl.hText = hText;\r
6928   cl.sizeX = sizeX;\r
6929   cl.sizeY = sizeY;\r
6930   cl.newSizeX = newSizeX;\r
6931   cl.newSizeY = newSizeY;\r
6932   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6933 \r
6934   EndDeferWindowPos(cl.hdwp);\r
6935 }\r
6936 \r
6937 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6938 {\r
6939     RECT    rChild, rParent;\r
6940     int     wChild, hChild, wParent, hParent;\r
6941     int     wScreen, hScreen, xNew, yNew;\r
6942     HDC     hdc;\r
6943 \r
6944     /* Get the Height and Width of the child window */\r
6945     GetWindowRect (hwndChild, &rChild);\r
6946     wChild = rChild.right - rChild.left;\r
6947     hChild = rChild.bottom - rChild.top;\r
6948 \r
6949     /* Get the Height and Width of the parent window */\r
6950     GetWindowRect (hwndParent, &rParent);\r
6951     wParent = rParent.right - rParent.left;\r
6952     hParent = rParent.bottom - rParent.top;\r
6953 \r
6954     /* Get the display limits */\r
6955     hdc = GetDC (hwndChild);\r
6956     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6957     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6958     ReleaseDC(hwndChild, hdc);\r
6959 \r
6960     /* Calculate new X position, then adjust for screen */\r
6961     xNew = rParent.left + ((wParent - wChild) /2);\r
6962     if (xNew < 0) {\r
6963         xNew = 0;\r
6964     } else if ((xNew+wChild) > wScreen) {\r
6965         xNew = wScreen - wChild;\r
6966     }\r
6967 \r
6968     /* Calculate new Y position, then adjust for screen */\r
6969     if( mode == 0 ) {\r
6970         yNew = rParent.top  + ((hParent - hChild) /2);\r
6971     }\r
6972     else {\r
6973         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6974     }\r
6975 \r
6976     if (yNew < 0) {\r
6977         yNew = 0;\r
6978     } else if ((yNew+hChild) > hScreen) {\r
6979         yNew = hScreen - hChild;\r
6980     }\r
6981 \r
6982     /* Set it, and return */\r
6983     return SetWindowPos (hwndChild, NULL,\r
6984                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6985 }\r
6986 \r
6987 /* Center one window over another */\r
6988 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6989 {\r
6990     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6991 }\r
6992 \r
6993 /*---------------------------------------------------------------------------*\\r
6994  *\r
6995  * Startup Dialog functions\r
6996  *\r
6997 \*---------------------------------------------------------------------------*/\r
6998 void\r
6999 InitComboStrings(HANDLE hwndCombo, char **cd)\r
7000 {\r
7001   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7002 \r
7003   while (*cd != NULL) {\r
7004     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
7005     cd++;\r
7006   }\r
7007 }\r
7008 \r
7009 void\r
7010 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
7011 {\r
7012   char buf1[ARG_MAX];\r
7013   int len;\r
7014 \r
7015   if (str[0] == '@') {\r
7016     FILE* f = fopen(str + 1, "r");\r
7017     if (f == NULL) {\r
7018       DisplayFatalError(str + 1, errno, 2);\r
7019       return;\r
7020     }\r
7021     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
7022     fclose(f);\r
7023     buf1[len] = NULLCHAR;\r
7024     str = buf1;\r
7025   }\r
7026 \r
7027   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7028 \r
7029   for (;;) {\r
7030     char buf[MSG_SIZ];\r
7031     char *end = strchr(str, '\n');\r
7032     if (end == NULL) return;\r
7033     memcpy(buf, str, end - str);\r
7034     buf[end - str] = NULLCHAR;\r
7035     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
7036     str = end + 1;\r
7037   }\r
7038 }\r
7039 \r
7040 void\r
7041 SetStartupDialogEnables(HWND hDlg)\r
7042 {\r
7043   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
7044     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7045     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
7046   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7047     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
7048   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
7049     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
7050   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
7051     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
7052   EnableWindow(GetDlgItem(hDlg, IDOK),\r
7053     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7054     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
7055     IsDlgButtonChecked(hDlg, OPT_View));\r
7056 }\r
7057 \r
7058 char *\r
7059 QuoteForFilename(char *filename)\r
7060 {\r
7061   int dquote, space;\r
7062   dquote = strchr(filename, '"') != NULL;\r
7063   space = strchr(filename, ' ') != NULL;\r
7064   if (dquote || space) {\r
7065     if (dquote) {\r
7066       return "'";\r
7067     } else {\r
7068       return "\"";\r
7069     }\r
7070   } else {\r
7071     return "";\r
7072   }\r
7073 }\r
7074 \r
7075 VOID\r
7076 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
7077 {\r
7078   char buf[MSG_SIZ];\r
7079   char *q;\r
7080 \r
7081   InitComboStringsFromOption(hwndCombo, nthnames);\r
7082   q = QuoteForFilename(nthcp);\r
7083   sprintf(buf, "%s%s%s", q, nthcp, q);\r
7084   if (*nthdir != NULLCHAR) {\r
7085     q = QuoteForFilename(nthdir);\r
7086     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
7087   }\r
7088   if (*nthcp == NULLCHAR) {\r
7089     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7090   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7091     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7092     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7093   }\r
7094 }\r
7095 \r
7096 LRESULT CALLBACK\r
7097 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7098 {\r
7099   char buf[MSG_SIZ];\r
7100   HANDLE hwndCombo;\r
7101   char *p;\r
7102 \r
7103   switch (message) {\r
7104   case WM_INITDIALOG:\r
7105     /* Center the dialog */\r
7106     CenterWindow (hDlg, GetDesktopWindow());\r
7107     /* Initialize the dialog items */\r
7108     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7109                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7110                   firstChessProgramNames);\r
7111     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7112                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7113                   secondChessProgramNames);\r
7114     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7115     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7116     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7117     if (*appData.icsHelper != NULLCHAR) {\r
7118       char *q = QuoteForFilename(appData.icsHelper);\r
7119       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7120     }\r
7121     if (*appData.icsHost == NULLCHAR) {\r
7122       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7123       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7124     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7125       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7126       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7127     }\r
7128 \r
7129     if (appData.icsActive) {\r
7130       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7131     }\r
7132     else if (appData.noChessProgram) {\r
7133       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7134     }\r
7135     else {\r
7136       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7137     }\r
7138 \r
7139     SetStartupDialogEnables(hDlg);\r
7140     return TRUE;\r
7141 \r
7142   case WM_COMMAND:\r
7143     switch (LOWORD(wParam)) {\r
7144     case IDOK:\r
7145       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7146         strcpy(buf, "/fcp=");\r
7147         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7148         p = buf;\r
7149         ParseArgs(StringGet, &p);\r
7150         strcpy(buf, "/scp=");\r
7151         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7152         p = buf;\r
7153         ParseArgs(StringGet, &p);\r
7154         appData.noChessProgram = FALSE;\r
7155         appData.icsActive = FALSE;\r
7156       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7157         strcpy(buf, "/ics /icshost=");\r
7158         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7159         p = buf;\r
7160         ParseArgs(StringGet, &p);\r
7161         if (appData.zippyPlay) {\r
7162           strcpy(buf, "/fcp=");\r
7163           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7164           p = buf;\r
7165           ParseArgs(StringGet, &p);\r
7166         }\r
7167       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7168         appData.noChessProgram = TRUE;\r
7169         appData.icsActive = FALSE;\r
7170       } else {\r
7171         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7172                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7173         return TRUE;\r
7174       }\r
7175       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7176         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7177         p = buf;\r
7178         ParseArgs(StringGet, &p);\r
7179       }\r
7180       EndDialog(hDlg, TRUE);\r
7181       return TRUE;\r
7182 \r
7183     case IDCANCEL:\r
7184       ExitEvent(0);\r
7185       return TRUE;\r
7186 \r
7187     case IDM_HELPCONTENTS:\r
7188       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7189         MessageBox (GetFocus(),\r
7190                     "Unable to activate help",\r
7191                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7192       }\r
7193       break;\r
7194 \r
7195     default:\r
7196       SetStartupDialogEnables(hDlg);\r
7197       break;\r
7198     }\r
7199     break;\r
7200   }\r
7201   return FALSE;\r
7202 }\r
7203 \r
7204 /*---------------------------------------------------------------------------*\\r
7205  *\r
7206  * About box dialog functions\r
7207  *\r
7208 \*---------------------------------------------------------------------------*/\r
7209 \r
7210 /* Process messages for "About" dialog box */\r
7211 LRESULT CALLBACK\r
7212 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7213 {\r
7214   switch (message) {\r
7215   case WM_INITDIALOG: /* message: initialize dialog box */\r
7216     /* Center the dialog over the application window */\r
7217     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7218     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7219     JAWS_COPYRIGHT\r
7220     return (TRUE);\r
7221 \r
7222   case WM_COMMAND: /* message: received a command */\r
7223     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7224         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7225       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7226       return (TRUE);\r
7227     }\r
7228     break;\r
7229   }\r
7230   return (FALSE);\r
7231 }\r
7232 \r
7233 /*---------------------------------------------------------------------------*\\r
7234  *\r
7235  * Comment Dialog functions\r
7236  *\r
7237 \*---------------------------------------------------------------------------*/\r
7238 \r
7239 LRESULT CALLBACK\r
7240 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7241 {\r
7242   static HANDLE hwndText = NULL;\r
7243   int len, newSizeX, newSizeY, flags;\r
7244   static int sizeX, sizeY;\r
7245   char *str;\r
7246   RECT rect;\r
7247   MINMAXINFO *mmi;\r
7248 \r
7249   switch (message) {\r
7250   case WM_INITDIALOG: /* message: initialize dialog box */\r
7251     /* Initialize the dialog items */\r
7252     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7253     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7254     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7255     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7256     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7257     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7258     SetWindowText(hDlg, commentTitle);\r
7259     if (editComment) {\r
7260       SetFocus(hwndText);\r
7261     } else {\r
7262       SetFocus(GetDlgItem(hDlg, IDOK));\r
7263     }\r
7264     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7265                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7266                 MAKELPARAM(FALSE, 0));\r
7267     /* Size and position the dialog */\r
7268     if (!commentDialog) {\r
7269       commentDialog = hDlg;\r
7270       flags = SWP_NOZORDER;\r
7271       GetClientRect(hDlg, &rect);\r
7272       sizeX = rect.right;\r
7273       sizeY = rect.bottom;\r
7274       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7275           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7276         WINDOWPLACEMENT wp;\r
7277         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7278         wp.length = sizeof(WINDOWPLACEMENT);\r
7279         wp.flags = 0;\r
7280         wp.showCmd = SW_SHOW;\r
7281         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7282         wp.rcNormalPosition.left = commentX;\r
7283         wp.rcNormalPosition.right = commentX + commentW;\r
7284         wp.rcNormalPosition.top = commentY;\r
7285         wp.rcNormalPosition.bottom = commentY + commentH;\r
7286         SetWindowPlacement(hDlg, &wp);\r
7287 \r
7288         GetClientRect(hDlg, &rect);\r
7289         newSizeX = rect.right;\r
7290         newSizeY = rect.bottom;\r
7291         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7292                               newSizeX, newSizeY);\r
7293         sizeX = newSizeX;\r
7294         sizeY = newSizeY;\r
7295       }\r
7296     }\r
7297     return FALSE;\r
7298 \r
7299   case WM_COMMAND: /* message: received a command */\r
7300     switch (LOWORD(wParam)) {\r
7301     case IDOK:\r
7302       if (editComment) {\r
7303         char *p, *q;\r
7304         /* Read changed options from the dialog box */\r
7305         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7306         len = GetWindowTextLength(hwndText);\r
7307         str = (char *) malloc(len + 1);\r
7308         GetWindowText(hwndText, str, len + 1);\r
7309         p = q = str;\r
7310         while (*q) {\r
7311           if (*q == '\r')\r
7312             q++;\r
7313           else\r
7314             *p++ = *q++;\r
7315         }\r
7316         *p = NULLCHAR;\r
7317         ReplaceComment(commentIndex, str);\r
7318         free(str);\r
7319       }\r
7320       CommentPopDown();\r
7321       return TRUE;\r
7322 \r
7323     case IDCANCEL:\r
7324     case OPT_CancelComment:\r
7325       CommentPopDown();\r
7326       return TRUE;\r
7327 \r
7328     case OPT_ClearComment:\r
7329       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7330       break;\r
7331 \r
7332     case OPT_EditComment:\r
7333       EditCommentEvent();\r
7334       return TRUE;\r
7335 \r
7336     default:\r
7337       break;\r
7338     }\r
7339     break;\r
7340 \r
7341   case WM_SIZE:\r
7342     newSizeX = LOWORD(lParam);\r
7343     newSizeY = HIWORD(lParam);\r
7344     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7345     sizeX = newSizeX;\r
7346     sizeY = newSizeY;\r
7347     break;\r
7348 \r
7349   case WM_GETMINMAXINFO:\r
7350     /* Prevent resizing window too small */\r
7351     mmi = (MINMAXINFO *) lParam;\r
7352     mmi->ptMinTrackSize.x = 100;\r
7353     mmi->ptMinTrackSize.y = 100;\r
7354     break;\r
7355   }\r
7356   return FALSE;\r
7357 }\r
7358 \r
7359 VOID\r
7360 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7361 {\r
7362   FARPROC lpProc;\r
7363   char *p, *q;\r
7364 \r
7365   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7366 \r
7367   if (str == NULL) str = "";\r
7368   p = (char *) malloc(2 * strlen(str) + 2);\r
7369   q = p;\r
7370   while (*str) {\r
7371     if (*str == '\n') *q++ = '\r';\r
7372     *q++ = *str++;\r
7373   }\r
7374   *q = NULLCHAR;\r
7375   if (commentText != NULL) free(commentText);\r
7376 \r
7377   commentIndex = index;\r
7378   commentTitle = title;\r
7379   commentText = p;\r
7380   editComment = edit;\r
7381 \r
7382   if (commentDialog) {\r
7383     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7384     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7385   } else {\r
7386     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7387     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7388                  hwndMain, (DLGPROC)lpProc);\r
7389     FreeProcInstance(lpProc);\r
7390   }\r
7391   commentDialogUp = TRUE;\r
7392 }\r
7393 \r
7394 \r
7395 /*---------------------------------------------------------------------------*\\r
7396  *\r
7397  * Type-in move dialog functions\r
7398  * \r
7399 \*---------------------------------------------------------------------------*/\r
7400 \r
7401 LRESULT CALLBACK\r
7402 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7403 {\r
7404   char move[MSG_SIZ];\r
7405   HWND hInput;\r
7406   ChessMove moveType;\r
7407   int fromX, fromY, toX, toY;\r
7408   char promoChar;\r
7409 \r
7410   switch (message) {\r
7411   case WM_INITDIALOG:\r
7412     move[0] = (char) lParam;\r
7413     move[1] = NULLCHAR;\r
7414     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7415     hInput = GetDlgItem(hDlg, OPT_Move);\r
7416     SetWindowText(hInput, move);\r
7417     SetFocus(hInput);\r
7418     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7419     return FALSE;\r
7420 \r
7421   case WM_COMMAND:\r
7422     switch (LOWORD(wParam)) {\r
7423     case IDOK:\r
7424       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7425       { int n; Board board;\r
7426         // [HGM] FENedit\r
7427         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7428                 EditPositionPasteFEN(move);\r
7429                 EndDialog(hDlg, TRUE);\r
7430                 return TRUE;\r
7431         }\r
7432         // [HGM] movenum: allow move number to be typed in any mode\r
7433         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7434           currentMove = 2*n-1;\r
7435           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7436           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7437           EndDialog(hDlg, TRUE);\r
7438           DrawPosition(TRUE, boards[currentMove]);\r
7439           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7440           else DisplayMessage("", "");\r
7441           return TRUE;\r
7442         }\r
7443       }\r
7444       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7445         gameMode != Training) {\r
7446         DisplayMoveError("Displayed move is not current");\r
7447       } else {\r
7448 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7449         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7450           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7451         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7452         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7453           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7454           if (gameMode != Training)\r
7455               forwardMostMove = currentMove;\r
7456           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7457         } else {\r
7458           DisplayMoveError("Could not parse move");\r
7459         }\r
7460       }\r
7461       EndDialog(hDlg, TRUE);\r
7462       return TRUE;\r
7463     case IDCANCEL:\r
7464       EndDialog(hDlg, FALSE);\r
7465       return TRUE;\r
7466     default:\r
7467       break;\r
7468     }\r
7469     break;\r
7470   }\r
7471   return FALSE;\r
7472 }\r
7473 \r
7474 VOID\r
7475 PopUpMoveDialog(char firstchar)\r
7476 {\r
7477     FARPROC lpProc;\r
7478     \r
7479     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7480         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7481         gameMode == AnalyzeMode || gameMode == EditGame || \r
7482         gameMode == EditPosition || gameMode == IcsExamining ||\r
7483         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7484         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7485                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7486                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7487         gameMode == Training) {\r
7488       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7489       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7490         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7491       FreeProcInstance(lpProc);\r
7492     }\r
7493 }\r
7494 \r
7495 /*---------------------------------------------------------------------------*\\r
7496  *\r
7497  * Type-in name dialog functions\r
7498  * \r
7499 \*---------------------------------------------------------------------------*/\r
7500 \r
7501 LRESULT CALLBACK\r
7502 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7503 {\r
7504   char move[MSG_SIZ];\r
7505   HWND hInput;\r
7506 \r
7507   switch (message) {\r
7508   case WM_INITDIALOG:\r
7509     move[0] = (char) lParam;\r
7510     move[1] = NULLCHAR;\r
7511     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7512     hInput = GetDlgItem(hDlg, OPT_Name);\r
7513     SetWindowText(hInput, move);\r
7514     SetFocus(hInput);\r
7515     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7516     return FALSE;\r
7517 \r
7518   case WM_COMMAND:\r
7519     switch (LOWORD(wParam)) {\r
7520     case IDOK:\r
7521       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7522       appData.userName = strdup(move);\r
7523       SetUserLogo();\r
7524 \r
7525       EndDialog(hDlg, TRUE);\r
7526       return TRUE;\r
7527     case IDCANCEL:\r
7528       EndDialog(hDlg, FALSE);\r
7529       return TRUE;\r
7530     default:\r
7531       break;\r
7532     }\r
7533     break;\r
7534   }\r
7535   return FALSE;\r
7536 }\r
7537 \r
7538 VOID\r
7539 PopUpNameDialog(char firstchar)\r
7540 {\r
7541     FARPROC lpProc;\r
7542     \r
7543       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7544       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7545         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7546       FreeProcInstance(lpProc);\r
7547 }\r
7548 \r
7549 /*---------------------------------------------------------------------------*\\r
7550  *\r
7551  *  Error dialogs\r
7552  * \r
7553 \*---------------------------------------------------------------------------*/\r
7554 \r
7555 /* Nonmodal error box */\r
7556 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7557                              WPARAM wParam, LPARAM lParam);\r
7558 \r
7559 VOID\r
7560 ErrorPopUp(char *title, char *content)\r
7561 {\r
7562   FARPROC lpProc;\r
7563   char *p, *q;\r
7564   BOOLEAN modal = hwndMain == NULL;\r
7565 \r
7566   p = content;\r
7567   q = errorMessage;\r
7568   while (*p) {\r
7569     if (*p == '\n') {\r
7570       if (modal) {\r
7571         *q++ = ' ';\r
7572         p++;\r
7573       } else {\r
7574         *q++ = '\r';\r
7575         *q++ = *p++;\r
7576       }\r
7577     } else {\r
7578       *q++ = *p++;\r
7579     }\r
7580   }\r
7581   *q = NULLCHAR;\r
7582   strncpy(errorTitle, title, sizeof(errorTitle));\r
7583   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7584   \r
7585   if (modal) {\r
7586     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7587   } else {\r
7588     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7589     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7590                  hwndMain, (DLGPROC)lpProc);\r
7591     FreeProcInstance(lpProc);\r
7592   }\r
7593 }\r
7594 \r
7595 VOID\r
7596 ErrorPopDown()\r
7597 {\r
7598   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7599   if (errorDialog == NULL) return;\r
7600   DestroyWindow(errorDialog);\r
7601   errorDialog = NULL;\r
7602 }\r
7603 \r
7604 LRESULT CALLBACK\r
7605 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7606 {\r
7607   HANDLE hwndText;\r
7608   RECT rChild;\r
7609 \r
7610   switch (message) {\r
7611   case WM_INITDIALOG:\r
7612     GetWindowRect(hDlg, &rChild);\r
7613 \r
7614     /*\r
7615     SetWindowPos(hDlg, NULL, rChild.left,\r
7616       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7617       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7618     */\r
7619 \r
7620     /* \r
7621         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7622         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7623         and it doesn't work when you resize the dialog.\r
7624         For now, just give it a default position.\r
7625     */\r
7626     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7627 \r
7628     errorDialog = hDlg;\r
7629     SetWindowText(hDlg, errorTitle);\r
7630     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7631     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7632     return FALSE;\r
7633 \r
7634   case WM_COMMAND:\r
7635     switch (LOWORD(wParam)) {\r
7636     case IDOK:\r
7637     case IDCANCEL:\r
7638       if (errorDialog == hDlg) errorDialog = NULL;\r
7639       DestroyWindow(hDlg);\r
7640       return TRUE;\r
7641 \r
7642     default:\r
7643       break;\r
7644     }\r
7645     break;\r
7646   }\r
7647   return FALSE;\r
7648 }\r
7649 \r
7650 #ifdef GOTHIC\r
7651 HWND gothicDialog = NULL;\r
7652 \r
7653 LRESULT CALLBACK\r
7654 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7655 {\r
7656   HANDLE hwndText;\r
7657   RECT rChild;\r
7658   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7659 \r
7660   switch (message) {\r
7661   case WM_INITDIALOG:\r
7662     GetWindowRect(hDlg, &rChild);\r
7663 \r
7664     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7665                                                              SWP_NOZORDER);\r
7666 \r
7667     /* \r
7668         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7669         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7670         and it doesn't work when you resize the dialog.\r
7671         For now, just give it a default position.\r
7672     */\r
7673     gothicDialog = hDlg;\r
7674     SetWindowText(hDlg, errorTitle);\r
7675     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7676     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7677     return FALSE;\r
7678 \r
7679   case WM_COMMAND:\r
7680     switch (LOWORD(wParam)) {\r
7681     case IDOK:\r
7682     case IDCANCEL:\r
7683       if (errorDialog == hDlg) errorDialog = NULL;\r
7684       DestroyWindow(hDlg);\r
7685       return TRUE;\r
7686 \r
7687     default:\r
7688       break;\r
7689     }\r
7690     break;\r
7691   }\r
7692   return FALSE;\r
7693 }\r
7694 \r
7695 VOID\r
7696 GothicPopUp(char *title, VariantClass variant)\r
7697 {\r
7698   FARPROC lpProc;\r
7699   static char *lastTitle;\r
7700 \r
7701   strncpy(errorTitle, title, sizeof(errorTitle));\r
7702   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7703 \r
7704   if(lastTitle != title && gothicDialog != NULL) {\r
7705     DestroyWindow(gothicDialog);\r
7706     gothicDialog = NULL;\r
7707   }\r
7708   if(variant != VariantNormal && gothicDialog == NULL) {\r
7709     title = lastTitle;\r
7710     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7711     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7712                  hwndMain, (DLGPROC)lpProc);\r
7713     FreeProcInstance(lpProc);\r
7714   }\r
7715 }\r
7716 #endif\r
7717 \r
7718 /*---------------------------------------------------------------------------*\\r
7719  *\r
7720  *  Ics Interaction console functions\r
7721  *\r
7722 \*---------------------------------------------------------------------------*/\r
7723 \r
7724 #define HISTORY_SIZE 64\r
7725 static char *history[HISTORY_SIZE];\r
7726 int histIn = 0, histP = 0;\r
7727 \r
7728 VOID\r
7729 SaveInHistory(char *cmd)\r
7730 {\r
7731   if (history[histIn] != NULL) {\r
7732     free(history[histIn]);\r
7733     history[histIn] = NULL;\r
7734   }\r
7735   if (*cmd == NULLCHAR) return;\r
7736   history[histIn] = StrSave(cmd);\r
7737   histIn = (histIn + 1) % HISTORY_SIZE;\r
7738   if (history[histIn] != NULL) {\r
7739     free(history[histIn]);\r
7740     history[histIn] = NULL;\r
7741   }\r
7742   histP = histIn;\r
7743 }\r
7744 \r
7745 char *\r
7746 PrevInHistory(char *cmd)\r
7747 {\r
7748   int newhp;\r
7749   if (histP == histIn) {\r
7750     if (history[histIn] != NULL) free(history[histIn]);\r
7751     history[histIn] = StrSave(cmd);\r
7752   }\r
7753   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7754   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7755   histP = newhp;\r
7756   return history[histP];\r
7757 }\r
7758 \r
7759 char *\r
7760 NextInHistory()\r
7761 {\r
7762   if (histP == histIn) return NULL;\r
7763   histP = (histP + 1) % HISTORY_SIZE;\r
7764   return history[histP];\r
7765 }\r
7766 \r
7767 typedef struct {\r
7768   char *item;\r
7769   char *command;\r
7770   BOOLEAN getname;\r
7771   BOOLEAN immediate;\r
7772 } IcsTextMenuEntry;\r
7773 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7774 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7775 \r
7776 void\r
7777 ParseIcsTextMenu(char *icsTextMenuString)\r
7778 {\r
7779 //  int flags = 0;\r
7780   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7781   char *p = icsTextMenuString;\r
7782   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7783     free(e->item);\r
7784     e->item = NULL;\r
7785     if (e->command != NULL) {\r
7786       free(e->command);\r
7787       e->command = NULL;\r
7788     }\r
7789     e++;\r
7790   }\r
7791   e = icsTextMenuEntry;\r
7792   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7793     if (*p == ';' || *p == '\n') {\r
7794       e->item = strdup("-");\r
7795       e->command = NULL;\r
7796       p++;\r
7797     } else if (*p == '-') {\r
7798       e->item = strdup("-");\r
7799       e->command = NULL;\r
7800       p++;\r
7801       if (*p) p++;\r
7802     } else {\r
7803       char *q, *r, *s, *t;\r
7804       char c;\r
7805       q = strchr(p, ',');\r
7806       if (q == NULL) break;\r
7807       *q = NULLCHAR;\r
7808       r = strchr(q + 1, ',');\r
7809       if (r == NULL) break;\r
7810       *r = NULLCHAR;\r
7811       s = strchr(r + 1, ',');\r
7812       if (s == NULL) break;\r
7813       *s = NULLCHAR;\r
7814       c = ';';\r
7815       t = strchr(s + 1, c);\r
7816       if (t == NULL) {\r
7817         c = '\n';\r
7818         t = strchr(s + 1, c);\r
7819       }\r
7820       if (t != NULL) *t = NULLCHAR;\r
7821       e->item = strdup(p);\r
7822       e->command = strdup(q + 1);\r
7823       e->getname = *(r + 1) != '0';\r
7824       e->immediate = *(s + 1) != '0';\r
7825       *q = ',';\r
7826       *r = ',';\r
7827       *s = ',';\r
7828       if (t == NULL) break;\r
7829       *t = c;\r
7830       p = t + 1;\r
7831     }\r
7832     e++;\r
7833   } \r
7834 }\r
7835 \r
7836 HMENU\r
7837 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7838 {\r
7839   HMENU hmenu, h;\r
7840   int i = 0;\r
7841   hmenu = LoadMenu(hInst, "TextMenu");\r
7842   h = GetSubMenu(hmenu, 0);\r
7843   while (e->item) {\r
7844     if (strcmp(e->item, "-") == 0) {\r
7845       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7846     } else {\r
7847       if (e->item[0] == '|') {\r
7848         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7849                    IDM_CommandX + i, &e->item[1]);\r
7850       } else {\r
7851         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7852       }\r
7853     }\r
7854     e++;\r
7855     i++;\r
7856   } \r
7857   return hmenu;\r
7858 }\r
7859 \r
7860 WNDPROC consoleTextWindowProc;\r
7861 \r
7862 void\r
7863 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7864 {\r
7865   char buf[MSG_SIZ], name[MSG_SIZ];\r
7866   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7867   CHARRANGE sel;\r
7868 \r
7869   if (!getname) {\r
7870     SetWindowText(hInput, command);\r
7871     if (immediate) {\r
7872       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7873     } else {\r
7874       sel.cpMin = 999999;\r
7875       sel.cpMax = 999999;\r
7876       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7877       SetFocus(hInput);\r
7878     }\r
7879     return;\r
7880   }    \r
7881   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7882   if (sel.cpMin == sel.cpMax) {\r
7883     /* Expand to surrounding word */\r
7884     TEXTRANGE tr;\r
7885     do {\r
7886       tr.chrg.cpMax = sel.cpMin;\r
7887       tr.chrg.cpMin = --sel.cpMin;\r
7888       if (sel.cpMin < 0) break;\r
7889       tr.lpstrText = name;\r
7890       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7891     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7892     sel.cpMin++;\r
7893 \r
7894     do {\r
7895       tr.chrg.cpMin = sel.cpMax;\r
7896       tr.chrg.cpMax = ++sel.cpMax;\r
7897       tr.lpstrText = name;\r
7898       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7899     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7900     sel.cpMax--;\r
7901 \r
7902     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7903       MessageBeep(MB_ICONEXCLAMATION);\r
7904       return;\r
7905     }\r
7906     tr.chrg = sel;\r
7907     tr.lpstrText = name;\r
7908     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7909   } else {\r
7910     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7911       MessageBeep(MB_ICONEXCLAMATION);\r
7912       return;\r
7913     }\r
7914     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7915   }\r
7916   if (immediate) {\r
7917     sprintf(buf, "%s %s", command, name);\r
7918     SetWindowText(hInput, buf);\r
7919     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7920   } else {\r
7921     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7922     SetWindowText(hInput, buf);\r
7923     sel.cpMin = 999999;\r
7924     sel.cpMax = 999999;\r
7925     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7926     SetFocus(hInput);\r
7927   }\r
7928 }\r
7929 \r
7930 LRESULT CALLBACK \r
7931 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7932 {\r
7933   HWND hInput;\r
7934   CHARRANGE sel;\r
7935 \r
7936   switch (message) {\r
7937   case WM_KEYDOWN:\r
7938     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7939     switch (wParam) {\r
7940     case VK_PRIOR:\r
7941       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7942       return 0;\r
7943     case VK_NEXT:\r
7944       sel.cpMin = 999999;\r
7945       sel.cpMax = 999999;\r
7946       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7947       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7948       return 0;\r
7949     }\r
7950     break;\r
7951   case WM_CHAR:\r
7952    if(wParam != '\022') {\r
7953     if (wParam == '\t') {\r
7954       if (GetKeyState(VK_SHIFT) < 0) {\r
7955         /* shifted */\r
7956         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7957         if (buttonDesc[0].hwnd) {\r
7958           SetFocus(buttonDesc[0].hwnd);\r
7959         } else {\r
7960           SetFocus(hwndMain);\r
7961         }\r
7962       } else {\r
7963         /* unshifted */\r
7964         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7965       }\r
7966     } else {\r
7967       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7968       JAWS_DELETE( SetFocus(hInput); )\r
7969       SendMessage(hInput, message, wParam, lParam);\r
7970     }\r
7971     return 0;\r
7972    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7973   case WM_RBUTTONUP:\r
7974     if (GetKeyState(VK_SHIFT) & ~1) {\r
7975       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7976         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7977     } else {\r
7978       POINT pt;\r
7979       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7980       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7981       if (sel.cpMin == sel.cpMax) {\r
7982         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7983         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7984       }\r
7985       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7986         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7987       }\r
7988       pt.x = LOWORD(lParam);\r
7989       pt.y = HIWORD(lParam);\r
7990       MenuPopup(hwnd, pt, hmenu, -1);\r
7991     }\r
7992     return 0;\r
7993   case WM_PASTE:\r
7994     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7995     SetFocus(hInput);\r
7996     return SendMessage(hInput, message, wParam, lParam);\r
7997   case WM_MBUTTONDOWN:\r
7998     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7999   case WM_RBUTTONDOWN:\r
8000     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
8001       /* Move selection here if it was empty */\r
8002       POINT pt;\r
8003       pt.x = LOWORD(lParam);\r
8004       pt.y = HIWORD(lParam);\r
8005       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8006       if (sel.cpMin == sel.cpMax) {\r
8007         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
8008         sel.cpMax = sel.cpMin;\r
8009         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8010       }\r
8011       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
8012     }\r
8013     return 0;\r
8014   case WM_COMMAND:\r
8015     switch (LOWORD(wParam)) {\r
8016     case IDM_QuickPaste:\r
8017       {\r
8018         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8019         if (sel.cpMin == sel.cpMax) {\r
8020           MessageBeep(MB_ICONEXCLAMATION);\r
8021           return 0;\r
8022         }\r
8023         SendMessage(hwnd, WM_COPY, 0, 0);\r
8024         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8025         SendMessage(hInput, WM_PASTE, 0, 0);\r
8026         SetFocus(hInput);\r
8027         return 0;\r
8028       }\r
8029     case IDM_Cut:\r
8030       SendMessage(hwnd, WM_CUT, 0, 0);\r
8031       return 0;\r
8032     case IDM_Paste:\r
8033       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8034       return 0;\r
8035     case IDM_Copy:\r
8036       SendMessage(hwnd, WM_COPY, 0, 0);\r
8037       return 0;\r
8038     default:\r
8039       {\r
8040         int i = LOWORD(wParam) - IDM_CommandX;\r
8041         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
8042             icsTextMenuEntry[i].command != NULL) {\r
8043           CommandX(hwnd, icsTextMenuEntry[i].command,\r
8044                    icsTextMenuEntry[i].getname,\r
8045                    icsTextMenuEntry[i].immediate);\r
8046           return 0;\r
8047         }\r
8048       }\r
8049       break;\r
8050     }\r
8051     break;\r
8052   }\r
8053   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
8054 }\r
8055 \r
8056 WNDPROC consoleInputWindowProc;\r
8057 \r
8058 LRESULT CALLBACK\r
8059 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8060 {\r
8061   char buf[MSG_SIZ];\r
8062   char *p;\r
8063   static BOOL sendNextChar = FALSE;\r
8064   static BOOL quoteNextChar = FALSE;\r
8065   InputSource *is = consoleInputSource;\r
8066   CHARFORMAT cf;\r
8067   CHARRANGE sel;\r
8068 \r
8069   switch (message) {\r
8070   case WM_CHAR:\r
8071     if (!appData.localLineEditing || sendNextChar) {\r
8072       is->buf[0] = (CHAR) wParam;\r
8073       is->count = 1;\r
8074       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8075       sendNextChar = FALSE;\r
8076       return 0;\r
8077     }\r
8078     if (quoteNextChar) {\r
8079       buf[0] = (char) wParam;\r
8080       buf[1] = NULLCHAR;\r
8081       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
8082       quoteNextChar = FALSE;\r
8083       return 0;\r
8084     }\r
8085     switch (wParam) {\r
8086     case '\r':   /* Enter key */\r
8087       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
8088       if (consoleEcho) SaveInHistory(is->buf);\r
8089       is->buf[is->count++] = '\n';\r
8090       is->buf[is->count] = NULLCHAR;\r
8091       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8092       if (consoleEcho) {\r
8093         ConsoleOutput(is->buf, is->count, TRUE);\r
8094       } else if (appData.localLineEditing) {\r
8095         ConsoleOutput("\n", 1, TRUE);\r
8096       }\r
8097       /* fall thru */\r
8098     case '\033': /* Escape key */\r
8099       SetWindowText(hwnd, "");\r
8100       cf.cbSize = sizeof(CHARFORMAT);\r
8101       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8102       if (consoleEcho) {\r
8103         cf.crTextColor = textAttribs[ColorNormal].color;\r
8104       } else {\r
8105         cf.crTextColor = COLOR_ECHOOFF;\r
8106       }\r
8107       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8108       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8109       return 0;\r
8110     case '\t':   /* Tab key */\r
8111       if (GetKeyState(VK_SHIFT) < 0) {\r
8112         /* shifted */\r
8113         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8114       } else {\r
8115         /* unshifted */\r
8116         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8117         if (buttonDesc[0].hwnd) {\r
8118           SetFocus(buttonDesc[0].hwnd);\r
8119         } else {\r
8120           SetFocus(hwndMain);\r
8121         }\r
8122       }\r
8123       return 0;\r
8124     case '\023': /* Ctrl+S */\r
8125       sendNextChar = TRUE;\r
8126       return 0;\r
8127     case '\021': /* Ctrl+Q */\r
8128       quoteNextChar = TRUE;\r
8129       return 0;\r
8130     JAWS_REPLAY\r
8131     default:\r
8132       break;\r
8133     }\r
8134     break;\r
8135   case WM_KEYDOWN:\r
8136     switch (wParam) {\r
8137     case VK_UP:\r
8138       GetWindowText(hwnd, buf, MSG_SIZ);\r
8139       p = PrevInHistory(buf);\r
8140       if (p != NULL) {\r
8141         SetWindowText(hwnd, p);\r
8142         sel.cpMin = 999999;\r
8143         sel.cpMax = 999999;\r
8144         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8145         return 0;\r
8146       }\r
8147       break;\r
8148     case VK_DOWN:\r
8149       p = NextInHistory();\r
8150       if (p != NULL) {\r
8151         SetWindowText(hwnd, p);\r
8152         sel.cpMin = 999999;\r
8153         sel.cpMax = 999999;\r
8154         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8155         return 0;\r
8156       }\r
8157       break;\r
8158     case VK_HOME:\r
8159     case VK_END:\r
8160       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8161       /* fall thru */\r
8162     case VK_PRIOR:\r
8163     case VK_NEXT:\r
8164       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8165       return 0;\r
8166     }\r
8167     break;\r
8168   case WM_MBUTTONDOWN:\r
8169     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8170       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8171     break;\r
8172   case WM_RBUTTONUP:\r
8173     if (GetKeyState(VK_SHIFT) & ~1) {\r
8174       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8175         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8176     } else {\r
8177       POINT pt;\r
8178       HMENU hmenu;\r
8179       hmenu = LoadMenu(hInst, "InputMenu");\r
8180       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8181       if (sel.cpMin == sel.cpMax) {\r
8182         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8183         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8184       }\r
8185       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8186         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8187       }\r
8188       pt.x = LOWORD(lParam);\r
8189       pt.y = HIWORD(lParam);\r
8190       MenuPopup(hwnd, pt, hmenu, -1);\r
8191     }\r
8192     return 0;\r
8193   case WM_COMMAND:\r
8194     switch (LOWORD(wParam)) { \r
8195     case IDM_Undo:\r
8196       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8197       return 0;\r
8198     case IDM_SelectAll:\r
8199       sel.cpMin = 0;\r
8200       sel.cpMax = -1; /*999999?*/\r
8201       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8202       return 0;\r
8203     case IDM_Cut:\r
8204       SendMessage(hwnd, WM_CUT, 0, 0);\r
8205       return 0;\r
8206     case IDM_Paste:\r
8207       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8208       return 0;\r
8209     case IDM_Copy:\r
8210       SendMessage(hwnd, WM_COPY, 0, 0);\r
8211       return 0;\r
8212     }\r
8213     break;\r
8214   }\r
8215   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8216 }\r
8217 \r
8218 #define CO_MAX  100000\r
8219 #define CO_TRIM   1000\r
8220 \r
8221 LRESULT CALLBACK\r
8222 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8223 {\r
8224   static SnapData sd;\r
8225   static HWND hText, hInput /*, hFocus*/;\r
8226 //  InputSource *is = consoleInputSource;\r
8227   RECT rect;\r
8228   static int sizeX, sizeY;\r
8229   int newSizeX, newSizeY;\r
8230   MINMAXINFO *mmi;\r
8231 \r
8232   switch (message) {\r
8233   case WM_INITDIALOG: /* message: initialize dialog box */\r
8234     hwndConsole = hDlg;\r
8235     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8236     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8237     SetFocus(hInput);\r
8238     consoleTextWindowProc = (WNDPROC)\r
8239       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8240     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8241     consoleInputWindowProc = (WNDPROC)\r
8242       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8243     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8244     Colorize(ColorNormal, TRUE);\r
8245     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8246     ChangedConsoleFont();\r
8247     GetClientRect(hDlg, &rect);\r
8248     sizeX = rect.right;\r
8249     sizeY = rect.bottom;\r
8250     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8251         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8252       WINDOWPLACEMENT wp;\r
8253       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8254       wp.length = sizeof(WINDOWPLACEMENT);\r
8255       wp.flags = 0;\r
8256       wp.showCmd = SW_SHOW;\r
8257       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8258       wp.rcNormalPosition.left = wpConsole.x;\r
8259       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8260       wp.rcNormalPosition.top = wpConsole.y;\r
8261       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8262       SetWindowPlacement(hDlg, &wp);\r
8263     }\r
8264 #if 1\r
8265    // [HGM] Chessknight's change 2004-07-13\r
8266    else { /* Determine Defaults */\r
8267        WINDOWPLACEMENT wp;\r
8268        wpConsole.x = winWidth + 1;\r
8269        wpConsole.y = boardY;\r
8270        wpConsole.width = screenWidth -  winWidth;\r
8271        wpConsole.height = winHeight;\r
8272        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8273        wp.length = sizeof(WINDOWPLACEMENT);\r
8274        wp.flags = 0;\r
8275        wp.showCmd = SW_SHOW;\r
8276        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8277        wp.rcNormalPosition.left = wpConsole.x;\r
8278        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8279        wp.rcNormalPosition.top = wpConsole.y;\r
8280        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8281        SetWindowPlacement(hDlg, &wp);\r
8282     }\r
8283 #endif\r
8284     return FALSE;\r
8285 \r
8286   case WM_SETFOCUS:\r
8287     SetFocus(hInput);\r
8288     return 0;\r
8289 \r
8290   case WM_CLOSE:\r
8291     ExitEvent(0);\r
8292     /* not reached */\r
8293     break;\r
8294 \r
8295   case WM_SIZE:\r
8296     if (IsIconic(hDlg)) break;\r
8297     newSizeX = LOWORD(lParam);\r
8298     newSizeY = HIWORD(lParam);\r
8299     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8300       RECT rectText, rectInput;\r
8301       POINT pt;\r
8302       int newTextHeight, newTextWidth;\r
8303       GetWindowRect(hText, &rectText);\r
8304       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8305       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8306       if (newTextHeight < 0) {\r
8307         newSizeY += -newTextHeight;\r
8308         newTextHeight = 0;\r
8309       }\r
8310       SetWindowPos(hText, NULL, 0, 0,\r
8311         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8312       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8313       pt.x = rectInput.left;\r
8314       pt.y = rectInput.top + newSizeY - sizeY;\r
8315       ScreenToClient(hDlg, &pt);\r
8316       SetWindowPos(hInput, NULL, \r
8317         pt.x, pt.y, /* needs client coords */   \r
8318         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8319         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8320     }\r
8321     sizeX = newSizeX;\r
8322     sizeY = newSizeY;\r
8323     break;\r
8324 \r
8325   case WM_GETMINMAXINFO:\r
8326     /* Prevent resizing window too small */\r
8327     mmi = (MINMAXINFO *) lParam;\r
8328     mmi->ptMinTrackSize.x = 100;\r
8329     mmi->ptMinTrackSize.y = 100;\r
8330     break;\r
8331 \r
8332   /* [AS] Snapping */\r
8333   case WM_ENTERSIZEMOVE:\r
8334     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8335 \r
8336   case WM_SIZING:\r
8337     return OnSizing( &sd, hDlg, wParam, lParam );\r
8338 \r
8339   case WM_MOVING:\r
8340     return OnMoving( &sd, hDlg, wParam, lParam );\r
8341 \r
8342   case WM_EXITSIZEMOVE:\r
8343     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8344   }\r
8345 \r
8346   return DefWindowProc(hDlg, message, wParam, lParam);\r
8347 }\r
8348 \r
8349 \r
8350 VOID\r
8351 ConsoleCreate()\r
8352 {\r
8353   HWND hCons;\r
8354   if (hwndConsole) return;\r
8355   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8356   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8357 }\r
8358 \r
8359 \r
8360 VOID\r
8361 ConsoleOutput(char* data, int length, int forceVisible)\r
8362 {\r
8363   HWND hText;\r
8364   int trim, exlen;\r
8365   char *p, *q;\r
8366   char buf[CO_MAX+1];\r
8367   POINT pEnd;\r
8368   RECT rect;\r
8369   static int delayLF = 0;\r
8370   CHARRANGE savesel, sel;\r
8371 \r
8372   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8373   p = data;\r
8374   q = buf;\r
8375   if (delayLF) {\r
8376     *q++ = '\r';\r
8377     *q++ = '\n';\r
8378     delayLF = 0;\r
8379   }\r
8380   while (length--) {\r
8381     if (*p == '\n') {\r
8382       if (*++p) {\r
8383         *q++ = '\r';\r
8384         *q++ = '\n';\r
8385       } else {\r
8386         delayLF = 1;\r
8387       }\r
8388     } else if (*p == '\007') {\r
8389        MyPlaySound(&sounds[(int)SoundBell]);\r
8390        p++;\r
8391     } else {\r
8392       *q++ = *p++;\r
8393     }\r
8394   }\r
8395   *q = NULLCHAR;\r
8396   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8397   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8398   /* Save current selection */\r
8399   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8400   exlen = GetWindowTextLength(hText);\r
8401   /* Find out whether current end of text is visible */\r
8402   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8403   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8404   /* Trim existing text if it's too long */\r
8405   if (exlen + (q - buf) > CO_MAX) {\r
8406     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8407     sel.cpMin = 0;\r
8408     sel.cpMax = trim;\r
8409     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8410     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8411     exlen -= trim;\r
8412     savesel.cpMin -= trim;\r
8413     savesel.cpMax -= trim;\r
8414     if (exlen < 0) exlen = 0;\r
8415     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8416     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8417   }\r
8418   /* Append the new text */\r
8419   sel.cpMin = exlen;\r
8420   sel.cpMax = exlen;\r
8421   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8422   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8423   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8424   if (forceVisible || exlen == 0 ||\r
8425       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8426        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8427     /* Scroll to make new end of text visible if old end of text\r
8428        was visible or new text is an echo of user typein */\r
8429     sel.cpMin = 9999999;\r
8430     sel.cpMax = 9999999;\r
8431     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8432     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8433     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8434     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8435   }\r
8436   if (savesel.cpMax == exlen || forceVisible) {\r
8437     /* Move insert point to new end of text if it was at the old\r
8438        end of text or if the new text is an echo of user typein */\r
8439     sel.cpMin = 9999999;\r
8440     sel.cpMax = 9999999;\r
8441     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8442   } else {\r
8443     /* Restore previous selection */\r
8444     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8445   }\r
8446   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8447 }\r
8448 \r
8449 /*---------*/\r
8450 \r
8451 \r
8452 void\r
8453 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8454 {\r
8455   char buf[100];\r
8456   char *str;\r
8457   COLORREF oldFg, oldBg;\r
8458   HFONT oldFont;\r
8459   RECT rect;\r
8460 \r
8461   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8462 \r
8463   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8464   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8465   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8466 \r
8467   rect.left = x;\r
8468   rect.right = x + squareSize;\r
8469   rect.top  = y;\r
8470   rect.bottom = y + squareSize;\r
8471   str = buf;\r
8472 \r
8473   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8474                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8475              y, ETO_CLIPPED|ETO_OPAQUE,\r
8476              &rect, str, strlen(str), NULL);\r
8477 \r
8478   (void) SetTextColor(hdc, oldFg);\r
8479   (void) SetBkColor(hdc, oldBg);\r
8480   (void) SelectObject(hdc, oldFont);\r
8481 }\r
8482 \r
8483 void\r
8484 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8485               RECT *rect, char *color, char *flagFell)\r
8486 {\r
8487   char buf[100];\r
8488   char *str;\r
8489   COLORREF oldFg, oldBg;\r
8490   HFONT oldFont;\r
8491 \r
8492   if (appData.clockMode) {\r
8493     if (tinyLayout)\r
8494       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8495     else\r
8496       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8497     str = buf;\r
8498   } else {\r
8499     str = color;\r
8500   }\r
8501 \r
8502   if (highlight) {\r
8503     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8504     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8505   } else {\r
8506     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8507     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8508   }\r
8509   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8510 \r
8511   JAWS_SILENCE\r
8512 \r
8513   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8514              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8515              rect, str, strlen(str), NULL);\r
8516   if(logoHeight > 0 && appData.clockMode) {\r
8517       RECT r;\r
8518       sprintf(buf, "%s %s", buf+7, flagFell);\r
8519       r.top = rect->top + logoHeight/2;\r
8520       r.left = rect->left;\r
8521       r.right = rect->right;\r
8522       r.bottom = rect->bottom;\r
8523       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8524                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8525                  &r, str, strlen(str), NULL);\r
8526   }\r
8527   (void) SetTextColor(hdc, oldFg);\r
8528   (void) SetBkColor(hdc, oldBg);\r
8529   (void) SelectObject(hdc, oldFont);\r
8530 }\r
8531 \r
8532 \r
8533 int\r
8534 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8535            OVERLAPPED *ovl)\r
8536 {\r
8537   int ok, err;\r
8538 \r
8539   /* [AS]  */\r
8540   if( count <= 0 ) {\r
8541     if (appData.debugMode) {\r
8542       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8543     }\r
8544 \r
8545     return ERROR_INVALID_USER_BUFFER;\r
8546   }\r
8547 \r
8548   ResetEvent(ovl->hEvent);\r
8549   ovl->Offset = ovl->OffsetHigh = 0;\r
8550   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8551   if (ok) {\r
8552     err = NO_ERROR;\r
8553   } else {\r
8554     err = GetLastError();\r
8555     if (err == ERROR_IO_PENDING) {\r
8556       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8557       if (ok)\r
8558         err = NO_ERROR;\r
8559       else\r
8560         err = GetLastError();\r
8561     }\r
8562   }\r
8563   return err;\r
8564 }\r
8565 \r
8566 int\r
8567 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8568             OVERLAPPED *ovl)\r
8569 {\r
8570   int ok, err;\r
8571 \r
8572   ResetEvent(ovl->hEvent);\r
8573   ovl->Offset = ovl->OffsetHigh = 0;\r
8574   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8575   if (ok) {\r
8576     err = NO_ERROR;\r
8577   } else {\r
8578     err = GetLastError();\r
8579     if (err == ERROR_IO_PENDING) {\r
8580       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8581       if (ok)\r
8582         err = NO_ERROR;\r
8583       else\r
8584         err = GetLastError();\r
8585     }\r
8586   }\r
8587   return err;\r
8588 }\r
8589 \r
8590 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8591 void CheckForInputBufferFull( InputSource * is )\r
8592 {\r
8593     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8594         /* Look for end of line */\r
8595         char * p = is->buf;\r
8596         \r
8597         while( p < is->next && *p != '\n' ) {\r
8598             p++;\r
8599         }\r
8600 \r
8601         if( p >= is->next ) {\r
8602             if (appData.debugMode) {\r
8603                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8604             }\r
8605 \r
8606             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8607             is->count = (DWORD) -1;\r
8608             is->next = is->buf;\r
8609         }\r
8610     }\r
8611 }\r
8612 \r
8613 DWORD\r
8614 InputThread(LPVOID arg)\r
8615 {\r
8616   InputSource *is;\r
8617   OVERLAPPED ovl;\r
8618 \r
8619   is = (InputSource *) arg;\r
8620   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8621   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8622   while (is->hThread != NULL) {\r
8623     is->error = DoReadFile(is->hFile, is->next,\r
8624                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8625                            &is->count, &ovl);\r
8626     if (is->error == NO_ERROR) {\r
8627       is->next += is->count;\r
8628     } else {\r
8629       if (is->error == ERROR_BROKEN_PIPE) {\r
8630         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8631         is->count = 0;\r
8632       } else {\r
8633         is->count = (DWORD) -1;\r
8634         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8635         break; \r
8636       }\r
8637     }\r
8638 \r
8639     CheckForInputBufferFull( is );\r
8640 \r
8641     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8642 \r
8643     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8644 \r
8645     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8646   }\r
8647 \r
8648   CloseHandle(ovl.hEvent);\r
8649   CloseHandle(is->hFile);\r
8650 \r
8651   if (appData.debugMode) {\r
8652     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8653   }\r
8654 \r
8655   return 0;\r
8656 }\r
8657 \r
8658 \r
8659 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8660 DWORD\r
8661 NonOvlInputThread(LPVOID arg)\r
8662 {\r
8663   InputSource *is;\r
8664   char *p, *q;\r
8665   int i;\r
8666   char prev;\r
8667 \r
8668   is = (InputSource *) arg;\r
8669   while (is->hThread != NULL) {\r
8670     is->error = ReadFile(is->hFile, is->next,\r
8671                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8672                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8673     if (is->error == NO_ERROR) {\r
8674       /* Change CRLF to LF */\r
8675       if (is->next > is->buf) {\r
8676         p = is->next - 1;\r
8677         i = is->count + 1;\r
8678       } else {\r
8679         p = is->next;\r
8680         i = is->count;\r
8681       }\r
8682       q = p;\r
8683       prev = NULLCHAR;\r
8684       while (i > 0) {\r
8685         if (prev == '\r' && *p == '\n') {\r
8686           *(q-1) = '\n';\r
8687           is->count--;\r
8688         } else { \r
8689           *q++ = *p;\r
8690         }\r
8691         prev = *p++;\r
8692         i--;\r
8693       }\r
8694       *q = NULLCHAR;\r
8695       is->next = q;\r
8696     } else {\r
8697       if (is->error == ERROR_BROKEN_PIPE) {\r
8698         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8699         is->count = 0; \r
8700       } else {\r
8701         is->count = (DWORD) -1;\r
8702       }\r
8703     }\r
8704 \r
8705     CheckForInputBufferFull( is );\r
8706 \r
8707     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8708 \r
8709     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8710 \r
8711     if (is->count < 0) break;  /* Quit on error */\r
8712   }\r
8713   CloseHandle(is->hFile);\r
8714   return 0;\r
8715 }\r
8716 \r
8717 DWORD\r
8718 SocketInputThread(LPVOID arg)\r
8719 {\r
8720   InputSource *is;\r
8721 \r
8722   is = (InputSource *) arg;\r
8723   while (is->hThread != NULL) {\r
8724     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8725     if ((int)is->count == SOCKET_ERROR) {\r
8726       is->count = (DWORD) -1;\r
8727       is->error = WSAGetLastError();\r
8728     } else {\r
8729       is->error = NO_ERROR;\r
8730       is->next += is->count;\r
8731       if (is->count == 0 && is->second == is) {\r
8732         /* End of file on stderr; quit with no message */\r
8733         break;\r
8734       }\r
8735     }\r
8736     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8737 \r
8738     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8739 \r
8740     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8741   }\r
8742   return 0;\r
8743 }\r
8744 \r
8745 VOID\r
8746 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8747 {\r
8748   InputSource *is;\r
8749 \r
8750   is = (InputSource *) lParam;\r
8751   if (is->lineByLine) {\r
8752     /* Feed in lines one by one */\r
8753     char *p = is->buf;\r
8754     char *q = p;\r
8755     while (q < is->next) {\r
8756       if (*q++ == '\n') {\r
8757         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8758         p = q;\r
8759       }\r
8760     }\r
8761     \r
8762     /* Move any partial line to the start of the buffer */\r
8763     q = is->buf;\r
8764     while (p < is->next) {\r
8765       *q++ = *p++;\r
8766     }\r
8767     is->next = q;\r
8768 \r
8769     if (is->error != NO_ERROR || is->count == 0) {\r
8770       /* Notify backend of the error.  Note: If there was a partial\r
8771          line at the end, it is not flushed through. */\r
8772       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8773     }\r
8774   } else {\r
8775     /* Feed in the whole chunk of input at once */\r
8776     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8777     is->next = is->buf;\r
8778   }\r
8779 }\r
8780 \r
8781 /*---------------------------------------------------------------------------*\\r
8782  *\r
8783  *  Menu enables. Used when setting various modes.\r
8784  *\r
8785 \*---------------------------------------------------------------------------*/\r
8786 \r
8787 typedef struct {\r
8788   int item;\r
8789   int flags;\r
8790 } Enables;\r
8791 \r
8792 VOID\r
8793 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8794 {\r
8795   while (enab->item > 0) {\r
8796     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8797     enab++;\r
8798   }\r
8799 }\r
8800 \r
8801 Enables gnuEnables[] = {\r
8802   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8803   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8804   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8805   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8806   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8807   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8808   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8809   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8810   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8811   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8812   { -1, -1 }\r
8813 };\r
8814 \r
8815 Enables icsEnables[] = {\r
8816   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8817   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8818   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8819   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8820   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8821   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8822   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8823   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8824   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8825   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8826   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8827   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8828   { -1, -1 }\r
8829 };\r
8830 \r
8831 #ifdef ZIPPY\r
8832 Enables zippyEnables[] = {\r
8833   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8834   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8835   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8836   { -1, -1 }\r
8837 };\r
8838 #endif\r
8839 \r
8840 Enables ncpEnables[] = {\r
8841   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8842   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8843   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8844   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8845   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8846   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8847   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8848   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8849   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8850   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8851   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8852   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8853   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8854   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8855   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8856   { -1, -1 }\r
8857 };\r
8858 \r
8859 Enables trainingOnEnables[] = {\r
8860   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8861   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8862   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8863   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8864   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8865   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8866   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8867   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8868   { -1, -1 }\r
8869 };\r
8870 \r
8871 Enables trainingOffEnables[] = {\r
8872   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8873   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8874   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8875   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8876   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8877   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8878   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8879   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8880   { -1, -1 }\r
8881 };\r
8882 \r
8883 /* These modify either ncpEnables or gnuEnables */\r
8884 Enables cmailEnables[] = {\r
8885   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8886   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8887   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8888   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8889   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8890   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8891   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8892   { -1, -1 }\r
8893 };\r
8894 \r
8895 Enables machineThinkingEnables[] = {\r
8896   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8897   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8898   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8899   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8900   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8901   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8902   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8903   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8904   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8905   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8906   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8907   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8908   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8909   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8910   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8911   { -1, -1 }\r
8912 };\r
8913 \r
8914 Enables userThinkingEnables[] = {\r
8915   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8916   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8917   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8918   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8919   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8920   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8921   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8922   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8923   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8924   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8925   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8926   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8927   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8928   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8929   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8930   { -1, -1 }\r
8931 };\r
8932 \r
8933 /*---------------------------------------------------------------------------*\\r
8934  *\r
8935  *  Front-end interface functions exported by XBoard.\r
8936  *  Functions appear in same order as prototypes in frontend.h.\r
8937  * \r
8938 \*---------------------------------------------------------------------------*/\r
8939 VOID\r
8940 ModeHighlight()\r
8941 {\r
8942   static UINT prevChecked = 0;\r
8943   static int prevPausing = 0;\r
8944   UINT nowChecked;\r
8945 \r
8946   if (pausing != prevPausing) {\r
8947     prevPausing = pausing;\r
8948     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8949                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8950     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8951   }\r
8952 \r
8953   switch (gameMode) {\r
8954   case BeginningOfGame:\r
8955     if (appData.icsActive)\r
8956       nowChecked = IDM_IcsClient;\r
8957     else if (appData.noChessProgram)\r
8958       nowChecked = IDM_EditGame;\r
8959     else\r
8960       nowChecked = IDM_MachineBlack;\r
8961     break;\r
8962   case MachinePlaysBlack:\r
8963     nowChecked = IDM_MachineBlack;\r
8964     break;\r
8965   case MachinePlaysWhite:\r
8966     nowChecked = IDM_MachineWhite;\r
8967     break;\r
8968   case TwoMachinesPlay:\r
8969     nowChecked = IDM_TwoMachines;\r
8970     break;\r
8971   case AnalyzeMode:\r
8972     nowChecked = IDM_AnalysisMode;\r
8973     break;\r
8974   case AnalyzeFile:\r
8975     nowChecked = IDM_AnalyzeFile;\r
8976     break;\r
8977   case EditGame:\r
8978     nowChecked = IDM_EditGame;\r
8979     break;\r
8980   case PlayFromGameFile:\r
8981     nowChecked = IDM_LoadGame;\r
8982     break;\r
8983   case EditPosition:\r
8984     nowChecked = IDM_EditPosition;\r
8985     break;\r
8986   case Training:\r
8987     nowChecked = IDM_Training;\r
8988     break;\r
8989   case IcsPlayingWhite:\r
8990   case IcsPlayingBlack:\r
8991   case IcsObserving:\r
8992   case IcsIdle:\r
8993     nowChecked = IDM_IcsClient;\r
8994     break;\r
8995   default:\r
8996   case EndOfGame:\r
8997     nowChecked = 0;\r
8998     break;\r
8999   }\r
9000   if (prevChecked != 0)\r
9001     (void) CheckMenuItem(GetMenu(hwndMain),\r
9002                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
9003   if (nowChecked != 0)\r
9004     (void) CheckMenuItem(GetMenu(hwndMain),\r
9005                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
9006 \r
9007   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
9008     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
9009                           MF_BYCOMMAND|MF_ENABLED);\r
9010   } else {\r
9011     (void) EnableMenuItem(GetMenu(hwndMain), \r
9012                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
9013   }\r
9014 \r
9015   prevChecked = nowChecked;\r
9016 \r
9017   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
9018   if (appData.icsActive) {\r
9019        if (appData.icsEngineAnalyze) {\r
9020                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9021                        MF_BYCOMMAND|MF_CHECKED);\r
9022        } else {\r
9023                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9024                        MF_BYCOMMAND|MF_UNCHECKED);\r
9025        }\r
9026   }\r
9027 }\r
9028 \r
9029 VOID\r
9030 SetICSMode()\r
9031 {\r
9032   HMENU hmenu = GetMenu(hwndMain);\r
9033   SetMenuEnables(hmenu, icsEnables);\r
9034   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
9035     MF_BYPOSITION|MF_ENABLED);\r
9036 #ifdef ZIPPY\r
9037   if (appData.zippyPlay) {\r
9038     SetMenuEnables(hmenu, zippyEnables);\r
9039     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
9040          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9041           MF_BYCOMMAND|MF_ENABLED);\r
9042   }\r
9043 #endif\r
9044 }\r
9045 \r
9046 VOID\r
9047 SetGNUMode()\r
9048 {\r
9049   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
9050 }\r
9051 \r
9052 VOID\r
9053 SetNCPMode()\r
9054 {\r
9055   HMENU hmenu = GetMenu(hwndMain);\r
9056   SetMenuEnables(hmenu, ncpEnables);\r
9057   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
9058     MF_BYPOSITION|MF_GRAYED);\r
9059     DrawMenuBar(hwndMain);\r
9060 }\r
9061 \r
9062 VOID\r
9063 SetCmailMode()\r
9064 {\r
9065   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9066 }\r
9067 \r
9068 VOID \r
9069 SetTrainingModeOn()\r
9070 {\r
9071   int i;\r
9072   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9073   for (i = 0; i < N_BUTTONS; i++) {\r
9074     if (buttonDesc[i].hwnd != NULL)\r
9075       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9076   }\r
9077   CommentPopDown();\r
9078 }\r
9079 \r
9080 VOID SetTrainingModeOff()\r
9081 {\r
9082   int i;\r
9083   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9084   for (i = 0; i < N_BUTTONS; i++) {\r
9085     if (buttonDesc[i].hwnd != NULL)\r
9086       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9087   }\r
9088 }\r
9089 \r
9090 \r
9091 VOID\r
9092 SetUserThinkingEnables()\r
9093 {\r
9094   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9095 }\r
9096 \r
9097 VOID\r
9098 SetMachineThinkingEnables()\r
9099 {\r
9100   HMENU hMenu = GetMenu(hwndMain);\r
9101   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9102 \r
9103   SetMenuEnables(hMenu, machineThinkingEnables);\r
9104 \r
9105   if (gameMode == MachinePlaysBlack) {\r
9106     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9107   } else if (gameMode == MachinePlaysWhite) {\r
9108     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9109   } else if (gameMode == TwoMachinesPlay) {\r
9110     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9111   }\r
9112 }\r
9113 \r
9114 \r
9115 VOID\r
9116 DisplayTitle(char *str)\r
9117 {\r
9118   char title[MSG_SIZ], *host;\r
9119   if (str[0] != NULLCHAR) {\r
9120     strcpy(title, str);\r
9121   } else if (appData.icsActive) {\r
9122     if (appData.icsCommPort[0] != NULLCHAR)\r
9123       host = "ICS";\r
9124     else \r
9125       host = appData.icsHost;\r
9126     sprintf(title, "%s: %s", szTitle, host);\r
9127   } else if (appData.noChessProgram) {\r
9128     strcpy(title, szTitle);\r
9129   } else {\r
9130     strcpy(title, szTitle);\r
9131     strcat(title, ": ");\r
9132     strcat(title, first.tidy);\r
9133   }\r
9134   SetWindowText(hwndMain, title);\r
9135 }\r
9136 \r
9137 \r
9138 VOID\r
9139 DisplayMessage(char *str1, char *str2)\r
9140 {\r
9141   HDC hdc;\r
9142   HFONT oldFont;\r
9143   int remain = MESSAGE_TEXT_MAX - 1;\r
9144   int len;\r
9145 \r
9146   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9147   messageText[0] = NULLCHAR;\r
9148   if (*str1) {\r
9149     len = strlen(str1);\r
9150     if (len > remain) len = remain;\r
9151     strncpy(messageText, str1, len);\r
9152     messageText[len] = NULLCHAR;\r
9153     remain -= len;\r
9154   }\r
9155   if (*str2 && remain >= 2) {\r
9156     if (*str1) {\r
9157       strcat(messageText, "  ");\r
9158       remain -= 2;\r
9159     }\r
9160     len = strlen(str2);\r
9161     if (len > remain) len = remain;\r
9162     strncat(messageText, str2, len);\r
9163   }\r
9164   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9165 \r
9166   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9167 \r
9168   SAYMACHINEMOVE();\r
9169 \r
9170   hdc = GetDC(hwndMain);\r
9171   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9172   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9173              &messageRect, messageText, strlen(messageText), NULL);\r
9174   (void) SelectObject(hdc, oldFont);\r
9175   (void) ReleaseDC(hwndMain, hdc);\r
9176 }\r
9177 \r
9178 VOID\r
9179 DisplayError(char *str, int error)\r
9180 {\r
9181   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9182   int len;\r
9183 \r
9184   if (error == 0) {\r
9185     strcpy(buf, str);\r
9186   } else {\r
9187     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9188                         NULL, error, LANG_NEUTRAL,\r
9189                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9190     if (len > 0) {\r
9191       sprintf(buf, "%s:\n%s", str, buf2);\r
9192     } else {\r
9193       ErrorMap *em = errmap;\r
9194       while (em->err != 0 && em->err != error) em++;\r
9195       if (em->err != 0) {\r
9196         sprintf(buf, "%s:\n%s", str, em->msg);\r
9197       } else {\r
9198         sprintf(buf, "%s:\nError code %d", str, error);\r
9199       }\r
9200     }\r
9201   }\r
9202   \r
9203   ErrorPopUp("Error", buf);\r
9204 }\r
9205 \r
9206 \r
9207 VOID\r
9208 DisplayMoveError(char *str)\r
9209 {\r
9210   fromX = fromY = -1;\r
9211   ClearHighlights();\r
9212   DrawPosition(FALSE, NULL);\r
9213   if (appData.popupMoveErrors) {\r
9214     ErrorPopUp("Error", str);\r
9215   } else {\r
9216     DisplayMessage(str, "");\r
9217     moveErrorMessageUp = TRUE;\r
9218   }\r
9219 }\r
9220 \r
9221 VOID\r
9222 DisplayFatalError(char *str, int error, int exitStatus)\r
9223 {\r
9224   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9225   int len;\r
9226   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9227 \r
9228   if (error != 0) {\r
9229     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9230                         NULL, error, LANG_NEUTRAL,\r
9231                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9232     if (len > 0) {\r
9233       sprintf(buf, "%s:\n%s", str, buf2);\r
9234     } else {\r
9235       ErrorMap *em = errmap;\r
9236       while (em->err != 0 && em->err != error) em++;\r
9237       if (em->err != 0) {\r
9238         sprintf(buf, "%s:\n%s", str, em->msg);\r
9239       } else {\r
9240         sprintf(buf, "%s:\nError code %d", str, error);\r
9241       }\r
9242     }\r
9243     str = buf;\r
9244   }\r
9245   if (appData.debugMode) {\r
9246     fprintf(debugFP, "%s: %s\n", label, str);\r
9247   }\r
9248   if (appData.popupExitMessage) {\r
9249     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9250                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9251   }\r
9252   ExitEvent(exitStatus);\r
9253 }\r
9254 \r
9255 \r
9256 VOID\r
9257 DisplayInformation(char *str)\r
9258 {\r
9259   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9260 }\r
9261 \r
9262 \r
9263 VOID\r
9264 DisplayNote(char *str)\r
9265 {\r
9266   ErrorPopUp("Note", str);\r
9267 }\r
9268 \r
9269 \r
9270 typedef struct {\r
9271   char *title, *question, *replyPrefix;\r
9272   ProcRef pr;\r
9273 } QuestionParams;\r
9274 \r
9275 LRESULT CALLBACK\r
9276 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9277 {\r
9278   static QuestionParams *qp;\r
9279   char reply[MSG_SIZ];\r
9280   int len, err;\r
9281 \r
9282   switch (message) {\r
9283   case WM_INITDIALOG:\r
9284     qp = (QuestionParams *) lParam;\r
9285     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9286     SetWindowText(hDlg, qp->title);\r
9287     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9288     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9289     return FALSE;\r
9290 \r
9291   case WM_COMMAND:\r
9292     switch (LOWORD(wParam)) {\r
9293     case IDOK:\r
9294       strcpy(reply, qp->replyPrefix);\r
9295       if (*reply) strcat(reply, " ");\r
9296       len = strlen(reply);\r
9297       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9298       strcat(reply, "\n");\r
9299       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9300       EndDialog(hDlg, TRUE);\r
9301       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9302       return TRUE;\r
9303     case IDCANCEL:\r
9304       EndDialog(hDlg, FALSE);\r
9305       return TRUE;\r
9306     default:\r
9307       break;\r
9308     }\r
9309     break;\r
9310   }\r
9311   return FALSE;\r
9312 }\r
9313 \r
9314 VOID\r
9315 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9316 {\r
9317     QuestionParams qp;\r
9318     FARPROC lpProc;\r
9319     \r
9320     qp.title = title;\r
9321     qp.question = question;\r
9322     qp.replyPrefix = replyPrefix;\r
9323     qp.pr = pr;\r
9324     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9325     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9326       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9327     FreeProcInstance(lpProc);\r
9328 }\r
9329 \r
9330 /* [AS] Pick FRC position */\r
9331 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9332 {\r
9333     static int * lpIndexFRC;\r
9334     BOOL index_is_ok;\r
9335     char buf[16];\r
9336 \r
9337     switch( message )\r
9338     {\r
9339     case WM_INITDIALOG:\r
9340         lpIndexFRC = (int *) lParam;\r
9341 \r
9342         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9343 \r
9344         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9345         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9346         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9347         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9348 \r
9349         break;\r
9350 \r
9351     case WM_COMMAND:\r
9352         switch( LOWORD(wParam) ) {\r
9353         case IDOK:\r
9354             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9355             EndDialog( hDlg, 0 );\r
9356             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9357             return TRUE;\r
9358         case IDCANCEL:\r
9359             EndDialog( hDlg, 1 );   \r
9360             return TRUE;\r
9361         case IDC_NFG_Edit:\r
9362             if( HIWORD(wParam) == EN_CHANGE ) {\r
9363                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9364 \r
9365                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9366             }\r
9367             return TRUE;\r
9368         case IDC_NFG_Random:\r
9369             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9370             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9371             return TRUE;\r
9372         }\r
9373 \r
9374         break;\r
9375     }\r
9376 \r
9377     return FALSE;\r
9378 }\r
9379 \r
9380 int NewGameFRC()\r
9381 {\r
9382     int result;\r
9383     int index = appData.defaultFrcPosition;\r
9384     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9385 \r
9386     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9387 \r
9388     if( result == 0 ) {\r
9389         appData.defaultFrcPosition = index;\r
9390     }\r
9391 \r
9392     return result;\r
9393 }\r
9394 \r
9395 /* [AS] Game list options */\r
9396 typedef struct {\r
9397     char id;\r
9398     char * name;\r
9399 } GLT_Item;\r
9400 \r
9401 static GLT_Item GLT_ItemInfo[] = {\r
9402     { GLT_EVENT,      "Event" },\r
9403     { GLT_SITE,       "Site" },\r
9404     { GLT_DATE,       "Date" },\r
9405     { GLT_ROUND,      "Round" },\r
9406     { GLT_PLAYERS,    "Players" },\r
9407     { GLT_RESULT,     "Result" },\r
9408     { GLT_WHITE_ELO,  "White Rating" },\r
9409     { GLT_BLACK_ELO,  "Black Rating" },\r
9410     { GLT_TIME_CONTROL,"Time Control" },\r
9411     { GLT_VARIANT,    "Variant" },\r
9412     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9413     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9414     { 0, 0 }\r
9415 };\r
9416 \r
9417 const char * GLT_FindItem( char id )\r
9418 {\r
9419     const char * result = 0;\r
9420 \r
9421     GLT_Item * list = GLT_ItemInfo;\r
9422 \r
9423     while( list->id != 0 ) {\r
9424         if( list->id == id ) {\r
9425             result = list->name;\r
9426             break;\r
9427         }\r
9428 \r
9429         list++;\r
9430     }\r
9431 \r
9432     return result;\r
9433 }\r
9434 \r
9435 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9436 {\r
9437     const char * name = GLT_FindItem( id );\r
9438 \r
9439     if( name != 0 ) {\r
9440         if( index >= 0 ) {\r
9441             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9442         }\r
9443         else {\r
9444             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9445         }\r
9446     }\r
9447 }\r
9448 \r
9449 void GLT_TagsToList( HWND hDlg, char * tags )\r
9450 {\r
9451     char * pc = tags;\r
9452 \r
9453     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9454 \r
9455     while( *pc ) {\r
9456         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9457         pc++;\r
9458     }\r
9459 \r
9460     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9461 \r
9462     pc = GLT_ALL_TAGS;\r
9463 \r
9464     while( *pc ) {\r
9465         if( strchr( tags, *pc ) == 0 ) {\r
9466             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9467         }\r
9468         pc++;\r
9469     }\r
9470 \r
9471     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9472 }\r
9473 \r
9474 char GLT_ListItemToTag( HWND hDlg, int index )\r
9475 {\r
9476     char result = '\0';\r
9477     char name[128];\r
9478 \r
9479     GLT_Item * list = GLT_ItemInfo;\r
9480 \r
9481     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9482         while( list->id != 0 ) {\r
9483             if( strcmp( list->name, name ) == 0 ) {\r
9484                 result = list->id;\r
9485                 break;\r
9486             }\r
9487 \r
9488             list++;\r
9489         }\r
9490     }\r
9491 \r
9492     return result;\r
9493 }\r
9494 \r
9495 void GLT_MoveSelection( HWND hDlg, int delta )\r
9496 {\r
9497     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9498     int idx2 = idx1 + delta;\r
9499     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9500 \r
9501     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9502         char buf[128];\r
9503 \r
9504         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9505         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9506         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9507         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9508     }\r
9509 }\r
9510 \r
9511 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9512 {\r
9513     static char glt[64];\r
9514     static char * lpUserGLT;\r
9515 \r
9516     switch( message )\r
9517     {\r
9518     case WM_INITDIALOG:\r
9519         lpUserGLT = (char *) lParam;\r
9520         \r
9521         strcpy( glt, lpUserGLT );\r
9522 \r
9523         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9524 \r
9525         /* Initialize list */\r
9526         GLT_TagsToList( hDlg, glt );\r
9527 \r
9528         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9529 \r
9530         break;\r
9531 \r
9532     case WM_COMMAND:\r
9533         switch( LOWORD(wParam) ) {\r
9534         case IDOK:\r
9535             {\r
9536                 char * pc = lpUserGLT;\r
9537                 int idx = 0;\r
9538 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9539                 char id;\r
9540 \r
9541                 do {\r
9542                     id = GLT_ListItemToTag( hDlg, idx );\r
9543 \r
9544                     *pc++ = id;\r
9545                     idx++;\r
9546                 } while( id != '\0' );\r
9547             }\r
9548             EndDialog( hDlg, 0 );\r
9549             return TRUE;\r
9550         case IDCANCEL:\r
9551             EndDialog( hDlg, 1 );\r
9552             return TRUE;\r
9553 \r
9554         case IDC_GLT_Default:\r
9555             strcpy( glt, GLT_DEFAULT_TAGS );\r
9556             GLT_TagsToList( hDlg, glt );\r
9557             return TRUE;\r
9558 \r
9559         case IDC_GLT_Restore:\r
9560             strcpy( glt, lpUserGLT );\r
9561             GLT_TagsToList( hDlg, glt );\r
9562             return TRUE;\r
9563 \r
9564         case IDC_GLT_Up:\r
9565             GLT_MoveSelection( hDlg, -1 );\r
9566             return TRUE;\r
9567 \r
9568         case IDC_GLT_Down:\r
9569             GLT_MoveSelection( hDlg, +1 );\r
9570             return TRUE;\r
9571         }\r
9572 \r
9573         break;\r
9574     }\r
9575 \r
9576     return FALSE;\r
9577 }\r
9578 \r
9579 int GameListOptions()\r
9580 {\r
9581     char glt[64];\r
9582     int result;\r
9583     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9584 \r
9585     strcpy( glt, appData.gameListTags );\r
9586 \r
9587     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9588 \r
9589     if( result == 0 ) {\r
9590         /* [AS] Memory leak here! */\r
9591         appData.gameListTags = strdup( glt ); \r
9592     }\r
9593 \r
9594     return result;\r
9595 }\r
9596 \r
9597 \r
9598 VOID\r
9599 DisplayIcsInteractionTitle(char *str)\r
9600 {\r
9601   char consoleTitle[MSG_SIZ];\r
9602 \r
9603   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9604   SetWindowText(hwndConsole, consoleTitle);\r
9605 }\r
9606 \r
9607 void\r
9608 DrawPosition(int fullRedraw, Board board)\r
9609 {\r
9610   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9611 }\r
9612 \r
9613 \r
9614 VOID\r
9615 ResetFrontEnd()\r
9616 {\r
9617   fromX = fromY = -1;\r
9618   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9619     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9620     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9621     dragInfo.lastpos = dragInfo.pos;\r
9622     dragInfo.start.x = dragInfo.start.y = -1;\r
9623     dragInfo.from = dragInfo.start;\r
9624     ReleaseCapture();\r
9625     DrawPosition(TRUE, NULL);\r
9626   }\r
9627 }\r
9628 \r
9629 \r
9630 VOID\r
9631 CommentPopUp(char *title, char *str)\r
9632 {\r
9633   HWND hwnd = GetActiveWindow();\r
9634   EitherCommentPopUp(0, title, str, FALSE);\r
9635   SetActiveWindow(hwnd);\r
9636 }\r
9637 \r
9638 VOID\r
9639 CommentPopDown(void)\r
9640 {\r
9641   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9642   if (commentDialog) {\r
9643     ShowWindow(commentDialog, SW_HIDE);\r
9644   }\r
9645   commentDialogUp = FALSE;\r
9646 }\r
9647 \r
9648 VOID\r
9649 EditCommentPopUp(int index, char *title, char *str)\r
9650 {\r
9651   EitherCommentPopUp(index, title, str, TRUE);\r
9652 }\r
9653 \r
9654 \r
9655 VOID\r
9656 RingBell()\r
9657 {\r
9658   MyPlaySound(&sounds[(int)SoundMove]);\r
9659 }\r
9660 \r
9661 VOID PlayIcsWinSound()\r
9662 {\r
9663   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9664 }\r
9665 \r
9666 VOID PlayIcsLossSound()\r
9667 {\r
9668   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9669 }\r
9670 \r
9671 VOID PlayIcsDrawSound()\r
9672 {\r
9673   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9674 }\r
9675 \r
9676 VOID PlayIcsUnfinishedSound()\r
9677 {\r
9678   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9679 }\r
9680 \r
9681 VOID\r
9682 PlayAlarmSound()\r
9683 {\r
9684   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9685 }\r
9686 \r
9687 \r
9688 VOID\r
9689 EchoOn()\r
9690 {\r
9691   HWND hInput;\r
9692   consoleEcho = TRUE;\r
9693   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9694   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9695   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9696 }\r
9697 \r
9698 \r
9699 VOID\r
9700 EchoOff()\r
9701 {\r
9702   CHARFORMAT cf;\r
9703   HWND hInput;\r
9704   consoleEcho = FALSE;\r
9705   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9706   /* This works OK: set text and background both to the same color */\r
9707   cf = consoleCF;\r
9708   cf.crTextColor = COLOR_ECHOOFF;\r
9709   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9710   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9711 }\r
9712 \r
9713 /* No Raw()...? */\r
9714 \r
9715 void Colorize(ColorClass cc, int continuation)\r
9716 {\r
9717   currentColorClass = cc;\r
9718   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9719   consoleCF.crTextColor = textAttribs[cc].color;\r
9720   consoleCF.dwEffects = textAttribs[cc].effects;\r
9721   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9722 }\r
9723 \r
9724 char *\r
9725 UserName()\r
9726 {\r
9727   static char buf[MSG_SIZ];\r
9728   DWORD bufsiz = MSG_SIZ;\r
9729 \r
9730   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9731         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9732   }\r
9733   if (!GetUserName(buf, &bufsiz)) {\r
9734     /*DisplayError("Error getting user name", GetLastError());*/\r
9735     strcpy(buf, "User");\r
9736   }\r
9737   return buf;\r
9738 }\r
9739 \r
9740 char *\r
9741 HostName()\r
9742 {\r
9743   static char buf[MSG_SIZ];\r
9744   DWORD bufsiz = MSG_SIZ;\r
9745 \r
9746   if (!GetComputerName(buf, &bufsiz)) {\r
9747     /*DisplayError("Error getting host name", GetLastError());*/\r
9748     strcpy(buf, "Unknown");\r
9749   }\r
9750   return buf;\r
9751 }\r
9752 \r
9753 \r
9754 int\r
9755 ClockTimerRunning()\r
9756 {\r
9757   return clockTimerEvent != 0;\r
9758 }\r
9759 \r
9760 int\r
9761 StopClockTimer()\r
9762 {\r
9763   if (clockTimerEvent == 0) return FALSE;\r
9764   KillTimer(hwndMain, clockTimerEvent);\r
9765   clockTimerEvent = 0;\r
9766   return TRUE;\r
9767 }\r
9768 \r
9769 void\r
9770 StartClockTimer(long millisec)\r
9771 {\r
9772   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9773                              (UINT) millisec, NULL);\r
9774 }\r
9775 \r
9776 void\r
9777 DisplayWhiteClock(long timeRemaining, int highlight)\r
9778 {\r
9779   HDC hdc;\r
9780   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9781 \r
9782   if(appData.noGUI) return;\r
9783   hdc = GetDC(hwndMain);\r
9784   if (!IsIconic(hwndMain)) {\r
9785     DisplayAClock(hdc, timeRemaining, highlight, \r
9786                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9787   }\r
9788   if (highlight && iconCurrent == iconBlack) {\r
9789     iconCurrent = iconWhite;\r
9790     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9791     if (IsIconic(hwndMain)) {\r
9792       DrawIcon(hdc, 2, 2, iconCurrent);\r
9793     }\r
9794   }\r
9795   (void) ReleaseDC(hwndMain, hdc);\r
9796   if (hwndConsole)\r
9797     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9798 }\r
9799 \r
9800 void\r
9801 DisplayBlackClock(long timeRemaining, int highlight)\r
9802 {\r
9803   HDC hdc;\r
9804   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9805 \r
9806   if(appData.noGUI) return;\r
9807   hdc = GetDC(hwndMain);\r
9808   if (!IsIconic(hwndMain)) {\r
9809     DisplayAClock(hdc, timeRemaining, highlight, \r
9810                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9811   }\r
9812   if (highlight && iconCurrent == iconWhite) {\r
9813     iconCurrent = iconBlack;\r
9814     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9815     if (IsIconic(hwndMain)) {\r
9816       DrawIcon(hdc, 2, 2, iconCurrent);\r
9817     }\r
9818   }\r
9819   (void) ReleaseDC(hwndMain, hdc);\r
9820   if (hwndConsole)\r
9821     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9822 }\r
9823 \r
9824 \r
9825 int\r
9826 LoadGameTimerRunning()\r
9827 {\r
9828   return loadGameTimerEvent != 0;\r
9829 }\r
9830 \r
9831 int\r
9832 StopLoadGameTimer()\r
9833 {\r
9834   if (loadGameTimerEvent == 0) return FALSE;\r
9835   KillTimer(hwndMain, loadGameTimerEvent);\r
9836   loadGameTimerEvent = 0;\r
9837   return TRUE;\r
9838 }\r
9839 \r
9840 void\r
9841 StartLoadGameTimer(long millisec)\r
9842 {\r
9843   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9844                                 (UINT) millisec, NULL);\r
9845 }\r
9846 \r
9847 void\r
9848 AutoSaveGame()\r
9849 {\r
9850   char *defName;\r
9851   FILE *f;\r
9852   char fileTitle[MSG_SIZ];\r
9853 \r
9854   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9855   f = OpenFileDialog(hwndMain, "a", defName,\r
9856                      appData.oldSaveStyle ? "gam" : "pgn",\r
9857                      GAME_FILT, \r
9858                      "Save Game to File", NULL, fileTitle, NULL);\r
9859   if (f != NULL) {\r
9860     SaveGame(f, 0, "");\r
9861     fclose(f);\r
9862   }\r
9863 }\r
9864 \r
9865 \r
9866 void\r
9867 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9868 {\r
9869   if (delayedTimerEvent != 0) {\r
9870     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9871       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9872     }\r
9873     KillTimer(hwndMain, delayedTimerEvent);\r
9874     delayedTimerEvent = 0;\r
9875     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9876     delayedTimerCallback();\r
9877   }\r
9878   delayedTimerCallback = cb;\r
9879   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9880                                 (UINT) millisec, NULL);\r
9881 }\r
9882 \r
9883 DelayedEventCallback\r
9884 GetDelayedEvent()\r
9885 {\r
9886   if (delayedTimerEvent) {\r
9887     return delayedTimerCallback;\r
9888   } else {\r
9889     return NULL;\r
9890   }\r
9891 }\r
9892 \r
9893 void\r
9894 CancelDelayedEvent()\r
9895 {\r
9896   if (delayedTimerEvent) {\r
9897     KillTimer(hwndMain, delayedTimerEvent);\r
9898     delayedTimerEvent = 0;\r
9899   }\r
9900 }\r
9901 \r
9902 DWORD GetWin32Priority(int nice)\r
9903 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9904 /*\r
9905 REALTIME_PRIORITY_CLASS     0x00000100\r
9906 HIGH_PRIORITY_CLASS         0x00000080\r
9907 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9908 NORMAL_PRIORITY_CLASS       0x00000020\r
9909 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9910 IDLE_PRIORITY_CLASS         0x00000040\r
9911 */\r
9912         if (nice < -15) return 0x00000080;\r
9913         if (nice < 0)   return 0x00008000;\r
9914         if (nice == 0)  return 0x00000020;\r
9915         if (nice < 15)  return 0x00004000;\r
9916         return 0x00000040;\r
9917 }\r
9918 \r
9919 /* Start a child process running the given program.\r
9920    The process's standard output can be read from "from", and its\r
9921    standard input can be written to "to".\r
9922    Exit with fatal error if anything goes wrong.\r
9923    Returns an opaque pointer that can be used to destroy the process\r
9924    later.\r
9925 */\r
9926 int\r
9927 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9928 {\r
9929 #define BUFSIZE 4096\r
9930 \r
9931   HANDLE hChildStdinRd, hChildStdinWr,\r
9932     hChildStdoutRd, hChildStdoutWr;\r
9933   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9934   SECURITY_ATTRIBUTES saAttr;\r
9935   BOOL fSuccess;\r
9936   PROCESS_INFORMATION piProcInfo;\r
9937   STARTUPINFO siStartInfo;\r
9938   ChildProc *cp;\r
9939   char buf[MSG_SIZ];\r
9940   DWORD err;\r
9941 \r
9942   if (appData.debugMode) {\r
9943     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9944   }\r
9945 \r
9946   *pr = NoProc;\r
9947 \r
9948   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9949   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9950   saAttr.bInheritHandle = TRUE;\r
9951   saAttr.lpSecurityDescriptor = NULL;\r
9952 \r
9953   /*\r
9954    * The steps for redirecting child's STDOUT:\r
9955    *     1. Create anonymous pipe to be STDOUT for child.\r
9956    *     2. Create a noninheritable duplicate of read handle,\r
9957    *         and close the inheritable read handle.\r
9958    */\r
9959 \r
9960   /* Create a pipe for the child's STDOUT. */\r
9961   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9962     return GetLastError();\r
9963   }\r
9964 \r
9965   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9966   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9967                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9968                              FALSE,     /* not inherited */\r
9969                              DUPLICATE_SAME_ACCESS);\r
9970   if (! fSuccess) {\r
9971     return GetLastError();\r
9972   }\r
9973   CloseHandle(hChildStdoutRd);\r
9974 \r
9975   /*\r
9976    * The steps for redirecting child's STDIN:\r
9977    *     1. Create anonymous pipe to be STDIN for child.\r
9978    *     2. Create a noninheritable duplicate of write handle,\r
9979    *         and close the inheritable write handle.\r
9980    */\r
9981 \r
9982   /* Create a pipe for the child's STDIN. */\r
9983   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9984     return GetLastError();\r
9985   }\r
9986 \r
9987   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9988   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9989                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9990                              FALSE,     /* not inherited */\r
9991                              DUPLICATE_SAME_ACCESS);\r
9992   if (! fSuccess) {\r
9993     return GetLastError();\r
9994   }\r
9995   CloseHandle(hChildStdinWr);\r
9996 \r
9997   /* Arrange to (1) look in dir for the child .exe file, and\r
9998    * (2) have dir be the child's working directory.  Interpret\r
9999    * dir relative to the directory WinBoard loaded from. */\r
10000   GetCurrentDirectory(MSG_SIZ, buf);\r
10001   SetCurrentDirectory(installDir);\r
10002   SetCurrentDirectory(dir);\r
10003 \r
10004   /* Now create the child process. */\r
10005 \r
10006   siStartInfo.cb = sizeof(STARTUPINFO);\r
10007   siStartInfo.lpReserved = NULL;\r
10008   siStartInfo.lpDesktop = NULL;\r
10009   siStartInfo.lpTitle = NULL;\r
10010   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
10011   siStartInfo.cbReserved2 = 0;\r
10012   siStartInfo.lpReserved2 = NULL;\r
10013   siStartInfo.hStdInput = hChildStdinRd;\r
10014   siStartInfo.hStdOutput = hChildStdoutWr;\r
10015   siStartInfo.hStdError = hChildStdoutWr;\r
10016 \r
10017   fSuccess = CreateProcess(NULL,\r
10018                            cmdLine,        /* command line */\r
10019                            NULL,           /* process security attributes */\r
10020                            NULL,           /* primary thread security attrs */\r
10021                            TRUE,           /* handles are inherited */\r
10022                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
10023                            NULL,           /* use parent's environment */\r
10024                            NULL,\r
10025                            &siStartInfo, /* STARTUPINFO pointer */\r
10026                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
10027 \r
10028   err = GetLastError();\r
10029   SetCurrentDirectory(buf); /* return to prev directory */\r
10030   if (! fSuccess) {\r
10031     return err;\r
10032   }\r
10033 \r
10034   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
10035     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
10036     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
10037   }\r
10038 \r
10039   /* Close the handles we don't need in the parent */\r
10040   CloseHandle(piProcInfo.hThread);\r
10041   CloseHandle(hChildStdinRd);\r
10042   CloseHandle(hChildStdoutWr);\r
10043 \r
10044   /* Prepare return value */\r
10045   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10046   cp->kind = CPReal;\r
10047   cp->hProcess = piProcInfo.hProcess;\r
10048   cp->pid = piProcInfo.dwProcessId;\r
10049   cp->hFrom = hChildStdoutRdDup;\r
10050   cp->hTo = hChildStdinWrDup;\r
10051 \r
10052   *pr = (void *) cp;\r
10053 \r
10054   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10055      2000 where engines sometimes don't see the initial command(s)\r
10056      from WinBoard and hang.  I don't understand how that can happen,\r
10057      but the Sleep is harmless, so I've put it in.  Others have also\r
10058      reported what may be the same problem, so hopefully this will fix\r
10059      it for them too.  */\r
10060   Sleep(500);\r
10061 \r
10062   return NO_ERROR;\r
10063 }\r
10064 \r
10065 \r
10066 void\r
10067 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10068 {\r
10069   ChildProc *cp; int result;\r
10070 \r
10071   cp = (ChildProc *) pr;\r
10072   if (cp == NULL) return;\r
10073 \r
10074   switch (cp->kind) {\r
10075   case CPReal:\r
10076     /* TerminateProcess is considered harmful, so... */\r
10077     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10078     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10079     /* The following doesn't work because the chess program\r
10080        doesn't "have the same console" as WinBoard.  Maybe\r
10081        we could arrange for this even though neither WinBoard\r
10082        nor the chess program uses a console for stdio? */\r
10083     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10084 \r
10085     /* [AS] Special termination modes for misbehaving programs... */\r
10086     if( signal == 9 ) { \r
10087         result = TerminateProcess( cp->hProcess, 0 );\r
10088 \r
10089         if ( appData.debugMode) {\r
10090             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10091         }\r
10092     }\r
10093     else if( signal == 10 ) {\r
10094         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10095 \r
10096         if( dw != WAIT_OBJECT_0 ) {\r
10097             result = TerminateProcess( cp->hProcess, 0 );\r
10098 \r
10099             if ( appData.debugMode) {\r
10100                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10101             }\r
10102 \r
10103         }\r
10104     }\r
10105 \r
10106     CloseHandle(cp->hProcess);\r
10107     break;\r
10108 \r
10109   case CPComm:\r
10110     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10111     break;\r
10112 \r
10113   case CPSock:\r
10114     closesocket(cp->sock);\r
10115     WSACleanup();\r
10116     break;\r
10117 \r
10118   case CPRcmd:\r
10119     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10120     closesocket(cp->sock);\r
10121     closesocket(cp->sock2);\r
10122     WSACleanup();\r
10123     break;\r
10124   }\r
10125   free(cp);\r
10126 }\r
10127 \r
10128 void\r
10129 InterruptChildProcess(ProcRef pr)\r
10130 {\r
10131   ChildProc *cp;\r
10132 \r
10133   cp = (ChildProc *) pr;\r
10134   if (cp == NULL) return;\r
10135   switch (cp->kind) {\r
10136   case CPReal:\r
10137     /* The following doesn't work because the chess program\r
10138        doesn't "have the same console" as WinBoard.  Maybe\r
10139        we could arrange for this even though neither WinBoard\r
10140        nor the chess program uses a console for stdio */\r
10141     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10142     break;\r
10143 \r
10144   case CPComm:\r
10145   case CPSock:\r
10146     /* Can't interrupt */\r
10147     break;\r
10148 \r
10149   case CPRcmd:\r
10150     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10151     break;\r
10152   }\r
10153 }\r
10154 \r
10155 \r
10156 int\r
10157 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10158 {\r
10159   char cmdLine[MSG_SIZ];\r
10160 \r
10161   if (port[0] == NULLCHAR) {\r
10162     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10163   } else {\r
10164     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10165   }\r
10166   return StartChildProcess(cmdLine, "", pr);\r
10167 }\r
10168 \r
10169 \r
10170 /* Code to open TCP sockets */\r
10171 \r
10172 int\r
10173 OpenTCP(char *host, char *port, ProcRef *pr)\r
10174 {\r
10175   ChildProc *cp;\r
10176   int err;\r
10177   SOCKET s;\r
10178   struct sockaddr_in sa, mysa;\r
10179   struct hostent FAR *hp;\r
10180   unsigned short uport;\r
10181   WORD wVersionRequested;\r
10182   WSADATA wsaData;\r
10183 \r
10184   /* Initialize socket DLL */\r
10185   wVersionRequested = MAKEWORD(1, 1);\r
10186   err = WSAStartup(wVersionRequested, &wsaData);\r
10187   if (err != 0) return err;\r
10188 \r
10189   /* Make socket */\r
10190   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10191     err = WSAGetLastError();\r
10192     WSACleanup();\r
10193     return err;\r
10194   }\r
10195 \r
10196   /* Bind local address using (mostly) don't-care values.\r
10197    */\r
10198   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10199   mysa.sin_family = AF_INET;\r
10200   mysa.sin_addr.s_addr = INADDR_ANY;\r
10201   uport = (unsigned short) 0;\r
10202   mysa.sin_port = htons(uport);\r
10203   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10204       == SOCKET_ERROR) {\r
10205     err = WSAGetLastError();\r
10206     WSACleanup();\r
10207     return err;\r
10208   }\r
10209 \r
10210   /* Resolve remote host name */\r
10211   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10212   if (!(hp = gethostbyname(host))) {\r
10213     unsigned int b0, b1, b2, b3;\r
10214 \r
10215     err = WSAGetLastError();\r
10216 \r
10217     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10218       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10219       hp->h_addrtype = AF_INET;\r
10220       hp->h_length = 4;\r
10221       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10222       hp->h_addr_list[0] = (char *) malloc(4);\r
10223       hp->h_addr_list[0][0] = (char) b0;\r
10224       hp->h_addr_list[0][1] = (char) b1;\r
10225       hp->h_addr_list[0][2] = (char) b2;\r
10226       hp->h_addr_list[0][3] = (char) b3;\r
10227     } else {\r
10228       WSACleanup();\r
10229       return err;\r
10230     }\r
10231   }\r
10232   sa.sin_family = hp->h_addrtype;\r
10233   uport = (unsigned short) atoi(port);\r
10234   sa.sin_port = htons(uport);\r
10235   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10236 \r
10237   /* Make connection */\r
10238   if (connect(s, (struct sockaddr *) &sa,\r
10239               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10240     err = WSAGetLastError();\r
10241     WSACleanup();\r
10242     return err;\r
10243   }\r
10244 \r
10245   /* Prepare return value */\r
10246   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10247   cp->kind = CPSock;\r
10248   cp->sock = s;\r
10249   *pr = (ProcRef *) cp;\r
10250 \r
10251   return NO_ERROR;\r
10252 }\r
10253 \r
10254 int\r
10255 OpenCommPort(char *name, ProcRef *pr)\r
10256 {\r
10257   HANDLE h;\r
10258   COMMTIMEOUTS ct;\r
10259   ChildProc *cp;\r
10260   char fullname[MSG_SIZ];\r
10261 \r
10262   if (*name != '\\')\r
10263     sprintf(fullname, "\\\\.\\%s", name);\r
10264   else\r
10265     strcpy(fullname, name);\r
10266 \r
10267   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10268                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10269   if (h == (HANDLE) -1) {\r
10270     return GetLastError();\r
10271   }\r
10272   hCommPort = h;\r
10273 \r
10274   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10275 \r
10276   /* Accumulate characters until a 100ms pause, then parse */\r
10277   ct.ReadIntervalTimeout = 100;\r
10278   ct.ReadTotalTimeoutMultiplier = 0;\r
10279   ct.ReadTotalTimeoutConstant = 0;\r
10280   ct.WriteTotalTimeoutMultiplier = 0;\r
10281   ct.WriteTotalTimeoutConstant = 0;\r
10282   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10283 \r
10284   /* Prepare return value */\r
10285   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10286   cp->kind = CPComm;\r
10287   cp->hFrom = h;\r
10288   cp->hTo = h;\r
10289   *pr = (ProcRef *) cp;\r
10290 \r
10291   return NO_ERROR;\r
10292 }\r
10293 \r
10294 int\r
10295 OpenLoopback(ProcRef *pr)\r
10296 {\r
10297   DisplayFatalError("Not implemented", 0, 1);\r
10298   return NO_ERROR;\r
10299 }\r
10300 \r
10301 \r
10302 int\r
10303 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10304 {\r
10305   ChildProc *cp;\r
10306   int err;\r
10307   SOCKET s, s2, s3;\r
10308   struct sockaddr_in sa, mysa;\r
10309   struct hostent FAR *hp;\r
10310   unsigned short uport;\r
10311   WORD wVersionRequested;\r
10312   WSADATA wsaData;\r
10313   int fromPort;\r
10314   char stderrPortStr[MSG_SIZ];\r
10315 \r
10316   /* Initialize socket DLL */\r
10317   wVersionRequested = MAKEWORD(1, 1);\r
10318   err = WSAStartup(wVersionRequested, &wsaData);\r
10319   if (err != 0) return err;\r
10320 \r
10321   /* Resolve remote host name */\r
10322   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10323   if (!(hp = gethostbyname(host))) {\r
10324     unsigned int b0, b1, b2, b3;\r
10325 \r
10326     err = WSAGetLastError();\r
10327 \r
10328     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10329       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10330       hp->h_addrtype = AF_INET;\r
10331       hp->h_length = 4;\r
10332       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10333       hp->h_addr_list[0] = (char *) malloc(4);\r
10334       hp->h_addr_list[0][0] = (char) b0;\r
10335       hp->h_addr_list[0][1] = (char) b1;\r
10336       hp->h_addr_list[0][2] = (char) b2;\r
10337       hp->h_addr_list[0][3] = (char) b3;\r
10338     } else {\r
10339       WSACleanup();\r
10340       return err;\r
10341     }\r
10342   }\r
10343   sa.sin_family = hp->h_addrtype;\r
10344   uport = (unsigned short) 514;\r
10345   sa.sin_port = htons(uport);\r
10346   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10347 \r
10348   /* Bind local socket to unused "privileged" port address\r
10349    */\r
10350   s = INVALID_SOCKET;\r
10351   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10352   mysa.sin_family = AF_INET;\r
10353   mysa.sin_addr.s_addr = INADDR_ANY;\r
10354   for (fromPort = 1023;; fromPort--) {\r
10355     if (fromPort < 0) {\r
10356       WSACleanup();\r
10357       return WSAEADDRINUSE;\r
10358     }\r
10359     if (s == INVALID_SOCKET) {\r
10360       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10361         err = WSAGetLastError();\r
10362         WSACleanup();\r
10363         return err;\r
10364       }\r
10365     }\r
10366     uport = (unsigned short) fromPort;\r
10367     mysa.sin_port = htons(uport);\r
10368     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10369         == SOCKET_ERROR) {\r
10370       err = WSAGetLastError();\r
10371       if (err == WSAEADDRINUSE) continue;\r
10372       WSACleanup();\r
10373       return err;\r
10374     }\r
10375     if (connect(s, (struct sockaddr *) &sa,\r
10376       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10377       err = WSAGetLastError();\r
10378       if (err == WSAEADDRINUSE) {\r
10379         closesocket(s);\r
10380         s = -1;\r
10381         continue;\r
10382       }\r
10383       WSACleanup();\r
10384       return err;\r
10385     }\r
10386     break;\r
10387   }\r
10388 \r
10389   /* Bind stderr local socket to unused "privileged" port address\r
10390    */\r
10391   s2 = INVALID_SOCKET;\r
10392   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10393   mysa.sin_family = AF_INET;\r
10394   mysa.sin_addr.s_addr = INADDR_ANY;\r
10395   for (fromPort = 1023;; fromPort--) {\r
10396     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10397     if (fromPort < 0) {\r
10398       (void) closesocket(s);\r
10399       WSACleanup();\r
10400       return WSAEADDRINUSE;\r
10401     }\r
10402     if (s2 == INVALID_SOCKET) {\r
10403       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10404         err = WSAGetLastError();\r
10405         closesocket(s);\r
10406         WSACleanup();\r
10407         return err;\r
10408       }\r
10409     }\r
10410     uport = (unsigned short) fromPort;\r
10411     mysa.sin_port = htons(uport);\r
10412     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10413         == SOCKET_ERROR) {\r
10414       err = WSAGetLastError();\r
10415       if (err == WSAEADDRINUSE) continue;\r
10416       (void) closesocket(s);\r
10417       WSACleanup();\r
10418       return err;\r
10419     }\r
10420     if (listen(s2, 1) == SOCKET_ERROR) {\r
10421       err = WSAGetLastError();\r
10422       if (err == WSAEADDRINUSE) {\r
10423         closesocket(s2);\r
10424         s2 = INVALID_SOCKET;\r
10425         continue;\r
10426       }\r
10427       (void) closesocket(s);\r
10428       (void) closesocket(s2);\r
10429       WSACleanup();\r
10430       return err;\r
10431     }\r
10432     break;\r
10433   }\r
10434   prevStderrPort = fromPort; // remember port used\r
10435   sprintf(stderrPortStr, "%d", fromPort);\r
10436 \r
10437   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10438     err = WSAGetLastError();\r
10439     (void) closesocket(s);\r
10440     (void) closesocket(s2);\r
10441     WSACleanup();\r
10442     return err;\r
10443   }\r
10444 \r
10445   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10446     err = WSAGetLastError();\r
10447     (void) closesocket(s);\r
10448     (void) closesocket(s2);\r
10449     WSACleanup();\r
10450     return err;\r
10451   }\r
10452   if (*user == NULLCHAR) user = UserName();\r
10453   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10454     err = WSAGetLastError();\r
10455     (void) closesocket(s);\r
10456     (void) closesocket(s2);\r
10457     WSACleanup();\r
10458     return err;\r
10459   }\r
10460   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10461     err = WSAGetLastError();\r
10462     (void) closesocket(s);\r
10463     (void) closesocket(s2);\r
10464     WSACleanup();\r
10465     return err;\r
10466   }\r
10467 \r
10468   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10469     err = WSAGetLastError();\r
10470     (void) closesocket(s);\r
10471     (void) closesocket(s2);\r
10472     WSACleanup();\r
10473     return err;\r
10474   }\r
10475   (void) closesocket(s2);  /* Stop listening */\r
10476 \r
10477   /* Prepare return value */\r
10478   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10479   cp->kind = CPRcmd;\r
10480   cp->sock = s;\r
10481   cp->sock2 = s3;\r
10482   *pr = (ProcRef *) cp;\r
10483 \r
10484   return NO_ERROR;\r
10485 }\r
10486 \r
10487 \r
10488 InputSourceRef\r
10489 AddInputSource(ProcRef pr, int lineByLine,\r
10490                InputCallback func, VOIDSTAR closure)\r
10491 {\r
10492   InputSource *is, *is2 = NULL;\r
10493   ChildProc *cp = (ChildProc *) pr;\r
10494 \r
10495   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10496   is->lineByLine = lineByLine;\r
10497   is->func = func;\r
10498   is->closure = closure;\r
10499   is->second = NULL;\r
10500   is->next = is->buf;\r
10501   if (pr == NoProc) {\r
10502     is->kind = CPReal;\r
10503     consoleInputSource = is;\r
10504   } else {\r
10505     is->kind = cp->kind;\r
10506     /* \r
10507         [AS] Try to avoid a race condition if the thread is given control too early:\r
10508         we create all threads suspended so that the is->hThread variable can be\r
10509         safely assigned, then let the threads start with ResumeThread.\r
10510     */\r
10511     switch (cp->kind) {\r
10512     case CPReal:\r
10513       is->hFile = cp->hFrom;\r
10514       cp->hFrom = NULL; /* now owned by InputThread */\r
10515       is->hThread =\r
10516         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10517                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10518       break;\r
10519 \r
10520     case CPComm:\r
10521       is->hFile = cp->hFrom;\r
10522       cp->hFrom = NULL; /* now owned by InputThread */\r
10523       is->hThread =\r
10524         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10525                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10526       break;\r
10527 \r
10528     case CPSock:\r
10529       is->sock = cp->sock;\r
10530       is->hThread =\r
10531         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10532                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10533       break;\r
10534 \r
10535     case CPRcmd:\r
10536       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10537       *is2 = *is;\r
10538       is->sock = cp->sock;\r
10539       is->second = is2;\r
10540       is2->sock = cp->sock2;\r
10541       is2->second = is2;\r
10542       is->hThread =\r
10543         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10544                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10545       is2->hThread =\r
10546         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10547                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10548       break;\r
10549     }\r
10550 \r
10551     if( is->hThread != NULL ) {\r
10552         ResumeThread( is->hThread );\r
10553     }\r
10554 \r
10555     if( is2 != NULL && is2->hThread != NULL ) {\r
10556         ResumeThread( is2->hThread );\r
10557     }\r
10558   }\r
10559 \r
10560   return (InputSourceRef) is;\r
10561 }\r
10562 \r
10563 void\r
10564 RemoveInputSource(InputSourceRef isr)\r
10565 {\r
10566   InputSource *is;\r
10567 \r
10568   is = (InputSource *) isr;\r
10569   is->hThread = NULL;  /* tell thread to stop */\r
10570   CloseHandle(is->hThread);\r
10571   if (is->second != NULL) {\r
10572     is->second->hThread = NULL;\r
10573     CloseHandle(is->second->hThread);\r
10574   }\r
10575 }\r
10576 \r
10577 \r
10578 int\r
10579 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10580 {\r
10581   DWORD dOutCount;\r
10582   int outCount = SOCKET_ERROR;\r
10583   ChildProc *cp = (ChildProc *) pr;\r
10584   static OVERLAPPED ovl;\r
10585 \r
10586   if (pr == NoProc) {\r
10587     ConsoleOutput(message, count, FALSE);\r
10588     return count;\r
10589   } \r
10590 \r
10591   if (ovl.hEvent == NULL) {\r
10592     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10593   }\r
10594   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10595 \r
10596   switch (cp->kind) {\r
10597   case CPSock:\r
10598   case CPRcmd:\r
10599     outCount = send(cp->sock, message, count, 0);\r
10600     if (outCount == SOCKET_ERROR) {\r
10601       *outError = WSAGetLastError();\r
10602     } else {\r
10603       *outError = NO_ERROR;\r
10604     }\r
10605     break;\r
10606 \r
10607   case CPReal:\r
10608     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10609                   &dOutCount, NULL)) {\r
10610       *outError = NO_ERROR;\r
10611       outCount = (int) dOutCount;\r
10612     } else {\r
10613       *outError = GetLastError();\r
10614     }\r
10615     break;\r
10616 \r
10617   case CPComm:\r
10618     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10619                             &dOutCount, &ovl);\r
10620     if (*outError == NO_ERROR) {\r
10621       outCount = (int) dOutCount;\r
10622     }\r
10623     break;\r
10624   }\r
10625   return outCount;\r
10626 }\r
10627 \r
10628 int\r
10629 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10630                        long msdelay)\r
10631 {\r
10632   /* Ignore delay, not implemented for WinBoard */\r
10633   return OutputToProcess(pr, message, count, outError);\r
10634 }\r
10635 \r
10636 \r
10637 void\r
10638 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10639                         char *buf, int count, int error)\r
10640 {\r
10641   DisplayFatalError("Not implemented", 0, 1);\r
10642 }\r
10643 \r
10644 /* see wgamelist.c for Game List functions */\r
10645 /* see wedittags.c for Edit Tags functions */\r
10646 \r
10647 \r
10648 VOID\r
10649 ICSInitScript()\r
10650 {\r
10651   FILE *f;\r
10652   char buf[MSG_SIZ];\r
10653   char *dummy;\r
10654 \r
10655   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10656     f = fopen(buf, "r");\r
10657     if (f != NULL) {\r
10658       ProcessICSInitScript(f);\r
10659       fclose(f);\r
10660     }\r
10661   }\r
10662 }\r
10663 \r
10664 \r
10665 VOID\r
10666 StartAnalysisClock()\r
10667 {\r
10668   if (analysisTimerEvent) return;\r
10669   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10670                                         (UINT) 2000, NULL);\r
10671 }\r
10672 \r
10673 LRESULT CALLBACK\r
10674 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10675 {\r
10676   static HANDLE hwndText;\r
10677   RECT rect;\r
10678   static int sizeX, sizeY;\r
10679   int newSizeX, newSizeY, flags;\r
10680   MINMAXINFO *mmi;\r
10681 \r
10682   switch (message) {\r
10683   case WM_INITDIALOG: /* message: initialize dialog box */\r
10684     /* Initialize the dialog items */\r
10685     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10686     SetWindowText(hDlg, analysisTitle);\r
10687     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10688     /* Size and position the dialog */\r
10689     if (!analysisDialog) {\r
10690       analysisDialog = hDlg;\r
10691       flags = SWP_NOZORDER;\r
10692       GetClientRect(hDlg, &rect);\r
10693       sizeX = rect.right;\r
10694       sizeY = rect.bottom;\r
10695       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10696           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10697         WINDOWPLACEMENT wp;\r
10698         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10699         wp.length = sizeof(WINDOWPLACEMENT);\r
10700         wp.flags = 0;\r
10701         wp.showCmd = SW_SHOW;\r
10702         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10703         wp.rcNormalPosition.left = analysisX;\r
10704         wp.rcNormalPosition.right = analysisX + analysisW;\r
10705         wp.rcNormalPosition.top = analysisY;\r
10706         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10707         SetWindowPlacement(hDlg, &wp);\r
10708 \r
10709         GetClientRect(hDlg, &rect);\r
10710         newSizeX = rect.right;\r
10711         newSizeY = rect.bottom;\r
10712         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10713                               newSizeX, newSizeY);\r
10714         sizeX = newSizeX;\r
10715         sizeY = newSizeY;\r
10716       }\r
10717     }\r
10718     return FALSE;\r
10719 \r
10720   case WM_COMMAND: /* message: received a command */\r
10721     switch (LOWORD(wParam)) {\r
10722     case IDCANCEL:\r
10723       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10724           ExitAnalyzeMode();\r
10725           ModeHighlight();\r
10726           return TRUE;\r
10727       }\r
10728       EditGameEvent();\r
10729       return TRUE;\r
10730     default:\r
10731       break;\r
10732     }\r
10733     break;\r
10734 \r
10735   case WM_SIZE:\r
10736     newSizeX = LOWORD(lParam);\r
10737     newSizeY = HIWORD(lParam);\r
10738     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10739     sizeX = newSizeX;\r
10740     sizeY = newSizeY;\r
10741     break;\r
10742 \r
10743   case WM_GETMINMAXINFO:\r
10744     /* Prevent resizing window too small */\r
10745     mmi = (MINMAXINFO *) lParam;\r
10746     mmi->ptMinTrackSize.x = 100;\r
10747     mmi->ptMinTrackSize.y = 100;\r
10748     break;\r
10749   }\r
10750   return FALSE;\r
10751 }\r
10752 \r
10753 VOID\r
10754 AnalysisPopUp(char* title, char* str)\r
10755 {\r
10756   FARPROC lpProc;\r
10757   char *p, *q;\r
10758 \r
10759   /* [AS] */\r
10760   EngineOutputPopUp();\r
10761   return;\r
10762 \r
10763   if (str == NULL) str = "";\r
10764   p = (char *) malloc(2 * strlen(str) + 2);\r
10765   q = p;\r
10766   while (*str) {\r
10767     if (*str == '\n') *q++ = '\r';\r
10768     *q++ = *str++;\r
10769   }\r
10770   *q = NULLCHAR;\r
10771   if (analysisText != NULL) free(analysisText);\r
10772   analysisText = p;\r
10773 \r
10774   if (analysisDialog) {\r
10775     SetWindowText(analysisDialog, title);\r
10776     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10777     ShowWindow(analysisDialog, SW_SHOW);\r
10778   } else {\r
10779     analysisTitle = title;\r
10780     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10781     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10782                  hwndMain, (DLGPROC)lpProc);\r
10783     FreeProcInstance(lpProc);\r
10784   }\r
10785   analysisDialogUp = TRUE;  \r
10786 }\r
10787 \r
10788 VOID\r
10789 AnalysisPopDown()\r
10790 {\r
10791   if (analysisDialog) {\r
10792     ShowWindow(analysisDialog, SW_HIDE);\r
10793   }\r
10794   analysisDialogUp = FALSE;  \r
10795 }\r
10796 \r
10797 \r
10798 VOID\r
10799 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10800 {\r
10801   highlightInfo.sq[0].x = fromX;\r
10802   highlightInfo.sq[0].y = fromY;\r
10803   highlightInfo.sq[1].x = toX;\r
10804   highlightInfo.sq[1].y = toY;\r
10805 }\r
10806 \r
10807 VOID\r
10808 ClearHighlights()\r
10809 {\r
10810   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10811     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10812 }\r
10813 \r
10814 VOID\r
10815 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10816 {\r
10817   premoveHighlightInfo.sq[0].x = fromX;\r
10818   premoveHighlightInfo.sq[0].y = fromY;\r
10819   premoveHighlightInfo.sq[1].x = toX;\r
10820   premoveHighlightInfo.sq[1].y = toY;\r
10821 }\r
10822 \r
10823 VOID\r
10824 ClearPremoveHighlights()\r
10825 {\r
10826   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10827     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10828 }\r
10829 \r
10830 VOID\r
10831 ShutDownFrontEnd()\r
10832 {\r
10833   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10834   DeleteClipboardTempFiles();\r
10835 }\r
10836 \r
10837 void\r
10838 BoardToTop()\r
10839 {\r
10840     if (IsIconic(hwndMain))\r
10841       ShowWindow(hwndMain, SW_RESTORE);\r
10842 \r
10843     SetActiveWindow(hwndMain);\r
10844 }\r
10845 \r
10846 /*\r
10847  * Prototypes for animation support routines\r
10848  */\r
10849 static void ScreenSquare(int column, int row, POINT * pt);\r
10850 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10851      POINT frames[], int * nFrames);\r
10852 \r
10853 \r
10854 void\r
10855 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10856 {       // [HGM] atomic: animate blast wave\r
10857         int i;\r
10858 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10859         explodeInfo.fromX = fromX;\r
10860         explodeInfo.fromY = fromY;\r
10861         explodeInfo.toX = toX;\r
10862         explodeInfo.toY = toY;\r
10863         for(i=1; i<nFrames; i++) {\r
10864             explodeInfo.radius = (i*180)/(nFrames-1);\r
10865             DrawPosition(FALSE, NULL);\r
10866             Sleep(appData.animSpeed);\r
10867         }\r
10868         explodeInfo.radius = 0;\r
10869         DrawPosition(TRUE, NULL);\r
10870 }\r
10871 \r
10872 #define kFactor 4\r
10873 \r
10874 void\r
10875 AnimateMove(board, fromX, fromY, toX, toY)\r
10876      Board board;\r
10877      int fromX;\r
10878      int fromY;\r
10879      int toX;\r
10880      int toY;\r
10881 {\r
10882   ChessSquare piece;\r
10883   POINT start, finish, mid;\r
10884   POINT frames[kFactor * 2 + 1];\r
10885   int nFrames, n;\r
10886 \r
10887   if (!appData.animate) return;\r
10888   if (doingSizing) return;\r
10889   if (fromY < 0 || fromX < 0) return;\r
10890   piece = board[fromY][fromX];\r
10891   if (piece >= EmptySquare) return;\r
10892 \r
10893   ScreenSquare(fromX, fromY, &start);\r
10894   ScreenSquare(toX, toY, &finish);\r
10895 \r
10896   /* All pieces except knights move in straight line */\r
10897   if (piece != WhiteKnight && piece != BlackKnight) {\r
10898     mid.x = start.x + (finish.x - start.x) / 2;\r
10899     mid.y = start.y + (finish.y - start.y) / 2;\r
10900   } else {\r
10901     /* Knight: make diagonal movement then straight */\r
10902     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10903        mid.x = start.x + (finish.x - start.x) / 2;\r
10904        mid.y = finish.y;\r
10905      } else {\r
10906        mid.x = finish.x;\r
10907        mid.y = start.y + (finish.y - start.y) / 2;\r
10908      }\r
10909   }\r
10910   \r
10911   /* Don't use as many frames for very short moves */\r
10912   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10913     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10914   else\r
10915     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10916 \r
10917   animInfo.from.x = fromX;\r
10918   animInfo.from.y = fromY;\r
10919   animInfo.to.x = toX;\r
10920   animInfo.to.y = toY;\r
10921   animInfo.lastpos = start;\r
10922   animInfo.piece = piece;\r
10923   for (n = 0; n < nFrames; n++) {\r
10924     animInfo.pos = frames[n];\r
10925     DrawPosition(FALSE, NULL);\r
10926     animInfo.lastpos = animInfo.pos;\r
10927     Sleep(appData.animSpeed);\r
10928   }\r
10929   animInfo.pos = finish;\r
10930   DrawPosition(FALSE, NULL);\r
10931   animInfo.piece = EmptySquare;\r
10932   if(gameInfo.variant == VariantAtomic && \r
10933      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10934         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10935 }\r
10936 \r
10937 /*      Convert board position to corner of screen rect and color       */\r
10938 \r
10939 static void\r
10940 ScreenSquare(column, row, pt)\r
10941      int column; int row; POINT * pt;\r
10942 {\r
10943   if (flipView) {\r
10944     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10945     pt->y = lineGap + row * (squareSize + lineGap);\r
10946   } else {\r
10947     pt->x = lineGap + column * (squareSize + lineGap);\r
10948     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10949   }\r
10950 }\r
10951 \r
10952 /*      Generate a series of frame coords from start->mid->finish.\r
10953         The movement rate doubles until the half way point is\r
10954         reached, then halves back down to the final destination,\r
10955         which gives a nice slow in/out effect. The algorithmn\r
10956         may seem to generate too many intermediates for short\r
10957         moves, but remember that the purpose is to attract the\r
10958         viewers attention to the piece about to be moved and\r
10959         then to where it ends up. Too few frames would be less\r
10960         noticeable.                                             */\r
10961 \r
10962 static void\r
10963 Tween(start, mid, finish, factor, frames, nFrames)\r
10964      POINT * start; POINT * mid;\r
10965      POINT * finish; int factor;\r
10966      POINT frames[]; int * nFrames;\r
10967 {\r
10968   int n, fraction = 1, count = 0;\r
10969 \r
10970   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10971   for (n = 0; n < factor; n++)\r
10972     fraction *= 2;\r
10973   for (n = 0; n < factor; n++) {\r
10974     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10975     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10976     count ++;\r
10977     fraction = fraction / 2;\r
10978   }\r
10979   \r
10980   /* Midpoint */\r
10981   frames[count] = *mid;\r
10982   count ++;\r
10983   \r
10984   /* Slow out, stepping 1/2, then 1/4, ... */\r
10985   fraction = 2;\r
10986   for (n = 0; n < factor; n++) {\r
10987     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10988     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10989     count ++;\r
10990     fraction = fraction * 2;\r
10991   }\r
10992   *nFrames = count;\r
10993 }\r
10994 \r
10995 void\r
10996 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10997 {\r
10998 #if 0\r
10999     char buf[256];\r
11000 \r
11001     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
11002         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
11003 \r
11004     OutputDebugString( buf );\r
11005 #endif\r
11006 \r
11007     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
11008 \r
11009     EvalGraphSet( first, last, current, pvInfoList );\r
11010 }\r
11011 \r
11012 void SetProgramStats( FrontEndProgramStats * stats )\r
11013 {\r
11014 #if 0\r
11015     char buf[1024];\r
11016 \r
11017     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
11018         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
11019 \r
11020     OutputDebugString( buf );\r
11021 #endif\r
11022 \r
11023     EngineOutputUpdate( stats );\r
11024 }\r