fix premove for winboard
[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 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 typedef struct {\r
112   ChessSquare piece;  \r
113   POINT pos;      /* window coordinates of current pos */\r
114   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
115   POINT from;     /* board coordinates of the piece's orig pos */\r
116   POINT to;       /* board coordinates of the piece's new pos */\r
117 } AnimInfo;\r
118 \r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
120 \r
121 typedef struct {\r
122   POINT start;    /* window coordinates of start pos */\r
123   POINT pos;      /* window coordinates of current pos */\r
124   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
125   POINT from;     /* board coordinates of the piece's orig pos */\r
126 } DragInfo;\r
127 \r
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
129 \r
130 typedef struct {\r
131   POINT sq[2];    /* board coordinates of from, to squares */\r
132 } HighlightInfo;\r
133 \r
134 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
136 \r
137 typedef struct { // [HGM] atomic\r
138   int fromX, fromY, toX, toY, radius;\r
139 } ExplodeInfo;\r
140 \r
141 static ExplodeInfo explodeInfo;\r
142 \r
143 /* Window class names */\r
144 char szAppName[] = "WinBoard";\r
145 char szConsoleName[] = "WBConsole";\r
146 \r
147 /* Title bar text */\r
148 char szTitle[] = "WinBoard";\r
149 char szConsoleTitle[] = "I C S Interaction";\r
150 \r
151 char *programName;\r
152 char *settingsFileName;\r
153 BOOLEAN saveSettingsOnExit;\r
154 char installDir[MSG_SIZ];\r
155 \r
156 BoardSize boardSize;\r
157 BOOLEAN chessProgram;\r
158 static int boardX, boardY;\r
159 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
160 static int squareSize, lineGap, minorSize;\r
161 static int winWidth, winHeight, winW, winH;\r
162 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
163 static int logoHeight = 0;\r
164 static char messageText[MESSAGE_TEXT_MAX];\r
165 static int clockTimerEvent = 0;\r
166 static int loadGameTimerEvent = 0;\r
167 static int analysisTimerEvent = 0;\r
168 static DelayedEventCallback delayedTimerCallback;\r
169 static int delayedTimerEvent = 0;\r
170 static int buttonCount = 2;\r
171 char *icsTextMenuString;\r
172 char *icsNames;\r
173 char *firstChessProgramNames;\r
174 char *secondChessProgramNames;\r
175 \r
176 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
177 \r
178 #define PALETTESIZE 256\r
179 \r
180 HINSTANCE hInst;          /* current instance */\r
181 HWND hwndMain = NULL;        /* root window*/\r
182 HWND hwndConsole = NULL;\r
183 BOOLEAN alwaysOnTop = FALSE;\r
184 RECT boardRect;\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
186   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
187 HPALETTE hPal;\r
188 ColorClass currentColorClass;\r
189 \r
190 HWND hCommPort = NULL;    /* currently open comm port */\r
191 static HWND hwndPause;    /* pause button */\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,\r
194   blackSquareBrush, /* [HGM] for band between board and holdings */\r
195   explodeBrush,     /* [HGM] atomic */\r
196   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
197 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
198 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
199 static HPEN gridPen = NULL;\r
200 static HPEN highlightPen = NULL;\r
201 static HPEN premovePen = NULL;\r
202 static NPLOGPALETTE pLogPal;\r
203 static BOOL paletteChanged = FALSE;\r
204 static HICON iconWhite, iconBlack, iconCurrent;\r
205 static int doingSizing = FALSE;\r
206 static int lastSizing = 0;\r
207 static int prevStderrPort;\r
208 static HBITMAP userLogo;\r
209 \r
210 /* [AS] Support for background textures */\r
211 #define BACK_TEXTURE_MODE_DISABLED      0\r
212 #define BACK_TEXTURE_MODE_PLAIN         1\r
213 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 #define oldDialog (_winmajor < 4)\r
226 #endif\r
227 \r
228 char *defaultTextAttribs[] = \r
229 {\r
230   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
231   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
232   COLOR_NONE\r
233 };\r
234 \r
235 typedef struct {\r
236   char *name;\r
237   int squareSize;\r
238   int lineGap;\r
239   int smallLayout;\r
240   int tinyLayout;\r
241   int cliWidth, cliHeight;\r
242 } SizeInfo;\r
243 \r
244 SizeInfo sizeInfo[] = \r
245 {\r
246   { "tiny",     21, 0, 1, 1, 0, 0 },\r
247   { "teeny",    25, 1, 1, 1, 0, 0 },\r
248   { "dinky",    29, 1, 1, 1, 0, 0 },\r
249   { "petite",   33, 1, 1, 1, 0, 0 },\r
250   { "slim",     37, 2, 1, 0, 0, 0 },\r
251   { "small",    40, 2, 1, 0, 0, 0 },\r
252   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
253   { "middling", 49, 2, 0, 0, 0, 0 },\r
254   { "average",  54, 2, 0, 0, 0, 0 },\r
255   { "moderate", 58, 3, 0, 0, 0, 0 },\r
256   { "medium",   64, 3, 0, 0, 0, 0 },\r
257   { "bulky",    72, 3, 0, 0, 0, 0 },\r
258   { "large",    80, 3, 0, 0, 0, 0 },\r
259   { "big",      87, 3, 0, 0, 0, 0 },\r
260   { "huge",     95, 3, 0, 0, 0, 0 },\r
261   { "giant",    108, 3, 0, 0, 0, 0 },\r
262   { "colossal", 116, 4, 0, 0, 0, 0 },\r
263   { "titanic",  129, 4, 0, 0, 0, 0 },\r
264   { NULL, 0, 0, 0, 0, 0, 0 }\r
265 };\r
266 \r
267 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
268 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
269 {\r
270   { 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
271   { 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
272   { 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
273   { 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
274   { 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
275   { 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
276   { 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
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283   { 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
284   { 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
285   { 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
286   { 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
287   { 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
288 };\r
289 \r
290 MyFont *font[NUM_SIZES][NUM_FONTS];\r
291 \r
292 typedef struct {\r
293   char *label;\r
294   int id;\r
295   HWND hwnd;\r
296   WNDPROC wndproc;\r
297 } MyButtonDesc;\r
298 \r
299 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
300 #define N_BUTTONS 5\r
301 \r
302 MyButtonDesc buttonDesc[N_BUTTONS] =\r
303 {\r
304   {"<<", IDM_ToStart, NULL, NULL},\r
305   {"<", IDM_Backward, NULL, NULL},\r
306   {"P", IDM_Pause, NULL, NULL},\r
307   {">", IDM_Forward, NULL, NULL},\r
308   {">>", IDM_ToEnd, NULL, NULL},\r
309 };\r
310 \r
311 int tinyLayout = 0, smallLayout = 0;\r
312 #define MENU_BAR_ITEMS 7\r
313 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
314   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
315   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
316 };\r
317 \r
318 \r
319 MySound sounds[(int)NSoundClasses];\r
320 MyTextAttribs textAttribs[(int)NColorClasses];\r
321 \r
322 MyColorizeAttribs colorizeAttribs[] = {\r
323   { (COLORREF)0, 0, "Shout Text" },\r
324   { (COLORREF)0, 0, "SShout/CShout" },\r
325   { (COLORREF)0, 0, "Channel 1 Text" },\r
326   { (COLORREF)0, 0, "Channel Text" },\r
327   { (COLORREF)0, 0, "Kibitz Text" },\r
328   { (COLORREF)0, 0, "Tell Text" },\r
329   { (COLORREF)0, 0, "Challenge Text" },\r
330   { (COLORREF)0, 0, "Request Text" },\r
331   { (COLORREF)0, 0, "Seek Text" },\r
332   { (COLORREF)0, 0, "Normal Text" },\r
333   { (COLORREF)0, 0, "None" }\r
334 };\r
335 \r
336 \r
337 \r
338 static char *commentTitle;\r
339 static char *commentText;\r
340 static int commentIndex;\r
341 static Boolean editComment = FALSE;\r
342 HWND commentDialog = NULL;\r
343 BOOLEAN commentDialogUp = FALSE;\r
344 static int commentX, commentY, commentH, commentW;\r
345 \r
346 static char *analysisTitle;\r
347 static char *analysisText;\r
348 HWND analysisDialog = NULL;\r
349 BOOLEAN analysisDialogUp = FALSE;\r
350 static int analysisX, analysisY, analysisH, analysisW;\r
351 \r
352 char errorTitle[MSG_SIZ];\r
353 char errorMessage[2*MSG_SIZ];\r
354 HWND errorDialog = NULL;\r
355 BOOLEAN moveErrorMessageUp = FALSE;\r
356 BOOLEAN consoleEcho = TRUE;\r
357 CHARFORMAT consoleCF;\r
358 COLORREF consoleBackgroundColor;\r
359 \r
360 char *programVersion;\r
361 \r
362 #define CPReal 1\r
363 #define CPComm 2\r
364 #define CPSock 3\r
365 #define CPRcmd 4\r
366 typedef int CPKind;\r
367 \r
368 typedef struct {\r
369   CPKind kind;\r
370   HANDLE hProcess;\r
371   DWORD pid;\r
372   HANDLE hTo;\r
373   HANDLE hFrom;\r
374   SOCKET sock;\r
375   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
376 } ChildProc;\r
377 \r
378 #define INPUT_SOURCE_BUF_SIZE 4096\r
379 \r
380 typedef struct _InputSource {\r
381   CPKind kind;\r
382   HANDLE hFile;\r
383   SOCKET sock;\r
384   int lineByLine;\r
385   HANDLE hThread;\r
386   DWORD id;\r
387   char buf[INPUT_SOURCE_BUF_SIZE];\r
388   char *next;\r
389   DWORD count;\r
390   int error;\r
391   InputCallback func;\r
392   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
393   VOIDSTAR closure;\r
394 } InputSource;\r
395 \r
396 InputSource *consoleInputSource;\r
397 \r
398 DCB dcb;\r
399 \r
400 /* forward */\r
401 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
402 VOID ConsoleCreate();\r
403 LRESULT CALLBACK\r
404   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
405 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
406 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
407 VOID ParseCommSettings(char *arg, DCB *dcb);\r
408 LRESULT CALLBACK\r
409   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
410 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
411 void ParseIcsTextMenu(char *icsTextMenuString);\r
412 VOID PopUpMoveDialog(char firstchar);\r
413 VOID PopUpNameDialog(char firstchar);\r
414 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
415 \r
416 /* [AS] */\r
417 int NewGameFRC();\r
418 int GameListOptions();\r
419 \r
420 HWND moveHistoryDialog = NULL;\r
421 BOOLEAN moveHistoryDialogUp = FALSE;\r
422 \r
423 WindowPlacement wpMoveHistory;\r
424 \r
425 HWND evalGraphDialog = NULL;\r
426 BOOLEAN evalGraphDialogUp = FALSE;\r
427 \r
428 WindowPlacement wpEvalGraph;\r
429 \r
430 HWND engineOutputDialog = NULL;\r
431 BOOLEAN engineOutputDialogUp = FALSE;\r
432 \r
433 WindowPlacement wpEngineOutput;\r
434 WindowPlacement wpGameList;\r
435 WindowPlacement wpConsole;\r
436 \r
437 VOID MoveHistoryPopUp();\r
438 VOID MoveHistoryPopDown();\r
439 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
440 BOOL MoveHistoryIsUp();\r
441 \r
442 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
443 VOID EvalGraphPopUp();\r
444 VOID EvalGraphPopDown();\r
445 BOOL EvalGraphIsUp();\r
446 \r
447 VOID EngineOutputPopUp();\r
448 VOID EngineOutputPopDown();\r
449 BOOL EngineOutputIsUp();\r
450 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
451 \r
452 VOID GothicPopUp(char *title, VariantClass variant);\r
453 /*\r
454  * Setting "frozen" should disable all user input other than deleting\r
455  * the window.  We do this while engines are initializing themselves.\r
456  */\r
457 static int frozen = 0;\r
458 static int oldMenuItemState[MENU_BAR_ITEMS];\r
459 void FreezeUI()\r
460 {\r
461   HMENU hmenu;\r
462   int i;\r
463 \r
464   if (frozen) return;\r
465   frozen = 1;\r
466   hmenu = GetMenu(hwndMain);\r
467   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
468     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
469   }\r
470   DrawMenuBar(hwndMain);\r
471 }\r
472 \r
473 /* Undo a FreezeUI */\r
474 void ThawUI()\r
475 {\r
476   HMENU hmenu;\r
477   int i;\r
478 \r
479   if (!frozen) return;\r
480   frozen = 0;\r
481   hmenu = GetMenu(hwndMain);\r
482   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
483     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
484   }\r
485   DrawMenuBar(hwndMain);\r
486 }\r
487 \r
488 static int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
489 \r
490 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
491 #ifdef JAWS\r
492 #include "jaws.c"\r
493 #else\r
494 #define JAWS_INIT\r
495 #define JAWS_ALT_INTERCEPT\r
496 #define JAWS_KB_NAVIGATION\r
497 #define JAWS_MENU_ITEMS\r
498 #define JAWS_SILENCE\r
499 #define JAWS_REPLAY\r
500 #define JAWS_ACCEL\r
501 #define JAWS_COPYRIGHT\r
502 #define JAWS_DELETE(X) X\r
503 #define SAYMACHINEMOVE()\r
504 #define SAY(X)\r
505 #endif\r
506 \r
507 /*---------------------------------------------------------------------------*\\r
508  *\r
509  * WinMain\r
510  *\r
511 \*---------------------------------------------------------------------------*/\r
512 \r
513 int APIENTRY\r
514 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
515         LPSTR lpCmdLine, int nCmdShow)\r
516 {\r
517   MSG msg;\r
518   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
519 //  INITCOMMONCONTROLSEX ex;\r
520 \r
521   debugFP = stderr;\r
522 \r
523   LoadLibrary("RICHED32.DLL");\r
524   consoleCF.cbSize = sizeof(CHARFORMAT);\r
525 \r
526   if (!InitApplication(hInstance)) {\r
527     return (FALSE);\r
528   }\r
529   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
530     return (FALSE);\r
531   }\r
532 \r
533   JAWS_INIT\r
534 \r
535 //  InitCommonControlsEx(&ex);\r
536   InitCommonControls();\r
537 \r
538   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
539   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
540   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
541 \r
542   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
543 \r
544   while (GetMessage(&msg, /* message structure */\r
545                     NULL, /* handle of window receiving the message */\r
546                     0,    /* lowest message to examine */\r
547                     0))   /* highest message to examine */\r
548     {\r
549 \r
550       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
551         // [HGM] navigate: switch between all windows with tab\r
552         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
553         int i, currentElement = 0;\r
554 \r
555         // first determine what element of the chain we come from (if any)\r
556         if(appData.icsActive) {\r
557             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
558             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
559         }\r
560         if(engineOutputDialog && EngineOutputIsUp()) {\r
561             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
562             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
563         }\r
564         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
565             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
566         }\r
567         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
568         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
569         if(msg.hwnd == e1)                 currentElement = 2; else\r
570         if(msg.hwnd == e2)                 currentElement = 3; else\r
571         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
572         if(msg.hwnd == mh)                currentElement = 4; else\r
573         if(msg.hwnd == evalGraphDialog)    currentElement = 7; else\r
574         if(msg.hwnd == hText)  currentElement = 5; else\r
575         if(msg.hwnd == hInput) currentElement = 6; else\r
576         for (i = 0; i < N_BUTTONS; i++) {\r
577             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
578         }\r
579 \r
580         // determine where to go to\r
581         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
582           do {\r
583             currentElement = (currentElement + direction) % 7;\r
584             switch(currentElement) {\r
585                 case 0:\r
586                   h = hwndMain; break; // passing this case always makes the loop exit\r
587                 case 1:\r
588                   h = buttonDesc[0].hwnd; break; // could be NULL\r
589                 case 2:\r
590                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
591                   h = e1; break;\r
592                 case 3:\r
593                   if(!EngineOutputIsUp()) continue;\r
594                   h = e2; break;\r
595                 case 4:\r
596                   if(!MoveHistoryIsUp()) continue;\r
597                   h = mh; break;\r
598 //              case 5: // input to eval graph does not seem to get here!\r
599 //                if(!EvalGraphIsUp()) continue;\r
600 //                h = evalGraphDialog; break;\r
601                 case 5:\r
602                   if(!appData.icsActive) continue;\r
603                   SAY("display");\r
604                   h = hText; break;\r
605                 case 6:\r
606                   if(!appData.icsActive) continue;\r
607                   SAY("input");\r
608                   h = hInput; break;\r
609             }\r
610           } while(h == 0);\r
611 \r
612           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
613           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
614           SetFocus(h);\r
615 \r
616           continue; // this message now has been processed\r
617         }\r
618       }\r
619 \r
620       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
621           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
622           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
623           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
624           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
625           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
626           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
627           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
628           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
629           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
630         TranslateMessage(&msg); /* Translates virtual key codes */\r
631         DispatchMessage(&msg);  /* Dispatches message to window */\r
632       }\r
633     }\r
634 \r
635 \r
636   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
637 }\r
638 \r
639 /*---------------------------------------------------------------------------*\\r
640  *\r
641  * Initialization functions\r
642  *\r
643 \*---------------------------------------------------------------------------*/\r
644 \r
645 void\r
646 SetUserLogo()\r
647 {   // update user logo if necessary\r
648     static char oldUserName[MSG_SIZ], *curName;\r
649 \r
650     if(appData.autoLogo) {\r
651           curName = UserName();\r
652           if(strcmp(curName, oldUserName)) {\r
653                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
654                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
655                 strcpy(oldUserName, curName);\r
656           }\r
657     }\r
658 }\r
659 \r
660 BOOL\r
661 InitApplication(HINSTANCE hInstance)\r
662 {\r
663   WNDCLASS wc;\r
664 \r
665   /* Fill in window class structure with parameters that describe the */\r
666   /* main window. */\r
667 \r
668   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
669   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
670   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
671   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
672   wc.hInstance     = hInstance;         /* Owner of this class */\r
673   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
674   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
675   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
676   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
677   wc.lpszClassName = szAppName;                 /* Name to register as */\r
678 \r
679   /* Register the window class and return success/failure code. */\r
680   if (!RegisterClass(&wc)) return FALSE;\r
681 \r
682   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
683   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
684   wc.cbClsExtra    = 0;\r
685   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
686   wc.hInstance     = hInstance;\r
687   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
688   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
689   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
690   wc.lpszMenuName  = NULL;\r
691   wc.lpszClassName = szConsoleName;\r
692 \r
693   if (!RegisterClass(&wc)) return FALSE;\r
694   return TRUE;\r
695 }\r
696 \r
697 \r
698 /* Set by InitInstance, used by EnsureOnScreen */\r
699 int screenHeight, screenWidth;\r
700 \r
701 void\r
702 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
703 {\r
704 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
705   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
706   if (*x > screenWidth - 32) *x = 0;\r
707   if (*y > screenHeight - 32) *y = 0;\r
708   if (*x < minX) *x = minX;\r
709   if (*y < minY) *y = minY;\r
710 }\r
711 \r
712 BOOL\r
713 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
714 {\r
715   HWND hwnd; /* Main window handle. */\r
716   int ibs;\r
717   WINDOWPLACEMENT wp;\r
718   char *filepart;\r
719 \r
720   hInst = hInstance;    /* Store instance handle in our global variable */\r
721 \r
722   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
723     *filepart = NULLCHAR;\r
724   } else {\r
725     GetCurrentDirectory(MSG_SIZ, installDir);\r
726   }\r
727   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
728   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
729   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
730   if (appData.debugMode) {\r
731     debugFP = fopen(appData.nameOfDebugFile, "w");\r
732     setbuf(debugFP, NULL);\r
733   }\r
734 \r
735   InitBackEnd1();\r
736 \r
737 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
738 //  InitEngineUCI( installDir, &second );\r
739 \r
740   /* Create a main window for this application instance. */\r
741   hwnd = CreateWindow(szAppName, szTitle,\r
742                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
743                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
744                       NULL, NULL, hInstance, NULL);\r
745   hwndMain = hwnd;\r
746 \r
747   /* If window could not be created, return "failure" */\r
748   if (!hwnd) {\r
749     return (FALSE);\r
750   }\r
751 \r
752   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
753   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
754       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
755 \r
756       if (first.programLogo == NULL && appData.debugMode) {\r
757           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
758       }\r
759   } else if(appData.autoLogo) {\r
760       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
761         char buf[MSG_SIZ];\r
762         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
763         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
764       }\r
765   }\r
766 \r
767   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
768       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
769 \r
770       if (second.programLogo == NULL && appData.debugMode) {\r
771           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
772       }\r
773   } else if(appData.autoLogo) {\r
774       char buf[MSG_SIZ];\r
775       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
776         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
777         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
778       } else\r
779       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
780         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
781         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
782       }\r
783   }\r
784 \r
785   SetUserLogo();\r
786 \r
787   iconWhite = LoadIcon(hInstance, "icon_white");\r
788   iconBlack = LoadIcon(hInstance, "icon_black");\r
789   iconCurrent = iconWhite;\r
790   InitDrawingColors();\r
791   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
792   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
793   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
794     /* Compute window size for each board size, and use the largest\r
795        size that fits on this screen as the default. */\r
796     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
797     if (boardSize == (BoardSize)-1 &&\r
798         winH <= screenHeight\r
799            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
800         && winW <= screenWidth) {\r
801       boardSize = (BoardSize)ibs;\r
802     }\r
803   }\r
804 \r
805   InitDrawingSizes(boardSize, 0);\r
806   InitMenuChecks();\r
807   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
808 \r
809   /* [AS] Load textures if specified */\r
810   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
811   \r
812   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
813       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
814       liteBackTextureMode = appData.liteBackTextureMode;\r
815 \r
816       if (liteBackTexture == NULL && appData.debugMode) {\r
817           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
818       }\r
819   }\r
820   \r
821   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
822       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
823       darkBackTextureMode = appData.darkBackTextureMode;\r
824 \r
825       if (darkBackTexture == NULL && appData.debugMode) {\r
826           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
827       }\r
828   }\r
829 \r
830   mysrandom( (unsigned) time(NULL) );\r
831 \r
832   /* [AS] Restore layout */\r
833   if( wpMoveHistory.visible ) {\r
834       MoveHistoryPopUp();\r
835   }\r
836 \r
837   if( wpEvalGraph.visible ) {\r
838       EvalGraphPopUp();\r
839   }\r
840 \r
841   if( wpEngineOutput.visible ) {\r
842       EngineOutputPopUp();\r
843   }\r
844 \r
845   InitBackEnd2();\r
846 \r
847   /* Make the window visible; update its client area; and return "success" */\r
848   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
849   wp.length = sizeof(WINDOWPLACEMENT);\r
850   wp.flags = 0;\r
851   wp.showCmd = nCmdShow;\r
852   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
853   wp.rcNormalPosition.left = boardX;\r
854   wp.rcNormalPosition.right = boardX + winWidth;\r
855   wp.rcNormalPosition.top = boardY;\r
856   wp.rcNormalPosition.bottom = boardY + winHeight;\r
857   SetWindowPlacement(hwndMain, &wp);\r
858 \r
859   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
860                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
861 \r
862   if (hwndConsole) {\r
863 #if AOT_CONSOLE\r
864     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
865                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
866 #endif\r
867     ShowWindow(hwndConsole, nCmdShow);\r
868   }\r
869   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
870 \r
871   return TRUE;\r
872 \r
873 }\r
874 \r
875 \r
876 typedef enum {\r
877   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
878   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
879   ArgSettingsFilename,\r
880   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
881 } ArgType;\r
882 \r
883 typedef struct {\r
884   char *argName;\r
885   ArgType argType;\r
886   /***\r
887   union {\r
888     String *pString;       // ArgString\r
889     int *pInt;             // ArgInt\r
890     float *pFloat;         // ArgFloat\r
891     Boolean *pBoolean;     // ArgBoolean\r
892     COLORREF *pColor;      // ArgColor\r
893     ColorClass cc;         // ArgAttribs\r
894     String *pFilename;     // ArgFilename\r
895     BoardSize *pBoardSize; // ArgBoardSize\r
896     int whichFont;         // ArgFont\r
897     DCB *pDCB;             // ArgCommSettings\r
898     String *pFilename;     // ArgSettingsFilename\r
899   } argLoc;\r
900   ***/\r
901   LPVOID argLoc;\r
902   BOOL save;\r
903 } ArgDescriptor;\r
904 \r
905 int junk;\r
906 ArgDescriptor argDescriptors[] = {\r
907   /* positional arguments */\r
908   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
909   { "", ArgNone, NULL },\r
910   /* keyword arguments */\r
911   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
912   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
913   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
914   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
915   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
916   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
917   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
918   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
919   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
920   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
921   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
922   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
923   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
924   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
925   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
926   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
927   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
928   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
929     FALSE },\r
930   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
931     FALSE },\r
932   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
933     FALSE },\r
934   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
935   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
936     FALSE },\r
937   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
938   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
939   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
940   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
941   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
942   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
943   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
944   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
945   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
946   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
947   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
948   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
949   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
950   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
951   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
952   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
953   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
954   /*!!bitmapDirectory?*/\r
955   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
956   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
957   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
958   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
959   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
960   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
961   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
962   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
963   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
964   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
965   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
966   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
967   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
968   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
969   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
970   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
971   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
972   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
973   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
974   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
975   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
976   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
977   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
978   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
979   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
980   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
981   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
982   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
983   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
984   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
985   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
986   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
987   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
988   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
989   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
990   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
991   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
992   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
993   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
994   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
995   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
996   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
997   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
998   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
999   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1000   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1001   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1002   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
1003   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1004   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1005   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1006   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1007   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1008   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1009   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1010   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1011   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1012   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1013   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1014   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1015   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1016   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1017   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1018   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1019   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1020   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1021   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1022   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1023   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1024   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1025   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1026   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1027   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1028   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1029   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1030   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1031   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1032   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1033   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1034   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1035   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1036   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1037   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1038   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1039   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1040   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1041   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1042   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1043   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1044   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1045   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1046   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1047   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1048   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1049     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1050   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1051   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1052   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1053   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1054   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1055   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1056   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1057   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1058     TRUE }, /* must come after all fonts */\r
1059   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1060   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1061     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1062   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1063   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1064   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1065   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1066   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1067   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1068   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1069   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1070   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1071   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1072   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1073   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1074   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1075   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1076   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1077   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1078   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1079   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1080   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1081   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1082   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1083   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1084   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1085   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1086   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1087   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1088   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1089   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1090 #if 0\r
1091   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1092   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1093 #endif\r
1094   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1095   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1096   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1097   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1098   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1099   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1100   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1101   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1102   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1103   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1104   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1105   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1106   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1107   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1108   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1109   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1110   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1111   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1112   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1113   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1114   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1115   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1116   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1117   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1118   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1119   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1120   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1121   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1122   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1123   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1124   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1125   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1126   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1127   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1128   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1129   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1130   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1131   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1132   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1133   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1134   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1135   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1136   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1137   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1138   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1139   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1140   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1141   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1142   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1143   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1144   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1145   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1146   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1147   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1148   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1149   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1150   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1151   { "highlightLastMove", ArgBoolean,\r
1152     (LPVOID) &appData.highlightLastMove, TRUE },\r
1153   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1154   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1155   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1156   { "highlightDragging", ArgBoolean,\r
1157     (LPVOID) &appData.highlightDragging, TRUE },\r
1158   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1159   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1160   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1161   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1162   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1163   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1164   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1165   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1166   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1167   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1168   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1169   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1170   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1171   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1172   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1173   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1174   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1175   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1176   { "soundShout", ArgFilename,\r
1177     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1178   { "soundSShout", ArgFilename,\r
1179     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1180   { "soundChannel1", ArgFilename,\r
1181     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1182   { "soundChannel", ArgFilename,\r
1183     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1184   { "soundKibitz", ArgFilename,\r
1185     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1186   { "soundTell", ArgFilename,\r
1187     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1188   { "soundChallenge", ArgFilename,\r
1189     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1190   { "soundRequest", ArgFilename,\r
1191     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1192   { "soundSeek", ArgFilename,\r
1193     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1194   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1195   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1196   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1197   { "soundIcsLoss", ArgFilename, \r
1198     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1199   { "soundIcsDraw", ArgFilename, \r
1200     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1201   { "soundIcsUnfinished", ArgFilename, \r
1202     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1203   { "soundIcsAlarm", ArgFilename, \r
1204     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1205   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1206   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1207   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1208   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1209   { "reuseChessPrograms", ArgBoolean,\r
1210     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1211   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1212   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1213   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1214   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1215   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1216   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1217   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1218   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1219   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1220   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1221   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1222   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1223   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1224   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1225   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1226     TRUE },\r
1227   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1228     TRUE },\r
1229   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1230   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1231   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1232   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1233   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1234   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1235   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1236   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1237   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1238   /* [AS] New features */\r
1239   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1240   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1241   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1242   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1243   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1244   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1245   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1246   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1247   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1248   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1249   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1250   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1251   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1252   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1253   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1254   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1255   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1256   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1257   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1258   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1259   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1260   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1261   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1262   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1263   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1264   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1265   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1266   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1267   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1268   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1269   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1270   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1271   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1272   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1273   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1274   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1275   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1276   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1277   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1278   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1279   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1280   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1281   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1282   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1283   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1284   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1285   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1286   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1287   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1288   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1289 \r
1290   /* [HGM] board-size, adjudication and misc. options */\r
1291   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1292   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1293   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1294   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1295   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1296   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1297   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1298   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1299   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1300   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1301   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1302   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1303   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1304   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1305   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1306   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1307   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1308   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1309   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1310   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1311   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1312   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1313   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1314   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1315   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1316   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1317   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1318   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1319   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1320   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1321   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1322 \r
1323 #ifdef ZIPPY\r
1324   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1325   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1326   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1327   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1328   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1329   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1330   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1331   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1332   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1333   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1334   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1335   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1336   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1337     FALSE },\r
1338   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1339   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1340   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1341   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1342   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1343   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1344   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1345     FALSE },\r
1346   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1347   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1348   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1349   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1350   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1351   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1352   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1353   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1354   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1355   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1356   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1357   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1358   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1359   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1360   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1361   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1362   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1363   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1364   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1365 #endif\r
1366   /* [HGM] options for broadcasting and time odds */\r
1367   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1368   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1369   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1370   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1371   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1372   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1373   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1374   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1375   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1376   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1377   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1378 \r
1379   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1380   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1381   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1382   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1383   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1384   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1385   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1386   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1387   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1388   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1389   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1390   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1391   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1392   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1393   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1394   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1395   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1396   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1397   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1398   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1399   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1400   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1401   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1402   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1403   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1404   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1405   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1406   /* [AS] Layout stuff */\r
1407   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1408   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1409   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1410   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1411   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1412 \r
1413   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1414   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1415   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1416   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1417   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1418 \r
1419   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1420   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1421   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1422   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1423   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1424 \r
1425   { NULL, ArgNone, NULL, FALSE }\r
1426 };\r
1427 \r
1428 \r
1429 /* Kludge for indirection files on command line */\r
1430 char* lastIndirectionFilename;\r
1431 ArgDescriptor argDescriptorIndirection =\r
1432 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1433 \r
1434 \r
1435 VOID\r
1436 ExitArgError(char *msg, char *badArg)\r
1437 {\r
1438   char buf[MSG_SIZ];\r
1439 \r
1440   sprintf(buf, "%s %s", msg, badArg);\r
1441   DisplayFatalError(buf, 0, 2);\r
1442   exit(2);\r
1443 }\r
1444 \r
1445 /* Command line font name parser.  NULL name means do nothing.\r
1446    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1447    For backward compatibility, syntax without the colon is also\r
1448    accepted, but font names with digits in them won't work in that case.\r
1449 */\r
1450 VOID\r
1451 ParseFontName(char *name, MyFontParams *mfp)\r
1452 {\r
1453   char *p, *q;\r
1454   if (name == NULL) return;\r
1455   p = name;\r
1456   q = strchr(p, ':');\r
1457   if (q) {\r
1458     if (q - p >= sizeof(mfp->faceName))\r
1459       ExitArgError("Font name too long:", name);\r
1460     memcpy(mfp->faceName, p, q - p);\r
1461     mfp->faceName[q - p] = NULLCHAR;\r
1462     p = q + 1;\r
1463   } else {\r
1464     q = mfp->faceName;\r
1465     while (*p && !isdigit(*p)) {\r
1466       *q++ = *p++;\r
1467       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1468         ExitArgError("Font name too long:", name);\r
1469     }\r
1470     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1471     *q = NULLCHAR;\r
1472   }\r
1473   if (!*p) ExitArgError("Font point size missing:", name);\r
1474   mfp->pointSize = (float) atof(p);\r
1475   mfp->bold = (strchr(p, 'b') != NULL);\r
1476   mfp->italic = (strchr(p, 'i') != NULL);\r
1477   mfp->underline = (strchr(p, 'u') != NULL);\r
1478   mfp->strikeout = (strchr(p, 's') != NULL);\r
1479 }\r
1480 \r
1481 /* Color name parser.\r
1482    X version accepts X color names, but this one\r
1483    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1484 COLORREF\r
1485 ParseColorName(char *name)\r
1486 {\r
1487   int red, green, blue, count;\r
1488   char buf[MSG_SIZ];\r
1489 \r
1490   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1491   if (count != 3) {\r
1492     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1493       &red, &green, &blue);\r
1494   }\r
1495   if (count != 3) {\r
1496     sprintf(buf, "Can't parse color name %s", name);\r
1497     DisplayError(buf, 0);\r
1498     return RGB(0, 0, 0);\r
1499   }\r
1500   return PALETTERGB(red, green, blue);\r
1501 }\r
1502 \r
1503 \r
1504 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1505 {\r
1506   char *e = argValue;\r
1507   int eff = 0;\r
1508 \r
1509   while (*e) {\r
1510     if (*e == 'b')      eff |= CFE_BOLD;\r
1511     else if (*e == 'i') eff |= CFE_ITALIC;\r
1512     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1513     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1514     else if (*e == '#' || isdigit(*e)) break;\r
1515     e++;\r
1516   }\r
1517   *effects = eff;\r
1518   *color   = ParseColorName(e);\r
1519 }\r
1520 \r
1521 \r
1522 BoardSize\r
1523 ParseBoardSize(char *name)\r
1524 {\r
1525   BoardSize bs = SizeTiny;\r
1526   while (sizeInfo[bs].name != NULL) {\r
1527     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1528     bs++;\r
1529   }\r
1530   ExitArgError("Unrecognized board size value", name);\r
1531   return bs; /* not reached */\r
1532 }\r
1533 \r
1534 \r
1535 char\r
1536 StringGet(void *getClosure)\r
1537 {\r
1538   char **p = (char **) getClosure;\r
1539   return *((*p)++);\r
1540 }\r
1541 \r
1542 char\r
1543 FileGet(void *getClosure)\r
1544 {\r
1545   int c;\r
1546   FILE* f = (FILE*) getClosure;\r
1547 \r
1548   c = getc(f);\r
1549   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1550   if (c == EOF)\r
1551     return NULLCHAR;\r
1552   else\r
1553     return (char) c;\r
1554 }\r
1555 \r
1556 /* Parse settings file named "name". If file found, return the\r
1557    full name in fullname and return TRUE; else return FALSE */\r
1558 BOOLEAN\r
1559 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1560 {\r
1561   char *dummy;\r
1562   FILE *f;\r
1563   int ok; char buf[MSG_SIZ];\r
1564 \r
1565   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1566   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1567     sprintf(buf, "%s.ini", name);\r
1568     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1569   }\r
1570   if (ok) {\r
1571     f = fopen(fullname, "r");\r
1572     if (f != NULL) {\r
1573       ParseArgs(FileGet, f);\r
1574       fclose(f);\r
1575       return TRUE;\r
1576     }\r
1577   }\r
1578   return FALSE;\r
1579 }\r
1580 \r
1581 VOID\r
1582 ParseArgs(GetFunc get, void *cl)\r
1583 {\r
1584   char argName[ARG_MAX];\r
1585   char argValue[ARG_MAX];\r
1586   ArgDescriptor *ad;\r
1587   char start;\r
1588   char *q;\r
1589   int i, octval;\r
1590   char ch;\r
1591   int posarg = 0;\r
1592 \r
1593   ch = get(cl);\r
1594   for (;;) {\r
1595     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1596     if (ch == NULLCHAR) break;\r
1597     if (ch == ';') {\r
1598       /* Comment to end of line */\r
1599       ch = get(cl);\r
1600       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1601       continue;\r
1602     } else if (ch == '/' || ch == '-') {\r
1603       /* Switch */\r
1604       q = argName;\r
1605       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1606              ch != '\n' && ch != '\t') {\r
1607         *q++ = ch;\r
1608         ch = get(cl);\r
1609       }\r
1610       *q = NULLCHAR;\r
1611 \r
1612       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1613         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1614 \r
1615       if (ad->argName == NULL)\r
1616         ExitArgError("Unrecognized argument", argName);\r
1617 \r
1618     } else if (ch == '@') {\r
1619       /* Indirection file */\r
1620       ad = &argDescriptorIndirection;\r
1621       ch = get(cl);\r
1622     } else {\r
1623       /* Positional argument */\r
1624       ad = &argDescriptors[posarg++];\r
1625       strcpy(argName, ad->argName);\r
1626     }\r
1627 \r
1628     if (ad->argType == ArgTrue) {\r
1629       *(Boolean *) ad->argLoc = TRUE;\r
1630       continue;\r
1631     }\r
1632     if (ad->argType == ArgFalse) {\r
1633       *(Boolean *) ad->argLoc = FALSE;\r
1634       continue;\r
1635     }\r
1636 \r
1637     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1638     if (ch == NULLCHAR || ch == '\n') {\r
1639       ExitArgError("No value provided for argument", argName);\r
1640     }\r
1641     q = argValue;\r
1642     if (ch == '{') {\r
1643       // Quoting with { }.  No characters have to (or can) be escaped.\r
1644       // Thus the string cannot contain a '}' character.\r
1645       start = ch;\r
1646       ch = get(cl);\r
1647       while (start) {\r
1648         switch (ch) {\r
1649         case NULLCHAR:\r
1650           start = NULLCHAR;\r
1651           break;\r
1652           \r
1653         case '}':\r
1654           ch = get(cl);\r
1655           start = NULLCHAR;\r
1656           break;\r
1657 \r
1658         default:\r
1659           *q++ = ch;\r
1660           ch = get(cl);\r
1661           break;\r
1662         }\r
1663       }   \r
1664     } else if (ch == '\'' || ch == '"') {\r
1665       // Quoting with ' ' or " ", with \ as escape character.\r
1666       // Inconvenient for long strings that may contain Windows filenames.\r
1667       start = ch;\r
1668       ch = get(cl);\r
1669       while (start) {\r
1670         switch (ch) {\r
1671         case NULLCHAR:\r
1672           start = NULLCHAR;\r
1673           break;\r
1674 \r
1675         default:\r
1676         not_special:\r
1677           *q++ = ch;\r
1678           ch = get(cl);\r
1679           break;\r
1680 \r
1681         case '\'':\r
1682         case '\"':\r
1683           if (ch == start) {\r
1684             ch = get(cl);\r
1685             start = NULLCHAR;\r
1686             break;\r
1687           } else {\r
1688             goto not_special;\r
1689           }\r
1690 \r
1691         case '\\':\r
1692           if (ad->argType == ArgFilename\r
1693               || ad->argType == ArgSettingsFilename) {\r
1694               goto not_special;\r
1695           }\r
1696           ch = get(cl);\r
1697           switch (ch) {\r
1698           case NULLCHAR:\r
1699             ExitArgError("Incomplete \\ escape in value for", argName);\r
1700             break;\r
1701           case 'n':\r
1702             *q++ = '\n';\r
1703             ch = get(cl);\r
1704             break;\r
1705           case 'r':\r
1706             *q++ = '\r';\r
1707             ch = get(cl);\r
1708             break;\r
1709           case 't':\r
1710             *q++ = '\t';\r
1711             ch = get(cl);\r
1712             break;\r
1713           case 'b':\r
1714             *q++ = '\b';\r
1715             ch = get(cl);\r
1716             break;\r
1717           case 'f':\r
1718             *q++ = '\f';\r
1719             ch = get(cl);\r
1720             break;\r
1721           default:\r
1722             octval = 0;\r
1723             for (i = 0; i < 3; i++) {\r
1724               if (ch >= '0' && ch <= '7') {\r
1725                 octval = octval*8 + (ch - '0');\r
1726                 ch = get(cl);\r
1727               } else {\r
1728                 break;\r
1729               }\r
1730             }\r
1731             if (i > 0) {\r
1732               *q++ = (char) octval;\r
1733             } else {\r
1734               *q++ = ch;\r
1735               ch = get(cl);\r
1736             }\r
1737             break;\r
1738           }\r
1739           break;\r
1740         }\r
1741       }\r
1742     } else {\r
1743       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1744         *q++ = ch;\r
1745         ch = get(cl);\r
1746       }\r
1747     }\r
1748     *q = NULLCHAR;\r
1749 \r
1750     switch (ad->argType) {\r
1751     case ArgInt:\r
1752       *(int *) ad->argLoc = atoi(argValue);\r
1753       break;\r
1754 \r
1755     case ArgX:\r
1756       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1757       break;\r
1758 \r
1759     case ArgY:\r
1760       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1761       break;\r
1762 \r
1763     case ArgZ:\r
1764       *(int *) ad->argLoc = atoi(argValue);\r
1765       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1766       break;\r
1767 \r
1768     case ArgFloat:\r
1769       *(float *) ad->argLoc = (float) atof(argValue);\r
1770       break;\r
1771 \r
1772     case ArgString:\r
1773     case ArgFilename:\r
1774       *(char **) ad->argLoc = strdup(argValue);\r
1775       break;\r
1776 \r
1777     case ArgSettingsFilename:\r
1778       {\r
1779         char fullname[MSG_SIZ];\r
1780         if (ParseSettingsFile(argValue, fullname)) {\r
1781           if (ad->argLoc != NULL) {\r
1782             *(char **) ad->argLoc = strdup(fullname);\r
1783           }\r
1784         } else {\r
1785           if (ad->argLoc != NULL) {\r
1786           } else {\r
1787             ExitArgError("Failed to open indirection file", argValue);\r
1788           }\r
1789         }\r
1790       }\r
1791       break;\r
1792 \r
1793     case ArgBoolean:\r
1794       switch (argValue[0]) {\r
1795       case 't':\r
1796       case 'T':\r
1797         *(Boolean *) ad->argLoc = TRUE;\r
1798         break;\r
1799       case 'f':\r
1800       case 'F':\r
1801         *(Boolean *) ad->argLoc = FALSE;\r
1802         break;\r
1803       default:\r
1804         ExitArgError("Unrecognized boolean argument value", argValue);\r
1805         break;\r
1806       }\r
1807       break;\r
1808 \r
1809     case ArgColor:\r
1810       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1811       break;\r
1812 \r
1813     case ArgAttribs: {\r
1814       ColorClass cc = (ColorClass)ad->argLoc;\r
1815       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1816       }\r
1817       break;\r
1818       \r
1819     case ArgBoardSize:\r
1820       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1821       break;\r
1822 \r
1823     case ArgFont:\r
1824       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1825       break;\r
1826 \r
1827     case ArgCommSettings:\r
1828       ParseCommSettings(argValue, &dcb);\r
1829       break;\r
1830 \r
1831     case ArgNone:\r
1832       ExitArgError("Unrecognized argument", argValue);\r
1833       break;\r
1834     case ArgTrue:\r
1835     case ArgFalse: ;\r
1836     }\r
1837   }\r
1838 }\r
1839 \r
1840 VOID\r
1841 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1842 {\r
1843   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1844   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1845   DeleteDC(hdc);\r
1846   lf->lfWidth = 0;\r
1847   lf->lfEscapement = 0;\r
1848   lf->lfOrientation = 0;\r
1849   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1850   lf->lfItalic = mfp->italic;\r
1851   lf->lfUnderline = mfp->underline;\r
1852   lf->lfStrikeOut = mfp->strikeout;\r
1853   lf->lfCharSet = DEFAULT_CHARSET;\r
1854   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1855   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1856   lf->lfQuality = DEFAULT_QUALITY;\r
1857   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1858   strcpy(lf->lfFaceName, mfp->faceName);\r
1859 }\r
1860 \r
1861 VOID\r
1862 CreateFontInMF(MyFont *mf)\r
1863 {\r
1864   LFfromMFP(&mf->lf, &mf->mfp);\r
1865   if (mf->hf) DeleteObject(mf->hf);\r
1866   mf->hf = CreateFontIndirect(&mf->lf);\r
1867 }\r
1868 \r
1869 VOID\r
1870 SetDefaultTextAttribs()\r
1871 {\r
1872   ColorClass cc;\r
1873   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1874     ParseAttribs(&textAttribs[cc].color, \r
1875                  &textAttribs[cc].effects, \r
1876                  defaultTextAttribs[cc]);\r
1877   }\r
1878 }\r
1879 \r
1880 VOID\r
1881 SetDefaultSounds()\r
1882 {\r
1883   ColorClass cc;\r
1884   SoundClass sc;\r
1885   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1886     textAttribs[cc].sound.name = strdup("");\r
1887     textAttribs[cc].sound.data = NULL;\r
1888   }\r
1889   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1890     sounds[sc].name = strdup("");\r
1891     sounds[sc].data = NULL;\r
1892   }\r
1893   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1894 }\r
1895 \r
1896 VOID\r
1897 LoadAllSounds()\r
1898 {\r
1899   ColorClass cc;\r
1900   SoundClass sc;\r
1901   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1902     MyLoadSound(&textAttribs[cc].sound);\r
1903   }\r
1904   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1905     MyLoadSound(&sounds[sc]);\r
1906   }\r
1907 }\r
1908 \r
1909 VOID\r
1910 InitAppData(LPSTR lpCmdLine)\r
1911 {\r
1912   int i, j;\r
1913   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1914   char *dummy, *p;\r
1915 \r
1916   programName = szAppName;\r
1917 \r
1918   /* Initialize to defaults */\r
1919   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1920   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1921   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1922   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1923   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1924   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1925   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1926   SetDefaultTextAttribs();\r
1927   SetDefaultSounds();\r
1928   appData.movesPerSession = MOVES_PER_SESSION;\r
1929   appData.initString = INIT_STRING;\r
1930   appData.secondInitString = INIT_STRING;\r
1931   appData.firstComputerString = COMPUTER_STRING;\r
1932   appData.secondComputerString = COMPUTER_STRING;\r
1933   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1934   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1935   appData.firstPlaysBlack = FALSE;\r
1936   appData.noChessProgram = FALSE;\r
1937   chessProgram = FALSE;\r
1938   appData.firstHost = FIRST_HOST;\r
1939   appData.secondHost = SECOND_HOST;\r
1940   appData.firstDirectory = FIRST_DIRECTORY;\r
1941   appData.secondDirectory = SECOND_DIRECTORY;\r
1942   appData.bitmapDirectory = "";\r
1943   appData.remoteShell = REMOTE_SHELL;\r
1944   appData.remoteUser = "";\r
1945   appData.timeDelay = TIME_DELAY;\r
1946   appData.timeControl = TIME_CONTROL;\r
1947   appData.timeIncrement = TIME_INCREMENT;\r
1948   appData.icsActive = FALSE;\r
1949   appData.icsHost = "";\r
1950   appData.icsPort = ICS_PORT;\r
1951   appData.icsCommPort = ICS_COMM_PORT;\r
1952   appData.icsLogon = ICS_LOGON;\r
1953   appData.icsHelper = "";\r
1954   appData.useTelnet = FALSE;\r
1955   appData.telnetProgram = TELNET_PROGRAM;\r
1956   appData.gateway = "";\r
1957   appData.loadGameFile = "";\r
1958   appData.loadGameIndex = 0;\r
1959   appData.saveGameFile = "";\r
1960   appData.autoSaveGames = FALSE;\r
1961   appData.loadPositionFile = "";\r
1962   appData.loadPositionIndex = 1;\r
1963   appData.savePositionFile = "";\r
1964   appData.matchMode = FALSE;\r
1965   appData.matchGames = 0;\r
1966   appData.monoMode = FALSE;\r
1967   appData.debugMode = FALSE;\r
1968   appData.clockMode = TRUE;\r
1969   boardSize = (BoardSize) -1; /* determine by screen size */\r
1970   appData.Iconic = FALSE; /*unused*/\r
1971   appData.searchTime = "";\r
1972   appData.searchDepth = 0;\r
1973   appData.showCoords = FALSE;\r
1974   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1975   appData.autoCallFlag = FALSE;\r
1976   appData.flipView = FALSE;\r
1977   appData.autoFlipView = TRUE;\r
1978   appData.cmailGameName = "";\r
1979   appData.alwaysPromoteToQueen = FALSE;\r
1980   appData.oldSaveStyle = FALSE;\r
1981   appData.quietPlay = FALSE;\r
1982   appData.showThinking = FALSE;\r
1983   appData.ponderNextMove = TRUE;\r
1984   appData.periodicUpdates = TRUE;\r
1985   appData.popupExitMessage = TRUE;\r
1986   appData.popupMoveErrors = FALSE;\r
1987   appData.autoObserve = FALSE;\r
1988   appData.autoComment = FALSE;\r
1989   appData.animate = TRUE;\r
1990   appData.animSpeed = 10;\r
1991   appData.animateDragging = TRUE;\r
1992   appData.highlightLastMove = TRUE;\r
1993   appData.getMoveList = TRUE;\r
1994   appData.testLegality = TRUE;\r
1995   appData.premove = TRUE;\r
1996   appData.premoveWhite = FALSE;\r
1997   appData.premoveWhiteText = "";\r
1998   appData.premoveBlack = FALSE;\r
1999   appData.premoveBlackText = "";\r
2000   appData.icsAlarm = TRUE;\r
2001   appData.icsAlarmTime = 5000;\r
2002   appData.autoRaiseBoard = TRUE;\r
2003   appData.localLineEditing = TRUE;\r
2004   appData.colorize = TRUE;\r
2005   appData.reuseFirst = TRUE;\r
2006   appData.reuseSecond = TRUE;\r
2007   appData.blindfold = FALSE;\r
2008   appData.icsEngineAnalyze = FALSE;\r
2009   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2010   dcb.DCBlength = sizeof(DCB);\r
2011   dcb.BaudRate = 9600;\r
2012   dcb.fBinary = TRUE;\r
2013   dcb.fParity = FALSE;\r
2014   dcb.fOutxCtsFlow = FALSE;\r
2015   dcb.fOutxDsrFlow = FALSE;\r
2016   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2017   dcb.fDsrSensitivity = FALSE;\r
2018   dcb.fTXContinueOnXoff = TRUE;\r
2019   dcb.fOutX = FALSE;\r
2020   dcb.fInX = FALSE;\r
2021   dcb.fNull = FALSE;\r
2022   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2023   dcb.fAbortOnError = FALSE;\r
2024   dcb.ByteSize = 7;\r
2025   dcb.Parity = SPACEPARITY;\r
2026   dcb.StopBits = ONESTOPBIT;\r
2027   settingsFileName = SETTINGS_FILE;\r
2028   saveSettingsOnExit = TRUE;\r
2029   boardX = CW_USEDEFAULT;\r
2030   boardY = CW_USEDEFAULT;\r
2031   analysisX = CW_USEDEFAULT; \r
2032   analysisY = CW_USEDEFAULT; \r
2033   analysisW = CW_USEDEFAULT;\r
2034   analysisH = CW_USEDEFAULT;\r
2035   commentX = CW_USEDEFAULT; \r
2036   commentY = CW_USEDEFAULT; \r
2037   commentW = CW_USEDEFAULT;\r
2038   commentH = CW_USEDEFAULT;\r
2039   editTagsX = CW_USEDEFAULT; \r
2040   editTagsY = CW_USEDEFAULT; \r
2041   editTagsW = CW_USEDEFAULT;\r
2042   editTagsH = CW_USEDEFAULT;\r
2043   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2044   icsNames = ICS_NAMES;\r
2045   firstChessProgramNames = FCP_NAMES;\r
2046   secondChessProgramNames = SCP_NAMES;\r
2047   appData.initialMode = "";\r
2048   appData.variant = "normal";\r
2049   appData.firstProtocolVersion = PROTOVER;\r
2050   appData.secondProtocolVersion = PROTOVER;\r
2051   appData.showButtonBar = TRUE;\r
2052 \r
2053    /* [AS] New properties (see comments in header file) */\r
2054   appData.firstScoreIsAbsolute = FALSE;\r
2055   appData.secondScoreIsAbsolute = FALSE;\r
2056   appData.saveExtendedInfoInPGN = FALSE;\r
2057   appData.hideThinkingFromHuman = FALSE;\r
2058   appData.liteBackTextureFile = "";\r
2059   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2060   appData.darkBackTextureFile = "";\r
2061   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2062   appData.renderPiecesWithFont = "";\r
2063   appData.fontToPieceTable = "";\r
2064   appData.fontBackColorWhite = 0;\r
2065   appData.fontForeColorWhite = 0;\r
2066   appData.fontBackColorBlack = 0;\r
2067   appData.fontForeColorBlack = 0;\r
2068   appData.fontPieceSize = 80;\r
2069   appData.overrideLineGap = 1;\r
2070   appData.adjudicateLossThreshold = 0;\r
2071   appData.delayBeforeQuit = 0;\r
2072   appData.delayAfterQuit = 0;\r
2073   appData.nameOfDebugFile = "winboard.debug";\r
2074   appData.pgnEventHeader = "Computer Chess Game";\r
2075   appData.defaultFrcPosition = -1;\r
2076   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2077   appData.saveOutOfBookInfo = TRUE;\r
2078   appData.showEvalInMoveHistory = TRUE;\r
2079   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2080   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2081   appData.highlightMoveWithArrow = FALSE;\r
2082   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2083   appData.useStickyWindows = TRUE;\r
2084   appData.adjudicateDrawMoves = 0;\r
2085   appData.autoDisplayComment = TRUE;\r
2086   appData.autoDisplayTags = TRUE;\r
2087   appData.firstIsUCI = FALSE;\r
2088   appData.secondIsUCI = FALSE;\r
2089   appData.firstHasOwnBookUCI = TRUE;\r
2090   appData.secondHasOwnBookUCI = TRUE;\r
2091   appData.polyglotDir = "";\r
2092   appData.usePolyglotBook = FALSE;\r
2093   appData.polyglotBook = "";\r
2094   appData.defaultHashSize = 64;\r
2095   appData.defaultCacheSizeEGTB = 4;\r
2096   appData.defaultPathEGTB = "c:\\egtb";\r
2097   appData.firstOptions = "";\r
2098   appData.secondOptions = "";\r
2099 \r
2100   InitWindowPlacement( &wpGameList );\r
2101   InitWindowPlacement( &wpMoveHistory );\r
2102   InitWindowPlacement( &wpEvalGraph );\r
2103   InitWindowPlacement( &wpEngineOutput );\r
2104   InitWindowPlacement( &wpConsole );\r
2105 \r
2106   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2107   appData.NrFiles      = -1;\r
2108   appData.NrRanks      = -1;\r
2109   appData.holdingsSize = -1;\r
2110   appData.testClaims   = FALSE;\r
2111   appData.checkMates   = FALSE;\r
2112   appData.materialDraws= FALSE;\r
2113   appData.trivialDraws = FALSE;\r
2114   appData.ruleMoves    = 51;\r
2115   appData.drawRepeats  = 6;\r
2116   appData.matchPause   = 10000;\r
2117   appData.alphaRank    = FALSE;\r
2118   appData.allWhite     = FALSE;\r
2119   appData.upsideDown   = FALSE;\r
2120   appData.serverPause  = 15;\r
2121   appData.serverMovesName   = NULL;\r
2122   appData.suppressLoadMoves = FALSE;\r
2123   appData.firstTimeOdds  = 1;\r
2124   appData.secondTimeOdds = 1;\r
2125   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2126   appData.secondAccumulateTC = 1;\r
2127   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2128   appData.secondNPS = -1;\r
2129   appData.engineComments = 1;\r
2130   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2131   appData.egtFormats = "";\r
2132 \r
2133 #ifdef ZIPPY\r
2134   appData.zippyTalk = ZIPPY_TALK;\r
2135   appData.zippyPlay = ZIPPY_PLAY;\r
2136   appData.zippyLines = ZIPPY_LINES;\r
2137   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2138   appData.zippyPassword = ZIPPY_PASSWORD;\r
2139   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2140   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2141   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2142   appData.zippyUseI = ZIPPY_USE_I;\r
2143   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2144   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2145   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2146   appData.zippyGameStart = ZIPPY_GAME_START;\r
2147   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2148   appData.zippyAbort = ZIPPY_ABORT;\r
2149   appData.zippyVariants = ZIPPY_VARIANTS;\r
2150   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2151   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2152 #endif\r
2153 \r
2154   /* Point font array elements to structures and\r
2155      parse default font names */\r
2156   for (i=0; i<NUM_FONTS; i++) {\r
2157     for (j=0; j<NUM_SIZES; j++) {\r
2158       font[j][i] = &fontRec[j][i];\r
2159       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2160     }\r
2161   }\r
2162   \r
2163   /* Parse default settings file if any */\r
2164   if (ParseSettingsFile(settingsFileName, buf)) {\r
2165     settingsFileName = strdup(buf);\r
2166   }\r
2167 \r
2168   /* Parse command line */\r
2169   ParseArgs(StringGet, &lpCmdLine);\r
2170 \r
2171   /* [HGM] make sure board size is acceptable */\r
2172   if(appData.NrFiles > BOARD_SIZE ||\r
2173      appData.NrRanks > BOARD_SIZE   )\r
2174       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2175 \r
2176   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2177    * with options from the command line, we now make an even higher priority\r
2178    * overrule by WB options attached to the engine command line. This so that\r
2179    * tournament managers can use WB options (such as /timeOdds) that follow\r
2180    * the engines.\r
2181    */\r
2182   if(appData.firstChessProgram != NULL) {\r
2183       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2184       static char *f = "first";\r
2185       char buf[MSG_SIZ], *q = buf;\r
2186       if(p != NULL) { // engine command line contains WinBoard options\r
2187           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2188           ParseArgs(StringGet, &q);\r
2189           p[-1] = 0; // cut them offengine command line\r
2190       }\r
2191   }\r
2192   // now do same for second chess program\r
2193   if(appData.secondChessProgram != NULL) {\r
2194       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2195       static char *s = "second";\r
2196       char buf[MSG_SIZ], *q = buf;\r
2197       if(p != NULL) { // engine command line contains WinBoard options\r
2198           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2199           ParseArgs(StringGet, &q);\r
2200           p[-1] = 0; // cut them offengine command line\r
2201       }\r
2202   }\r
2203 \r
2204 \r
2205   /* Propagate options that affect others */\r
2206   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2207   if (appData.icsActive || appData.noChessProgram) {\r
2208      chessProgram = FALSE;  /* not local chess program mode */\r
2209   }\r
2210 \r
2211   /* Open startup dialog if needed */\r
2212   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2213       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2214       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2215                         *appData.secondChessProgram == NULLCHAR))) {\r
2216     FARPROC lpProc;\r
2217     \r
2218     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2219     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2220     FreeProcInstance(lpProc);\r
2221   }\r
2222 \r
2223   /* Make sure save files land in the right (?) directory */\r
2224   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2225     appData.saveGameFile = strdup(buf);\r
2226   }\r
2227   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2228     appData.savePositionFile = strdup(buf);\r
2229   }\r
2230 \r
2231   /* Finish initialization for fonts and sounds */\r
2232   for (i=0; i<NUM_FONTS; i++) {\r
2233     for (j=0; j<NUM_SIZES; j++) {\r
2234       CreateFontInMF(font[j][i]);\r
2235     }\r
2236   }\r
2237   /* xboard, and older WinBoards, controlled the move sound with the\r
2238      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2239      always turn the option on (so that the backend will call us),\r
2240      then let the user turn the sound off by setting it to silence if\r
2241      desired.  To accommodate old winboard.ini files saved by old\r
2242      versions of WinBoard, we also turn off the sound if the option\r
2243      was initially set to false. */\r
2244   if (!appData.ringBellAfterMoves) {\r
2245     sounds[(int)SoundMove].name = strdup("");\r
2246     appData.ringBellAfterMoves = TRUE;\r
2247   }\r
2248   GetCurrentDirectory(MSG_SIZ, currDir);\r
2249   SetCurrentDirectory(installDir);\r
2250   LoadAllSounds();\r
2251   SetCurrentDirectory(currDir);\r
2252 \r
2253   p = icsTextMenuString;\r
2254   if (p[0] == '@') {\r
2255     FILE* f = fopen(p + 1, "r");\r
2256     if (f == NULL) {\r
2257       DisplayFatalError(p + 1, errno, 2);\r
2258       return;\r
2259     }\r
2260     i = fread(buf, 1, sizeof(buf)-1, f);\r
2261     fclose(f);\r
2262     buf[i] = NULLCHAR;\r
2263     p = buf;\r
2264   }\r
2265   ParseIcsTextMenu(strdup(p));\r
2266 }\r
2267 \r
2268 \r
2269 VOID\r
2270 InitMenuChecks()\r
2271 {\r
2272   HMENU hmenu = GetMenu(hwndMain);\r
2273 \r
2274   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2275                         MF_BYCOMMAND|((appData.icsActive &&\r
2276                                        *appData.icsCommPort != NULLCHAR) ?\r
2277                                       MF_ENABLED : MF_GRAYED));\r
2278   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2279                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2280                                      MF_CHECKED : MF_UNCHECKED));\r
2281 }\r
2282 \r
2283 \r
2284 VOID\r
2285 SaveSettings(char* name)\r
2286 {\r
2287   FILE *f;\r
2288   ArgDescriptor *ad;\r
2289   WINDOWPLACEMENT wp;\r
2290   char dir[MSG_SIZ];\r
2291 \r
2292   if (!hwndMain) return;\r
2293 \r
2294   GetCurrentDirectory(MSG_SIZ, dir);\r
2295   SetCurrentDirectory(installDir);\r
2296   f = fopen(name, "w");\r
2297   SetCurrentDirectory(dir);\r
2298   if (f == NULL) {\r
2299     DisplayError(name, errno);\r
2300     return;\r
2301   }\r
2302   fprintf(f, ";\n");\r
2303   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2304   fprintf(f, ";\n");\r
2305   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2306   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2307   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2308   fprintf(f, ";\n");\r
2309 \r
2310   wp.length = sizeof(WINDOWPLACEMENT);\r
2311   GetWindowPlacement(hwndMain, &wp);\r
2312   boardX = wp.rcNormalPosition.left;\r
2313   boardY = wp.rcNormalPosition.top;\r
2314 \r
2315   if (hwndConsole) {\r
2316     GetWindowPlacement(hwndConsole, &wp);\r
2317     wpConsole.x = wp.rcNormalPosition.left;\r
2318     wpConsole.y = wp.rcNormalPosition.top;\r
2319     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2320     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2321   }\r
2322 \r
2323   if (analysisDialog) {\r
2324     GetWindowPlacement(analysisDialog, &wp);\r
2325     analysisX = wp.rcNormalPosition.left;\r
2326     analysisY = wp.rcNormalPosition.top;\r
2327     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2328     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2329   }\r
2330 \r
2331   if (commentDialog) {\r
2332     GetWindowPlacement(commentDialog, &wp);\r
2333     commentX = wp.rcNormalPosition.left;\r
2334     commentY = wp.rcNormalPosition.top;\r
2335     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2336     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2337   }\r
2338 \r
2339   if (editTagsDialog) {\r
2340     GetWindowPlacement(editTagsDialog, &wp);\r
2341     editTagsX = wp.rcNormalPosition.left;\r
2342     editTagsY = wp.rcNormalPosition.top;\r
2343     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2344     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2345   }\r
2346 \r
2347   if (gameListDialog) {\r
2348     GetWindowPlacement(gameListDialog, &wp);\r
2349     wpGameList.x = wp.rcNormalPosition.left;\r
2350     wpGameList.y = wp.rcNormalPosition.top;\r
2351     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2352     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2353   }\r
2354 \r
2355   /* [AS] Move history */\r
2356   wpMoveHistory.visible = MoveHistoryIsUp();\r
2357   \r
2358   if( moveHistoryDialog ) {\r
2359     GetWindowPlacement(moveHistoryDialog, &wp);\r
2360     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2361     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2362     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2363     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2364   }\r
2365 \r
2366   /* [AS] Eval graph */\r
2367   wpEvalGraph.visible = EvalGraphIsUp();\r
2368 \r
2369   if( evalGraphDialog ) {\r
2370     GetWindowPlacement(evalGraphDialog, &wp);\r
2371     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2372     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2373     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2374     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2375   }\r
2376 \r
2377   /* [AS] Engine output */\r
2378   wpEngineOutput.visible = EngineOutputIsUp();\r
2379 \r
2380   if( engineOutputDialog ) {\r
2381     GetWindowPlacement(engineOutputDialog, &wp);\r
2382     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2383     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2384     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2385     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2386   }\r
2387 \r
2388   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2389     if (!ad->save) continue;\r
2390     switch (ad->argType) {\r
2391     case ArgString:\r
2392       {\r
2393         char *p = *(char **)ad->argLoc;\r
2394         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2395           /* Quote multiline values or \-containing values\r
2396              with { } if possible */\r
2397           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2398         } else {\r
2399           /* Else quote with " " */\r
2400           fprintf(f, "/%s=\"", ad->argName);\r
2401           while (*p) {\r
2402             if (*p == '\n') fprintf(f, "\n");\r
2403             else if (*p == '\r') fprintf(f, "\\r");\r
2404             else if (*p == '\t') fprintf(f, "\\t");\r
2405             else if (*p == '\b') fprintf(f, "\\b");\r
2406             else if (*p == '\f') fprintf(f, "\\f");\r
2407             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2408             else if (*p == '\"') fprintf(f, "\\\"");\r
2409             else if (*p == '\\') fprintf(f, "\\\\");\r
2410             else putc(*p, f);\r
2411             p++;\r
2412           }\r
2413           fprintf(f, "\"\n");\r
2414         }\r
2415       }\r
2416       break;\r
2417     case ArgInt:\r
2418     case ArgZ:\r
2419       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2420       break;\r
2421     case ArgX:\r
2422       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2423       break;\r
2424     case ArgY:\r
2425       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2426       break;\r
2427     case ArgFloat:\r
2428       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2429       break;\r
2430     case ArgBoolean:\r
2431       fprintf(f, "/%s=%s\n", ad->argName, \r
2432         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2433       break;\r
2434     case ArgTrue:\r
2435       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2436       break;\r
2437     case ArgFalse:\r
2438       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2439       break;\r
2440     case ArgColor:\r
2441       {\r
2442         COLORREF color = *(COLORREF *)ad->argLoc;\r
2443         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2444           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2445       }\r
2446       break;\r
2447     case ArgAttribs:\r
2448       {\r
2449         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2450         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2451           (ta->effects & CFE_BOLD) ? "b" : "",\r
2452           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2453           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2454           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2455           (ta->effects) ? " " : "",\r
2456           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2457       }\r
2458       break;\r
2459     case ArgFilename:\r
2460       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2461         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2462       } else {\r
2463         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2464       }\r
2465       break;\r
2466     case ArgBoardSize:\r
2467       fprintf(f, "/%s=%s\n", ad->argName,\r
2468               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2469       break;\r
2470     case ArgFont:\r
2471       {\r
2472         int bs;\r
2473         for (bs=0; bs<NUM_SIZES; bs++) {\r
2474           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2475           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2476           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2477             ad->argName, mfp->faceName, mfp->pointSize,\r
2478             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2479             mfp->bold ? "b" : "",\r
2480             mfp->italic ? "i" : "",\r
2481             mfp->underline ? "u" : "",\r
2482             mfp->strikeout ? "s" : "");\r
2483         }\r
2484       }\r
2485       break;\r
2486     case ArgCommSettings:\r
2487       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2488     case ArgNone:\r
2489     case ArgSettingsFilename: ;\r
2490     }\r
2491   }\r
2492   fclose(f);\r
2493 }\r
2494 \r
2495 \r
2496 \r
2497 /*---------------------------------------------------------------------------*\\r
2498  *\r
2499  * GDI board drawing routines\r
2500  *\r
2501 \*---------------------------------------------------------------------------*/\r
2502 \r
2503 /* [AS] Draw square using background texture */\r
2504 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2505 {\r
2506     XFORM   x;\r
2507 \r
2508     if( mode == 0 ) {\r
2509         return; /* Should never happen! */\r
2510     }\r
2511 \r
2512     SetGraphicsMode( dst, GM_ADVANCED );\r
2513 \r
2514     switch( mode ) {\r
2515     case 1:\r
2516         /* Identity */\r
2517         break;\r
2518     case 2:\r
2519         /* X reflection */\r
2520         x.eM11 = -1.0;\r
2521         x.eM12 = 0;\r
2522         x.eM21 = 0;\r
2523         x.eM22 = 1.0;\r
2524         x.eDx = (FLOAT) dw + dx - 1;\r
2525         x.eDy = 0;\r
2526         dx = 0;\r
2527         SetWorldTransform( dst, &x );\r
2528         break;\r
2529     case 3:\r
2530         /* Y reflection */\r
2531         x.eM11 = 1.0;\r
2532         x.eM12 = 0;\r
2533         x.eM21 = 0;\r
2534         x.eM22 = -1.0;\r
2535         x.eDx = 0;\r
2536         x.eDy = (FLOAT) dh + dy - 1;\r
2537         dy = 0;\r
2538         SetWorldTransform( dst, &x );\r
2539         break;\r
2540     case 4:\r
2541         /* X/Y flip */\r
2542         x.eM11 = 0;\r
2543         x.eM12 = 1.0;\r
2544         x.eM21 = 1.0;\r
2545         x.eM22 = 0;\r
2546         x.eDx = (FLOAT) dx;\r
2547         x.eDy = (FLOAT) dy;\r
2548         dx = 0;\r
2549         dy = 0;\r
2550         SetWorldTransform( dst, &x );\r
2551         break;\r
2552     }\r
2553 \r
2554     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2555 \r
2556     x.eM11 = 1.0;\r
2557     x.eM12 = 0;\r
2558     x.eM21 = 0;\r
2559     x.eM22 = 1.0;\r
2560     x.eDx = 0;\r
2561     x.eDy = 0;\r
2562     SetWorldTransform( dst, &x );\r
2563 \r
2564     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2565 }\r
2566 \r
2567 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2568 enum {\r
2569     PM_WP = (int) WhitePawn, \r
2570     PM_WN = (int) WhiteKnight, \r
2571     PM_WB = (int) WhiteBishop, \r
2572     PM_WR = (int) WhiteRook, \r
2573     PM_WQ = (int) WhiteQueen, \r
2574     PM_WF = (int) WhiteFerz, \r
2575     PM_WW = (int) WhiteWazir, \r
2576     PM_WE = (int) WhiteAlfil, \r
2577     PM_WM = (int) WhiteMan, \r
2578     PM_WO = (int) WhiteCannon, \r
2579     PM_WU = (int) WhiteUnicorn, \r
2580     PM_WH = (int) WhiteNightrider, \r
2581     PM_WA = (int) WhiteAngel, \r
2582     PM_WC = (int) WhiteMarshall, \r
2583     PM_WAB = (int) WhiteCardinal, \r
2584     PM_WD = (int) WhiteDragon, \r
2585     PM_WL = (int) WhiteLance, \r
2586     PM_WS = (int) WhiteCobra, \r
2587     PM_WV = (int) WhiteFalcon, \r
2588     PM_WSG = (int) WhiteSilver, \r
2589     PM_WG = (int) WhiteGrasshopper, \r
2590     PM_WK = (int) WhiteKing,\r
2591     PM_BP = (int) BlackPawn, \r
2592     PM_BN = (int) BlackKnight, \r
2593     PM_BB = (int) BlackBishop, \r
2594     PM_BR = (int) BlackRook, \r
2595     PM_BQ = (int) BlackQueen, \r
2596     PM_BF = (int) BlackFerz, \r
2597     PM_BW = (int) BlackWazir, \r
2598     PM_BE = (int) BlackAlfil, \r
2599     PM_BM = (int) BlackMan,\r
2600     PM_BO = (int) BlackCannon, \r
2601     PM_BU = (int) BlackUnicorn, \r
2602     PM_BH = (int) BlackNightrider, \r
2603     PM_BA = (int) BlackAngel, \r
2604     PM_BC = (int) BlackMarshall, \r
2605     PM_BG = (int) BlackGrasshopper, \r
2606     PM_BAB = (int) BlackCardinal,\r
2607     PM_BD = (int) BlackDragon,\r
2608     PM_BL = (int) BlackLance,\r
2609     PM_BS = (int) BlackCobra,\r
2610     PM_BV = (int) BlackFalcon,\r
2611     PM_BSG = (int) BlackSilver,\r
2612     PM_BK = (int) BlackKing\r
2613 };\r
2614 \r
2615 static HFONT hPieceFont = NULL;\r
2616 static HBITMAP hPieceMask[(int) EmptySquare];\r
2617 static HBITMAP hPieceFace[(int) EmptySquare];\r
2618 static int fontBitmapSquareSize = 0;\r
2619 static char pieceToFontChar[(int) EmptySquare] =\r
2620                               { 'p', 'n', 'b', 'r', 'q', \r
2621                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2622                       'k', 'o', 'm', 'v', 't', 'w', \r
2623                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2624                                                               'l' };\r
2625 \r
2626 extern BOOL SetCharTable( char *table, const char * map );\r
2627 /* [HGM] moved to backend.c */\r
2628 \r
2629 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2630 {\r
2631     HBRUSH hbrush;\r
2632     BYTE r1 = GetRValue( color );\r
2633     BYTE g1 = GetGValue( color );\r
2634     BYTE b1 = GetBValue( color );\r
2635     BYTE r2 = r1 / 2;\r
2636     BYTE g2 = g1 / 2;\r
2637     BYTE b2 = b1 / 2;\r
2638     RECT rc;\r
2639 \r
2640     /* Create a uniform background first */\r
2641     hbrush = CreateSolidBrush( color );\r
2642     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2643     FillRect( hdc, &rc, hbrush );\r
2644     DeleteObject( hbrush );\r
2645     \r
2646     if( mode == 1 ) {\r
2647         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2648         int steps = squareSize / 2;\r
2649         int i;\r
2650 \r
2651         for( i=0; i<steps; i++ ) {\r
2652             BYTE r = r1 - (r1-r2) * i / steps;\r
2653             BYTE g = g1 - (g1-g2) * i / steps;\r
2654             BYTE b = b1 - (b1-b2) * i / steps;\r
2655 \r
2656             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2657             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2658             FillRect( hdc, &rc, hbrush );\r
2659             DeleteObject(hbrush);\r
2660         }\r
2661     }\r
2662     else if( mode == 2 ) {\r
2663         /* Diagonal gradient, good more or less for every piece */\r
2664         POINT triangle[3];\r
2665         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2666         HBRUSH hbrush_old;\r
2667         int steps = squareSize;\r
2668         int i;\r
2669 \r
2670         triangle[0].x = squareSize - steps;\r
2671         triangle[0].y = squareSize;\r
2672         triangle[1].x = squareSize;\r
2673         triangle[1].y = squareSize;\r
2674         triangle[2].x = squareSize;\r
2675         triangle[2].y = squareSize - steps;\r
2676 \r
2677         for( i=0; i<steps; i++ ) {\r
2678             BYTE r = r1 - (r1-r2) * i / steps;\r
2679             BYTE g = g1 - (g1-g2) * i / steps;\r
2680             BYTE b = b1 - (b1-b2) * i / steps;\r
2681 \r
2682             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2683             hbrush_old = SelectObject( hdc, hbrush );\r
2684             Polygon( hdc, triangle, 3 );\r
2685             SelectObject( hdc, hbrush_old );\r
2686             DeleteObject(hbrush);\r
2687             triangle[0].x++;\r
2688             triangle[2].y++;\r
2689         }\r
2690 \r
2691         SelectObject( hdc, hpen );\r
2692     }\r
2693 }\r
2694 \r
2695 /*\r
2696     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2697     seems to work ok. The main problem here is to find the "inside" of a chess\r
2698     piece: follow the steps as explained below.\r
2699 */\r
2700 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2701 {\r
2702     HBITMAP hbm;\r
2703     HBITMAP hbm_old;\r
2704     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2705     RECT rc;\r
2706     SIZE sz;\r
2707     POINT pt;\r
2708     int backColor = whitePieceColor; \r
2709     int foreColor = blackPieceColor;\r
2710     \r
2711     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2712         backColor = appData.fontBackColorWhite;\r
2713         foreColor = appData.fontForeColorWhite;\r
2714     }\r
2715     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2716         backColor = appData.fontBackColorBlack;\r
2717         foreColor = appData.fontForeColorBlack;\r
2718     }\r
2719 \r
2720     /* Mask */\r
2721     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2722 \r
2723     hbm_old = SelectObject( hdc, hbm );\r
2724 \r
2725     rc.left = 0;\r
2726     rc.top = 0;\r
2727     rc.right = squareSize;\r
2728     rc.bottom = squareSize;\r
2729 \r
2730     /* Step 1: background is now black */\r
2731     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2732 \r
2733     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2734 \r
2735     pt.x = (squareSize - sz.cx) / 2;\r
2736     pt.y = (squareSize - sz.cy) / 2;\r
2737 \r
2738     SetBkMode( hdc, TRANSPARENT );\r
2739     SetTextColor( hdc, chroma );\r
2740     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2741     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2742 \r
2743     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2744     /* Step 3: the area outside the piece is filled with white */\r
2745 //    FloodFill( hdc, 0, 0, chroma );\r
2746     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2747     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2748     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2749     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2750     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2751     /* \r
2752         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2753         but if the start point is not inside the piece we're lost!\r
2754         There should be a better way to do this... if we could create a region or path\r
2755         from the fill operation we would be fine for example.\r
2756     */\r
2757 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2758     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2759 \r
2760     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2761         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2762         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2763 \r
2764         SelectObject( dc2, bm2 );\r
2765         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2766         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2767         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2768         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2769         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2770 \r
2771         DeleteDC( dc2 );\r
2772         DeleteObject( bm2 );\r
2773     }\r
2774 \r
2775     SetTextColor( hdc, 0 );\r
2776     /* \r
2777         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2778         draw the piece again in black for safety.\r
2779     */\r
2780     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2781 \r
2782     SelectObject( hdc, hbm_old );\r
2783 \r
2784     if( hPieceMask[index] != NULL ) {\r
2785         DeleteObject( hPieceMask[index] );\r
2786     }\r
2787 \r
2788     hPieceMask[index] = hbm;\r
2789 \r
2790     /* Face */\r
2791     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2792 \r
2793     SelectObject( hdc, hbm );\r
2794 \r
2795     {\r
2796         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2797         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2798         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2799 \r
2800         SelectObject( dc1, hPieceMask[index] );\r
2801         SelectObject( dc2, bm2 );\r
2802         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2803         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2804         \r
2805         /* \r
2806             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2807             the piece background and deletes (makes transparent) the rest.\r
2808             Thanks to that mask, we are free to paint the background with the greates\r
2809             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2810             We use this, to make gradients and give the pieces a "roundish" look.\r
2811         */\r
2812         SetPieceBackground( hdc, backColor, 2 );\r
2813         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2814 \r
2815         DeleteDC( dc2 );\r
2816         DeleteDC( dc1 );\r
2817         DeleteObject( bm2 );\r
2818     }\r
2819 \r
2820     SetTextColor( hdc, foreColor );\r
2821     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2822 \r
2823     SelectObject( hdc, hbm_old );\r
2824 \r
2825     if( hPieceFace[index] != NULL ) {\r
2826         DeleteObject( hPieceFace[index] );\r
2827     }\r
2828 \r
2829     hPieceFace[index] = hbm;\r
2830 }\r
2831 \r
2832 static int TranslatePieceToFontPiece( int piece )\r
2833 {\r
2834     switch( piece ) {\r
2835     case BlackPawn:\r
2836         return PM_BP;\r
2837     case BlackKnight:\r
2838         return PM_BN;\r
2839     case BlackBishop:\r
2840         return PM_BB;\r
2841     case BlackRook:\r
2842         return PM_BR;\r
2843     case BlackQueen:\r
2844         return PM_BQ;\r
2845     case BlackKing:\r
2846         return PM_BK;\r
2847     case WhitePawn:\r
2848         return PM_WP;\r
2849     case WhiteKnight:\r
2850         return PM_WN;\r
2851     case WhiteBishop:\r
2852         return PM_WB;\r
2853     case WhiteRook:\r
2854         return PM_WR;\r
2855     case WhiteQueen:\r
2856         return PM_WQ;\r
2857     case WhiteKing:\r
2858         return PM_WK;\r
2859 \r
2860     case BlackAngel:\r
2861         return PM_BA;\r
2862     case BlackMarshall:\r
2863         return PM_BC;\r
2864     case BlackFerz:\r
2865         return PM_BF;\r
2866     case BlackNightrider:\r
2867         return PM_BH;\r
2868     case BlackAlfil:\r
2869         return PM_BE;\r
2870     case BlackWazir:\r
2871         return PM_BW;\r
2872     case BlackUnicorn:\r
2873         return PM_BU;\r
2874     case BlackCannon:\r
2875         return PM_BO;\r
2876     case BlackGrasshopper:\r
2877         return PM_BG;\r
2878     case BlackMan:\r
2879         return PM_BM;\r
2880     case BlackSilver:\r
2881         return PM_BSG;\r
2882     case BlackLance:\r
2883         return PM_BL;\r
2884     case BlackFalcon:\r
2885         return PM_BV;\r
2886     case BlackCobra:\r
2887         return PM_BS;\r
2888     case BlackCardinal:\r
2889         return PM_BAB;\r
2890     case BlackDragon:\r
2891         return PM_BD;\r
2892 \r
2893     case WhiteAngel:\r
2894         return PM_WA;\r
2895     case WhiteMarshall:\r
2896         return PM_WC;\r
2897     case WhiteFerz:\r
2898         return PM_WF;\r
2899     case WhiteNightrider:\r
2900         return PM_WH;\r
2901     case WhiteAlfil:\r
2902         return PM_WE;\r
2903     case WhiteWazir:\r
2904         return PM_WW;\r
2905     case WhiteUnicorn:\r
2906         return PM_WU;\r
2907     case WhiteCannon:\r
2908         return PM_WO;\r
2909     case WhiteGrasshopper:\r
2910         return PM_WG;\r
2911     case WhiteMan:\r
2912         return PM_WM;\r
2913     case WhiteSilver:\r
2914         return PM_WSG;\r
2915     case WhiteLance:\r
2916         return PM_WL;\r
2917     case WhiteFalcon:\r
2918         return PM_WV;\r
2919     case WhiteCobra:\r
2920         return PM_WS;\r
2921     case WhiteCardinal:\r
2922         return PM_WAB;\r
2923     case WhiteDragon:\r
2924         return PM_WD;\r
2925     }\r
2926 \r
2927     return 0;\r
2928 }\r
2929 \r
2930 void CreatePiecesFromFont()\r
2931 {\r
2932     LOGFONT lf;\r
2933     HDC hdc_window = NULL;\r
2934     HDC hdc = NULL;\r
2935     HFONT hfont_old;\r
2936     int fontHeight;\r
2937     int i;\r
2938 \r
2939     if( fontBitmapSquareSize < 0 ) {\r
2940         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2941         return;\r
2942     }\r
2943 \r
2944     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2945         fontBitmapSquareSize = -1;\r
2946         return;\r
2947     }\r
2948 \r
2949     if( fontBitmapSquareSize != squareSize ) {\r
2950         hdc_window = GetDC( hwndMain );\r
2951         hdc = CreateCompatibleDC( hdc_window );\r
2952 \r
2953         if( hPieceFont != NULL ) {\r
2954             DeleteObject( hPieceFont );\r
2955         }\r
2956         else {\r
2957             for( i=0; i<=(int)BlackKing; i++ ) {\r
2958                 hPieceMask[i] = NULL;\r
2959                 hPieceFace[i] = NULL;\r
2960             }\r
2961         }\r
2962 \r
2963         fontHeight = 75;\r
2964 \r
2965         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2966             fontHeight = appData.fontPieceSize;\r
2967         }\r
2968 \r
2969         fontHeight = (fontHeight * squareSize) / 100;\r
2970 \r
2971         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2972         lf.lfWidth = 0;\r
2973         lf.lfEscapement = 0;\r
2974         lf.lfOrientation = 0;\r
2975         lf.lfWeight = FW_NORMAL;\r
2976         lf.lfItalic = 0;\r
2977         lf.lfUnderline = 0;\r
2978         lf.lfStrikeOut = 0;\r
2979         lf.lfCharSet = DEFAULT_CHARSET;\r
2980         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2981         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2982         lf.lfQuality = PROOF_QUALITY;\r
2983         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2984         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2985         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2986 \r
2987         hPieceFont = CreateFontIndirect( &lf );\r
2988 \r
2989         if( hPieceFont == NULL ) {\r
2990             fontBitmapSquareSize = -2;\r
2991         }\r
2992         else {\r
2993             /* Setup font-to-piece character table */\r
2994             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2995                 /* No (or wrong) global settings, try to detect the font */\r
2996                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2997                     /* Alpha */\r
2998                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2999                 }\r
3000                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3001                     /* DiagramTT* family */\r
3002                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3003                 }\r
3004                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3005                     /* Fairy symbols */\r
3006                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3007                 }\r
3008                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3009                     /* Good Companion (Some characters get warped as literal :-( */\r
3010                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3011                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3012                     SetCharTable(pieceToFontChar, s);\r
3013                 }\r
3014                 else {\r
3015                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3016                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3017                 }\r
3018             }\r
3019 \r
3020             /* Create bitmaps */\r
3021             hfont_old = SelectObject( hdc, hPieceFont );\r
3022 #if 0\r
3023             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
3024             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
3025             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
3026             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
3027             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
3028             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
3029             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
3030             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
3031             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
3032             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
3033             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
3034             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
3035 \r
3036             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
3037             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
3038             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
3039             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
3040             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
3041             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
3042             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
3043             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
3044             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
3045             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
3046             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
3047             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
3048             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
3049             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
3050             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
3051             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
3052             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
3053             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
3054             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
3055             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
3056             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
3057             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
3058             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
3059             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
3060             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
3061             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
3062             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
3063             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
3064             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
3065             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
3066             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
3067             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
3068 #else\r
3069             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3070                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3071                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3072 #endif\r
3073             SelectObject( hdc, hfont_old );\r
3074 \r
3075             fontBitmapSquareSize = squareSize;\r
3076         }\r
3077     }\r
3078 \r
3079     if( hdc != NULL ) {\r
3080         DeleteDC( hdc );\r
3081     }\r
3082 \r
3083     if( hdc_window != NULL ) {\r
3084         ReleaseDC( hwndMain, hdc_window );\r
3085     }\r
3086 }\r
3087 \r
3088 HBITMAP\r
3089 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3090 {\r
3091   char name[128];\r
3092 \r
3093   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3094   if (gameInfo.event &&\r
3095       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3096       strcmp(name, "k80s") == 0) {\r
3097     strcpy(name, "tim");\r
3098   }\r
3099   return LoadBitmap(hinst, name);\r
3100 }\r
3101 \r
3102 \r
3103 /* Insert a color into the program's logical palette\r
3104    structure.  This code assumes the given color is\r
3105    the result of the RGB or PALETTERGB macro, and it\r
3106    knows how those macros work (which is documented).\r
3107 */\r
3108 VOID\r
3109 InsertInPalette(COLORREF color)\r
3110 {\r
3111   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3112 \r
3113   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3114     DisplayFatalError("Too many colors", 0, 1);\r
3115     pLogPal->palNumEntries--;\r
3116     return;\r
3117   }\r
3118 \r
3119   pe->peFlags = (char) 0;\r
3120   pe->peRed = (char) (0xFF & color);\r
3121   pe->peGreen = (char) (0xFF & (color >> 8));\r
3122   pe->peBlue = (char) (0xFF & (color >> 16));\r
3123   return;\r
3124 }\r
3125 \r
3126 \r
3127 VOID\r
3128 InitDrawingColors()\r
3129 {\r
3130   if (pLogPal == NULL) {\r
3131     /* Allocate enough memory for a logical palette with\r
3132      * PALETTESIZE entries and set the size and version fields\r
3133      * of the logical palette structure.\r
3134      */\r
3135     pLogPal = (NPLOGPALETTE)\r
3136       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3137                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3138     pLogPal->palVersion    = 0x300;\r
3139   }\r
3140   pLogPal->palNumEntries = 0;\r
3141 \r
3142   InsertInPalette(lightSquareColor);\r
3143   InsertInPalette(darkSquareColor);\r
3144   InsertInPalette(whitePieceColor);\r
3145   InsertInPalette(blackPieceColor);\r
3146   InsertInPalette(highlightSquareColor);\r
3147   InsertInPalette(premoveHighlightColor);\r
3148 \r
3149   /*  create a logical color palette according the information\r
3150    *  in the LOGPALETTE structure.\r
3151    */\r
3152   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3153 \r
3154   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3155   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3156   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3157   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3158   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3159   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3160   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3161   /* [AS] Force rendering of the font-based pieces */\r
3162   if( fontBitmapSquareSize > 0 ) {\r
3163     fontBitmapSquareSize = 0;\r
3164   }\r
3165 }\r
3166 \r
3167 \r
3168 int\r
3169 BoardWidth(int boardSize, int n)\r
3170 { /* [HGM] argument n added to allow different width and height */\r
3171   int lineGap = sizeInfo[boardSize].lineGap;\r
3172 \r
3173   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3174       lineGap = appData.overrideLineGap;\r
3175   }\r
3176 \r
3177   return (n + 1) * lineGap +\r
3178           n * sizeInfo[boardSize].squareSize;\r
3179 }\r
3180 \r
3181 /* Respond to board resize by dragging edge */\r
3182 VOID\r
3183 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3184 {\r
3185   BoardSize newSize = NUM_SIZES - 1;\r
3186   static int recurse = 0;\r
3187   if (IsIconic(hwndMain)) return;\r
3188   if (recurse > 0) return;\r
3189   recurse++;\r
3190   while (newSize > 0) {\r
3191         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3192         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3193            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3194     newSize--;\r
3195   } \r
3196   boardSize = newSize;\r
3197   InitDrawingSizes(boardSize, flags);\r
3198   recurse--;\r
3199 }\r
3200 \r
3201 \r
3202 \r
3203 VOID\r
3204 InitDrawingSizes(BoardSize boardSize, int flags)\r
3205 {\r
3206   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3207   ChessSquare piece;\r
3208   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3209   HDC hdc;\r
3210   SIZE clockSize, messageSize;\r
3211   HFONT oldFont;\r
3212   char buf[MSG_SIZ];\r
3213   char *str;\r
3214   HMENU hmenu = GetMenu(hwndMain);\r
3215   RECT crect, wrect, oldRect;\r
3216   int offby;\r
3217   LOGBRUSH logbrush;\r
3218 \r
3219   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3220   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3221 \r
3222   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3223   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3224 \r
3225   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3226   oldRect.top = boardY;\r
3227   oldRect.right = boardX + winWidth;\r
3228   oldRect.bottom = boardY + winHeight;\r
3229 \r
3230   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3231   smallLayout = sizeInfo[boardSize].smallLayout;\r
3232   squareSize = sizeInfo[boardSize].squareSize;\r
3233   lineGap = sizeInfo[boardSize].lineGap;\r
3234   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3235 \r
3236   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3237       lineGap = appData.overrideLineGap;\r
3238   }\r
3239 \r
3240   if (tinyLayout != oldTinyLayout) {\r
3241     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3242     if (tinyLayout) {\r
3243       style &= ~WS_SYSMENU;\r
3244       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3245                  "&Minimize\tCtrl+F4");\r
3246     } else {\r
3247       style |= WS_SYSMENU;\r
3248       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3249     }\r
3250     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3251 \r
3252     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3253       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3254         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3255     }\r
3256     DrawMenuBar(hwndMain);\r
3257   }\r
3258 \r
3259   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3260   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3261 \r
3262   /* Get text area sizes */\r
3263   hdc = GetDC(hwndMain);\r
3264   if (appData.clockMode) {\r
3265     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3266   } else {\r
3267     sprintf(buf, "White");\r
3268   }\r
3269   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3270   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3271   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3272   str = "We only care about the height here";\r
3273   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3274   SelectObject(hdc, oldFont);\r
3275   ReleaseDC(hwndMain, hdc);\r
3276 \r
3277   /* Compute where everything goes */\r
3278   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3279         /* [HGM] logo: if either logo is on, reserve space for it */\r
3280         logoHeight =  2*clockSize.cy;\r
3281         leftLogoRect.left   = OUTER_MARGIN;\r
3282         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3283         leftLogoRect.top    = OUTER_MARGIN;\r
3284         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3285 \r
3286         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3287         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3288         rightLogoRect.top    = OUTER_MARGIN;\r
3289         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3290 \r
3291 \r
3292     whiteRect.left = leftLogoRect.right;\r
3293     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3294     whiteRect.top = OUTER_MARGIN;\r
3295     whiteRect.bottom = whiteRect.top + logoHeight;\r
3296 \r
3297     blackRect.right = rightLogoRect.left;\r
3298     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3299     blackRect.top = whiteRect.top;\r
3300     blackRect.bottom = whiteRect.bottom;\r
3301   } else {\r
3302     whiteRect.left = OUTER_MARGIN;\r
3303     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3304     whiteRect.top = OUTER_MARGIN;\r
3305     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3306 \r
3307     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3308     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3309     blackRect.top = whiteRect.top;\r
3310     blackRect.bottom = whiteRect.bottom;\r
3311   }\r
3312 \r
3313   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3314   if (appData.showButtonBar) {\r
3315     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3316       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3317   } else {\r
3318     messageRect.right = OUTER_MARGIN + boardWidth;\r
3319   }\r
3320   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3321   messageRect.bottom = messageRect.top + messageSize.cy;\r
3322 \r
3323   boardRect.left = OUTER_MARGIN;\r
3324   boardRect.right = boardRect.left + boardWidth;\r
3325   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3326   boardRect.bottom = boardRect.top + boardHeight;\r
3327 \r
3328   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3329   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3330   oldBoardSize = boardSize;\r
3331   oldTinyLayout = tinyLayout;\r
3332   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3333   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3334     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3335   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3336   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3337   winHeight = winH; //       without disturbing window attachments\r
3338   GetWindowRect(hwndMain, &wrect);\r
3339   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3340                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3341 \r
3342   // [HGM] placement: let attached windows follow size change.\r
3343   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3344   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3345   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3346   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3347   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3348 \r
3349   /* compensate if menu bar wrapped */\r
3350   GetClientRect(hwndMain, &crect);\r
3351   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3352   winHeight += offby;\r
3353   switch (flags) {\r
3354   case WMSZ_TOPLEFT:\r
3355     SetWindowPos(hwndMain, NULL, \r
3356                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3357                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3358     break;\r
3359 \r
3360   case WMSZ_TOPRIGHT:\r
3361   case WMSZ_TOP:\r
3362     SetWindowPos(hwndMain, NULL, \r
3363                  wrect.left, wrect.bottom - winHeight, \r
3364                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3365     break;\r
3366 \r
3367   case WMSZ_BOTTOMLEFT:\r
3368   case WMSZ_LEFT:\r
3369     SetWindowPos(hwndMain, NULL, \r
3370                  wrect.right - winWidth, wrect.top, \r
3371                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3372     break;\r
3373 \r
3374   case WMSZ_BOTTOMRIGHT:\r
3375   case WMSZ_BOTTOM:\r
3376   case WMSZ_RIGHT:\r
3377   default:\r
3378     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3379                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3380     break;\r
3381   }\r
3382 \r
3383   hwndPause = NULL;\r
3384   for (i = 0; i < N_BUTTONS; i++) {\r
3385     if (buttonDesc[i].hwnd != NULL) {\r
3386       DestroyWindow(buttonDesc[i].hwnd);\r
3387       buttonDesc[i].hwnd = NULL;\r
3388     }\r
3389     if (appData.showButtonBar) {\r
3390       buttonDesc[i].hwnd =\r
3391         CreateWindow("BUTTON", buttonDesc[i].label,\r
3392                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3393                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3394                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3395                      (HMENU) buttonDesc[i].id,\r
3396                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3397       if (tinyLayout) {\r
3398         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3399                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3400                     MAKELPARAM(FALSE, 0));\r
3401       }\r
3402       if (buttonDesc[i].id == IDM_Pause)\r
3403         hwndPause = buttonDesc[i].hwnd;\r
3404       buttonDesc[i].wndproc = (WNDPROC)\r
3405         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3406     }\r
3407   }\r
3408   if (gridPen != NULL) DeleteObject(gridPen);\r
3409   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3410   if (premovePen != NULL) DeleteObject(premovePen);\r
3411   if (lineGap != 0) {\r
3412     logbrush.lbStyle = BS_SOLID;\r
3413     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3414     gridPen =\r
3415       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3416                    lineGap, &logbrush, 0, NULL);\r
3417     logbrush.lbColor = highlightSquareColor;\r
3418     highlightPen =\r
3419       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3420                    lineGap, &logbrush, 0, NULL);\r
3421 \r
3422     logbrush.lbColor = premoveHighlightColor; \r
3423     premovePen =\r
3424       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3425                    lineGap, &logbrush, 0, NULL);\r
3426 \r
3427     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3428     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3429       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3430       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3431         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3432       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3433         BOARD_WIDTH * (squareSize + lineGap);\r
3434       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3435     }\r
3436     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3437       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3438       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3439         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3440         lineGap / 2 + (i * (squareSize + lineGap));\r
3441       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3442         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3443       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3444     }\r
3445   }\r
3446 \r
3447   /* [HGM] Licensing requirement */\r
3448 #ifdef GOTHIC\r
3449   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3450 #endif\r
3451 #ifdef FALCON\r
3452   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3453 #endif\r
3454   GothicPopUp( "", VariantNormal);\r
3455 \r
3456 \r
3457 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3458 \r
3459   /* Load piece bitmaps for this board size */\r
3460   for (i=0; i<=2; i++) {\r
3461     for (piece = WhitePawn;\r
3462          (int) piece < (int) BlackPawn;\r
3463          piece = (ChessSquare) ((int) piece + 1)) {\r
3464       if (pieceBitmap[i][piece] != NULL)\r
3465         DeleteObject(pieceBitmap[i][piece]);\r
3466     }\r
3467   }\r
3468 \r
3469   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3470   // Orthodox Chess pieces\r
3471   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3472   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3473   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3474   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3475   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3476   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3477   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3478   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3479   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3480   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3481   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3482   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3483   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3484   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3485   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3486   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3487     // in Shogi, Hijack the unused Queen for Lance\r
3488     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3489     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3490     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3491   } else {\r
3492     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3493     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3494     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3495   }\r
3496 \r
3497   if(squareSize <= 72 && squareSize >= 33) { \r
3498     /* A & C are available in most sizes now */\r
3499     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3500       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3501       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3502       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3503       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3504       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3505       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3506       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3507       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3508       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3509       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3510       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3511       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3512     } else { // Smirf-like\r
3513       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3514       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3515       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3516     }\r
3517     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3518       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3519       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3520       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3521     } else { // WinBoard standard\r
3522       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3523       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3524       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3525     }\r
3526   }\r
3527 \r
3528 \r
3529   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3530     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3531     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3532     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3533     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3534     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3535     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3536     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3537     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3538     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3539     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3540     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3541     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3542     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3543     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3544     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3545     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3546     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3547     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3548     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3549     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3550     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3551     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3552     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3553     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3554     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3555     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3556     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3557     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3558     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3559     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3560 \r
3561     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3562       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3563       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3564       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3565       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3566       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3567       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3568       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3569       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3570       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3571       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3572       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3573       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3574     } else {\r
3575       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3576       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3577       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3578       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3579       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3580       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3581       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3582       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3583       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3584       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3585       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3586       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3587     }\r
3588 \r
3589   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3590     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3591     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3592     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3593     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3594     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3595     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3596     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3597     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3598     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3599     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3600     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3601     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3602     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3603     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3604   }\r
3605 \r
3606 \r
3607   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3608   /* special Shogi support in this size */\r
3609   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3610       for (piece = WhitePawn;\r
3611            (int) piece < (int) BlackPawn;\r
3612            piece = (ChessSquare) ((int) piece + 1)) {\r
3613         if (pieceBitmap[i][piece] != NULL)\r
3614           DeleteObject(pieceBitmap[i][piece]);\r
3615       }\r
3616     }\r
3617   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3618   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3619   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3620   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3621   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3622   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3623   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3624   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3625   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3626   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3627   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3628   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3629   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3630   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3631   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3632   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3633   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3634   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3635   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3636   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3637   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3638   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3639   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3640   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3641   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3642   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3643   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3644   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3645   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3646   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3647   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3648   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3649   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3650   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3651   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3652   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3653   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3654   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3655   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3656   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3657   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3658   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3659   minorSize = 0;\r
3660   }\r
3661 }\r
3662 \r
3663 HBITMAP\r
3664 PieceBitmap(ChessSquare p, int kind)\r
3665 {\r
3666   if ((int) p >= (int) BlackPawn)\r
3667     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3668 \r
3669   return pieceBitmap[kind][(int) p];\r
3670 }\r
3671 \r
3672 /***************************************************************/\r
3673 \r
3674 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3675 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3676 /*\r
3677 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3678 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3679 */\r
3680 \r
3681 VOID\r
3682 SquareToPos(int row, int column, int * x, int * y)\r
3683 {\r
3684   if (flipView) {\r
3685     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3686     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3687   } else {\r
3688     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3689     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3690   }\r
3691 }\r
3692 \r
3693 VOID\r
3694 DrawCoordsOnDC(HDC hdc)\r
3695 {\r
3696   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
3697   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
3698   char str[2] = { NULLCHAR, NULLCHAR };\r
3699   int oldMode, oldAlign, x, y, start, i;\r
3700   HFONT oldFont;\r
3701   HBRUSH oldBrush;\r
3702 \r
3703   if (!appData.showCoords)\r
3704     return;\r
3705 \r
3706   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3707 \r
3708   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3709   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3710   oldAlign = GetTextAlign(hdc);\r
3711   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3712 \r
3713   y = boardRect.top + lineGap;\r
3714   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3715 \r
3716   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3717   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3718     str[0] = files[start + i];\r
3719     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3720     y += squareSize + lineGap;\r
3721   }\r
3722 \r
3723   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3724 \r
3725   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3726   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3727     str[0] = ranks[start + i];\r
3728     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3729     x += squareSize + lineGap;\r
3730   }    \r
3731 \r
3732   SelectObject(hdc, oldBrush);\r
3733   SetBkMode(hdc, oldMode);\r
3734   SetTextAlign(hdc, oldAlign);\r
3735   SelectObject(hdc, oldFont);\r
3736 }\r
3737 \r
3738 VOID\r
3739 DrawGridOnDC(HDC hdc)\r
3740 {\r
3741   HPEN oldPen;\r
3742  \r
3743   if (lineGap != 0) {\r
3744     oldPen = SelectObject(hdc, gridPen);\r
3745     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3746     SelectObject(hdc, oldPen);\r
3747   }\r
3748 }\r
3749 \r
3750 #define HIGHLIGHT_PEN 0\r
3751 #define PREMOVE_PEN   1\r
3752 \r
3753 VOID\r
3754 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3755 {\r
3756   int x1, y1;\r
3757   HPEN oldPen, hPen;\r
3758   if (lineGap == 0) return;\r
3759   if (flipView) {\r
3760     x1 = boardRect.left +\r
3761       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3762     y1 = boardRect.top +\r
3763       lineGap/2 + y * (squareSize + lineGap);\r
3764   } else {\r
3765     x1 = boardRect.left +\r
3766       lineGap/2 + x * (squareSize + lineGap);\r
3767     y1 = boardRect.top +\r
3768       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3769   }\r
3770   hPen = pen ? premovePen : highlightPen;\r
3771   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3772   MoveToEx(hdc, x1, y1, NULL);\r
3773   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3774   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3775   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3776   LineTo(hdc, x1, y1);\r
3777   SelectObject(hdc, oldPen);\r
3778 }\r
3779 \r
3780 VOID\r
3781 DrawHighlightsOnDC(HDC hdc)\r
3782 {\r
3783   int i;\r
3784   for (i=0; i<2; i++) {\r
3785     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3786       DrawHighlightOnDC(hdc, TRUE,\r
3787                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3788                         HIGHLIGHT_PEN);\r
3789   }\r
3790   for (i=0; i<2; i++) {\r
3791     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3792         premoveHighlightInfo.sq[i].y >= 0) {\r
3793         DrawHighlightOnDC(hdc, TRUE,\r
3794                           premoveHighlightInfo.sq[i].x, \r
3795                           premoveHighlightInfo.sq[i].y,\r
3796                           PREMOVE_PEN);\r
3797     }\r
3798   }\r
3799 }\r
3800 \r
3801 /* Note: sqcolor is used only in monoMode */\r
3802 /* Note that this code is largely duplicated in woptions.c,\r
3803    function DrawSampleSquare, so that needs to be updated too */\r
3804 VOID\r
3805 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3806 {\r
3807   HBITMAP oldBitmap;\r
3808   HBRUSH oldBrush;\r
3809   int tmpSize;\r
3810 \r
3811   if (appData.blindfold) return;\r
3812 \r
3813   /* [AS] Use font-based pieces if needed */\r
3814   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3815     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3816     CreatePiecesFromFont();\r
3817 \r
3818     if( fontBitmapSquareSize == squareSize ) {\r
3819         int index = TranslatePieceToFontPiece(piece);\r
3820 \r
3821         SelectObject( tmphdc, hPieceMask[ index ] );\r
3822 \r
3823         BitBlt( hdc,\r
3824             x, y,\r
3825             squareSize, squareSize,\r
3826             tmphdc,\r
3827             0, 0,\r
3828             SRCAND );\r
3829 \r
3830         SelectObject( tmphdc, hPieceFace[ index ] );\r
3831 \r
3832         BitBlt( hdc,\r
3833             x, y,\r
3834             squareSize, squareSize,\r
3835             tmphdc,\r
3836             0, 0,\r
3837             SRCPAINT );\r
3838 \r
3839         return;\r
3840     }\r
3841   }\r
3842 \r
3843   if (appData.monoMode) {\r
3844     SelectObject(tmphdc, PieceBitmap(piece, \r
3845       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3846     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3847            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3848   } else {\r
3849     tmpSize = squareSize;\r
3850     if(minorSize &&\r
3851         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3852          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3853       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3854       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3855       x += (squareSize - minorSize)>>1;\r
3856       y += squareSize - minorSize - 2;\r
3857       tmpSize = minorSize;\r
3858     }\r
3859     if (color || appData.allWhite ) {\r
3860       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3861       if( color )\r
3862               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3863       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3864       if(appData.upsideDown && color==flipView)\r
3865         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3866       else\r
3867         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3868 #if 0\r
3869       /* Use black piece color for outline of white pieces */\r
3870       /* Not sure this looks really good (though xboard does it).\r
3871          Maybe better to have another selectable color, default black */\r
3872       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3873       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3874       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3875 #else\r
3876       /* Use black for outline of white pieces */\r
3877       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3878       if(appData.upsideDown && color==flipView)\r
3879         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3880       else\r
3881         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3882 #endif\r
3883     } else {\r
3884 #if 0\r
3885       /* Use white piece color for details of black pieces */\r
3886       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3887          WHITE_PIECE ones aren't always the right shape. */\r
3888       /* Not sure this looks really good (though xboard does it).\r
3889          Maybe better to have another selectable color, default medium gray? */\r
3890       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3891       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3892       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3893       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3894       SelectObject(hdc, blackPieceBrush);\r
3895       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3896 #else\r
3897       /* Use square color for details of black pieces */\r
3898       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3899       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3900       if(appData.upsideDown && !flipView)\r
3901         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3902       else\r
3903         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3904 #endif\r
3905     }\r
3906     SelectObject(hdc, oldBrush);\r
3907     SelectObject(tmphdc, oldBitmap);\r
3908   }\r
3909 }\r
3910 \r
3911 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3912 int GetBackTextureMode( int algo )\r
3913 {\r
3914     int result = BACK_TEXTURE_MODE_DISABLED;\r
3915 \r
3916     switch( algo ) \r
3917     {\r
3918         case BACK_TEXTURE_MODE_PLAIN:\r
3919             result = 1; /* Always use identity map */\r
3920             break;\r
3921         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3922             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3923             break;\r
3924     }\r
3925 \r
3926     return result;\r
3927 }\r
3928 \r
3929 /* \r
3930     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3931     to handle redraws cleanly (as random numbers would always be different).\r
3932 */\r
3933 VOID RebuildTextureSquareInfo()\r
3934 {\r
3935     BITMAP bi;\r
3936     int lite_w = 0;\r
3937     int lite_h = 0;\r
3938     int dark_w = 0;\r
3939     int dark_h = 0;\r
3940     int row;\r
3941     int col;\r
3942 \r
3943     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3944 \r
3945     if( liteBackTexture != NULL ) {\r
3946         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3947             lite_w = bi.bmWidth;\r
3948             lite_h = bi.bmHeight;\r
3949         }\r
3950     }\r
3951 \r
3952     if( darkBackTexture != NULL ) {\r
3953         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3954             dark_w = bi.bmWidth;\r
3955             dark_h = bi.bmHeight;\r
3956         }\r
3957     }\r
3958 \r
3959     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3960         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3961             if( (col + row) & 1 ) {\r
3962                 /* Lite square */\r
3963                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3964                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3965                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3966                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3967                 }\r
3968             }\r
3969             else {\r
3970                 /* Dark square */\r
3971                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3972                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3973                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3974                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3975                 }\r
3976             }\r
3977         }\r
3978     }\r
3979 }\r
3980 \r
3981 /* [AS] Arrow highlighting support */\r
3982 \r
3983 static int A_WIDTH = 5; /* Width of arrow body */\r
3984 \r
3985 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3986 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3987 \r
3988 static double Sqr( double x )\r
3989 {\r
3990     return x*x;\r
3991 }\r
3992 \r
3993 static int Round( double x )\r
3994 {\r
3995     return (int) (x + 0.5);\r
3996 }\r
3997 \r
3998 /* Draw an arrow between two points using current settings */\r
3999 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
4000 {\r
4001     POINT arrow[7];\r
4002     double dx, dy, j, k, x, y;\r
4003 \r
4004     if( d_x == s_x ) {\r
4005         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4006 \r
4007         arrow[0].x = s_x + A_WIDTH;\r
4008         arrow[0].y = s_y;\r
4009 \r
4010         arrow[1].x = s_x + A_WIDTH;\r
4011         arrow[1].y = d_y - h;\r
4012 \r
4013         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
4014         arrow[2].y = d_y - h;\r
4015 \r
4016         arrow[3].x = d_x;\r
4017         arrow[3].y = d_y;\r
4018 \r
4019         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
4020         arrow[4].y = d_y - h;\r
4021 \r
4022         arrow[5].x = s_x - A_WIDTH;\r
4023         arrow[5].y = d_y - h;\r
4024 \r
4025         arrow[6].x = s_x - A_WIDTH;\r
4026         arrow[6].y = s_y;\r
4027     }\r
4028     else if( d_y == s_y ) {\r
4029         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4030 \r
4031         arrow[0].x = s_x;\r
4032         arrow[0].y = s_y + A_WIDTH;\r
4033 \r
4034         arrow[1].x = d_x - w;\r
4035         arrow[1].y = s_y + A_WIDTH;\r
4036 \r
4037         arrow[2].x = d_x - w;\r
4038         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
4039 \r
4040         arrow[3].x = d_x;\r
4041         arrow[3].y = d_y;\r
4042 \r
4043         arrow[4].x = d_x - w;\r
4044         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
4045 \r
4046         arrow[5].x = d_x - w;\r
4047         arrow[5].y = s_y - A_WIDTH;\r
4048 \r
4049         arrow[6].x = s_x;\r
4050         arrow[6].y = s_y - A_WIDTH;\r
4051     }\r
4052     else {\r
4053         /* [AS] Needed a lot of paper for this! :-) */\r
4054         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4055         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4056   \r
4057         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4058 \r
4059         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4060 \r
4061         x = s_x;\r
4062         y = s_y;\r
4063 \r
4064         arrow[0].x = Round(x - j);\r
4065         arrow[0].y = Round(y + j*dx);\r
4066 \r
4067         arrow[1].x = Round(x + j);\r
4068         arrow[1].y = Round(y - j*dx);\r
4069 \r
4070         if( d_x > s_x ) {\r
4071             x = (double) d_x - k;\r
4072             y = (double) d_y - k*dy;\r
4073         }\r
4074         else {\r
4075             x = (double) d_x + k;\r
4076             y = (double) d_y + k*dy;\r
4077         }\r
4078 \r
4079         arrow[2].x = Round(x + j);\r
4080         arrow[2].y = Round(y - j*dx);\r
4081 \r
4082         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4083         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4084 \r
4085         arrow[4].x = d_x;\r
4086         arrow[4].y = d_y;\r
4087 \r
4088         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4089         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4090 \r
4091         arrow[6].x = Round(x - j);\r
4092         arrow[6].y = Round(y + j*dx);\r
4093     }\r
4094 \r
4095     Polygon( hdc, arrow, 7 );\r
4096 }\r
4097 \r
4098 /* [AS] Draw an arrow between two squares */\r
4099 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4100 {\r
4101     int s_x, s_y, d_x, d_y;\r
4102     HPEN hpen;\r
4103     HPEN holdpen;\r
4104     HBRUSH hbrush;\r
4105     HBRUSH holdbrush;\r
4106     LOGBRUSH stLB;\r
4107 \r
4108     if( s_col == d_col && s_row == d_row ) {\r
4109         return;\r
4110     }\r
4111 \r
4112     /* Get source and destination points */\r
4113     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4114     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4115 \r
4116     if( d_y > s_y ) {\r
4117         d_y += squareSize / 4;\r
4118     }\r
4119     else if( d_y < s_y ) {\r
4120         d_y += 3 * squareSize / 4;\r
4121     }\r
4122     else {\r
4123         d_y += squareSize / 2;\r
4124     }\r
4125 \r
4126     if( d_x > s_x ) {\r
4127         d_x += squareSize / 4;\r
4128     }\r
4129     else if( d_x < s_x ) {\r
4130         d_x += 3 * squareSize / 4;\r
4131     }\r
4132     else {\r
4133         d_x += squareSize / 2;\r
4134     }\r
4135 \r
4136     s_x += squareSize / 2;\r
4137     s_y += squareSize / 2;\r
4138 \r
4139     /* Adjust width */\r
4140     A_WIDTH = squareSize / 14;\r
4141 \r
4142     /* Draw */\r
4143     stLB.lbStyle = BS_SOLID;\r
4144     stLB.lbColor = appData.highlightArrowColor;\r
4145     stLB.lbHatch = 0;\r
4146 \r
4147     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4148     holdpen = SelectObject( hdc, hpen );\r
4149     hbrush = CreateBrushIndirect( &stLB );\r
4150     holdbrush = SelectObject( hdc, hbrush );\r
4151 \r
4152     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4153 \r
4154     SelectObject( hdc, holdpen );\r
4155     SelectObject( hdc, holdbrush );\r
4156     DeleteObject( hpen );\r
4157     DeleteObject( hbrush );\r
4158 }\r
4159 \r
4160 BOOL HasHighlightInfo()\r
4161 {\r
4162     BOOL result = FALSE;\r
4163 \r
4164     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4165         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4166     {\r
4167         result = TRUE;\r
4168     }\r
4169 \r
4170     return result;\r
4171 }\r
4172 \r
4173 BOOL IsDrawArrowEnabled()\r
4174 {\r
4175     BOOL result = FALSE;\r
4176 \r
4177     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4178         result = TRUE;\r
4179     }\r
4180 \r
4181     return result;\r
4182 }\r
4183 \r
4184 VOID DrawArrowHighlight( HDC hdc )\r
4185 {\r
4186     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4187         DrawArrowBetweenSquares( hdc,\r
4188             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4189             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4190     }\r
4191 }\r
4192 \r
4193 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4194 {\r
4195     HRGN result = NULL;\r
4196 \r
4197     if( HasHighlightInfo() ) {\r
4198         int x1, y1, x2, y2;\r
4199         int sx, sy, dx, dy;\r
4200 \r
4201         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4202         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4203 \r
4204         sx = MIN( x1, x2 );\r
4205         sy = MIN( y1, y2 );\r
4206         dx = MAX( x1, x2 ) + squareSize;\r
4207         dy = MAX( y1, y2 ) + squareSize;\r
4208 \r
4209         result = CreateRectRgn( sx, sy, dx, dy );\r
4210     }\r
4211 \r
4212     return result;\r
4213 }\r
4214 \r
4215 /*\r
4216     Warning: this function modifies the behavior of several other functions. \r
4217     \r
4218     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4219     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4220     repaint is scattered all over the place, which is not good for features such as\r
4221     "arrow highlighting" that require a full repaint of the board.\r
4222 \r
4223     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4224     user interaction, when speed is not so important) but especially to avoid errors\r
4225     in the displayed graphics.\r
4226 \r
4227     In such patched places, I always try refer to this function so there is a single\r
4228     place to maintain knowledge.\r
4229     \r
4230     To restore the original behavior, just return FALSE unconditionally.\r
4231 */\r
4232 BOOL IsFullRepaintPreferrable()\r
4233 {\r
4234     BOOL result = FALSE;\r
4235 \r
4236     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4237         /* Arrow may appear on the board */\r
4238         result = TRUE;\r
4239     }\r
4240 \r
4241     return result;\r
4242 }\r
4243 \r
4244 /* \r
4245     This function is called by DrawPosition to know whether a full repaint must\r
4246     be forced or not.\r
4247 \r
4248     Only DrawPosition may directly call this function, which makes use of \r
4249     some state information. Other function should call DrawPosition specifying \r
4250     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4251 */\r
4252 BOOL DrawPositionNeedsFullRepaint()\r
4253 {\r
4254     BOOL result = FALSE;\r
4255 \r
4256     /* \r
4257         Probably a slightly better policy would be to trigger a full repaint\r
4258         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4259         but animation is fast enough that it's difficult to notice.\r
4260     */\r
4261     if( animInfo.piece == EmptySquare ) {\r
4262         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4263             result = TRUE;\r
4264         }\r
4265     }\r
4266 \r
4267     return result;\r
4268 }\r
4269 \r
4270 VOID\r
4271 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4272 {\r
4273   int row, column, x, y, square_color, piece_color;\r
4274   ChessSquare piece;\r
4275   HBRUSH oldBrush;\r
4276   HDC texture_hdc = NULL;\r
4277 \r
4278   /* [AS] Initialize background textures if needed */\r
4279   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4280       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4281       if( backTextureSquareSize != squareSize \r
4282        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4283           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4284           backTextureSquareSize = squareSize;\r
4285           RebuildTextureSquareInfo();\r
4286       }\r
4287 \r
4288       texture_hdc = CreateCompatibleDC( hdc );\r
4289   }\r
4290 \r
4291   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4292     for (column = 0; column < BOARD_WIDTH; column++) {\r
4293   \r
4294       SquareToPos(row, column, &x, &y);\r
4295 \r
4296       piece = board[row][column];\r
4297 \r
4298       square_color = ((column + row) % 2) == 1;\r
4299       if( gameInfo.variant == VariantXiangqi ) {\r
4300           square_color = !InPalace(row, column);\r
4301           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4302           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4303       }\r
4304       piece_color = (int) piece < (int) BlackPawn;\r
4305 \r
4306 \r
4307       /* [HGM] holdings file: light square or black */\r
4308       if(column == BOARD_LEFT-2) {\r
4309             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4310                 square_color = 1;\r
4311             else {\r
4312                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4313                 continue;\r
4314             }\r
4315       } else\r
4316       if(column == BOARD_RGHT + 1 ) {\r
4317             if( row < gameInfo.holdingsSize )\r
4318                 square_color = 1;\r
4319             else {\r
4320                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4321                 continue;\r
4322             }\r
4323       }\r
4324       if(column == BOARD_LEFT-1 ) /* left align */\r
4325             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4326       else if( column == BOARD_RGHT) /* right align */\r
4327             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4328       else\r
4329       if (appData.monoMode) {\r
4330         if (piece == EmptySquare) {\r
4331           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4332                  square_color ? WHITENESS : BLACKNESS);\r
4333         } else {\r
4334           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4335         }\r
4336       } \r
4337       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4338           /* [AS] Draw the square using a texture bitmap */\r
4339           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4340           int r = row, c = column; // [HGM] do not flip board in flipView\r
4341           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4342 \r
4343           DrawTile( x, y, \r
4344               squareSize, squareSize, \r
4345               hdc, \r
4346               texture_hdc,\r
4347               backTextureSquareInfo[r][c].mode,\r
4348               backTextureSquareInfo[r][c].x,\r
4349               backTextureSquareInfo[r][c].y );\r
4350 \r
4351           SelectObject( texture_hdc, hbm );\r
4352 \r
4353           if (piece != EmptySquare) {\r
4354               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4355           }\r
4356       }\r
4357       else {\r
4358         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4359 \r
4360         oldBrush = SelectObject(hdc, brush );\r
4361         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4362         SelectObject(hdc, oldBrush);\r
4363         if (piece != EmptySquare)\r
4364           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4365       }\r
4366     }\r
4367   }\r
4368 \r
4369   if( texture_hdc != NULL ) {\r
4370     DeleteDC( texture_hdc );\r
4371   }\r
4372 }\r
4373 \r
4374 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4375 void fputDW(FILE *f, int x)\r
4376 {\r
4377         fputc(x     & 255, f);\r
4378         fputc(x>>8  & 255, f);\r
4379         fputc(x>>16 & 255, f);\r
4380         fputc(x>>24 & 255, f);\r
4381 }\r
4382 \r
4383 #define MAX_CLIPS 200   /* more than enough */\r
4384 \r
4385 VOID\r
4386 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4387 {\r
4388 //  HBITMAP bufferBitmap;\r
4389   BITMAP bi;\r
4390 //  RECT Rect;\r
4391   HDC tmphdc;\r
4392   HBITMAP hbm;\r
4393   int w = 100, h = 50;\r
4394 \r
4395   if(logo == NULL) return;\r
4396 //  GetClientRect(hwndMain, &Rect);\r
4397 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4398 //                                      Rect.bottom-Rect.top+1);\r
4399   tmphdc = CreateCompatibleDC(hdc);\r
4400   hbm = SelectObject(tmphdc, logo);\r
4401   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4402             w = bi.bmWidth;\r
4403             h = bi.bmHeight;\r
4404   }\r
4405   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4406                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4407   SelectObject(tmphdc, hbm);\r
4408   DeleteDC(tmphdc);\r
4409 }\r
4410 \r
4411 VOID\r
4412 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4413 {\r
4414   static Board lastReq, lastDrawn;\r
4415   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4416   static int lastDrawnFlipView = 0;\r
4417   static int lastReqValid = 0, lastDrawnValid = 0;\r
4418   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4419   HDC tmphdc;\r
4420   HDC hdcmem;\r
4421   HBITMAP bufferBitmap;\r
4422   HBITMAP oldBitmap;\r
4423   RECT Rect;\r
4424   HRGN clips[MAX_CLIPS];\r
4425   ChessSquare dragged_piece = EmptySquare;\r
4426 \r
4427   /* I'm undecided on this - this function figures out whether a full\r
4428    * repaint is necessary on its own, so there's no real reason to have the\r
4429    * caller tell it that.  I think this can safely be set to FALSE - but\r
4430    * if we trust the callers not to request full repaints unnessesarily, then\r
4431    * we could skip some clipping work.  In other words, only request a full\r
4432    * redraw when the majority of pieces have changed positions (ie. flip, \r
4433    * gamestart and similar)  --Hawk\r
4434    */\r
4435   Boolean fullrepaint = repaint;\r
4436 \r
4437   if( DrawPositionNeedsFullRepaint() ) {\r
4438       fullrepaint = TRUE;\r
4439   }\r
4440 \r
4441 #if 0\r
4442   if( fullrepaint ) {\r
4443       static int repaint_count = 0;\r
4444       char buf[128];\r
4445 \r
4446       repaint_count++;\r
4447       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4448       OutputDebugString( buf );\r
4449   }\r
4450 #endif\r
4451 \r
4452   if (board == NULL) {\r
4453     if (!lastReqValid) {\r
4454       return;\r
4455     }\r
4456     board = lastReq;\r
4457   } else {\r
4458     CopyBoard(lastReq, board);\r
4459     lastReqValid = 1;\r
4460   }\r
4461 \r
4462   if (doingSizing) {\r
4463     return;\r
4464   }\r
4465 \r
4466   if (IsIconic(hwndMain)) {\r
4467     return;\r
4468   }\r
4469 \r
4470   if (hdc == NULL) {\r
4471     hdc = GetDC(hwndMain);\r
4472     if (!appData.monoMode) {\r
4473       SelectPalette(hdc, hPal, FALSE);\r
4474       RealizePalette(hdc);\r
4475     }\r
4476     releaseDC = TRUE;\r
4477   } else {\r
4478     releaseDC = FALSE;\r
4479   }\r
4480 \r
4481 #if 0\r
4482   fprintf(debugFP, "*******************************\n"\r
4483                    "repaint = %s\n"\r
4484                    "dragInfo.from (%d,%d)\n"\r
4485                    "dragInfo.start (%d,%d)\n"\r
4486                    "dragInfo.pos (%d,%d)\n"\r
4487                    "dragInfo.lastpos (%d,%d)\n", \r
4488                     repaint ? "TRUE" : "FALSE",\r
4489                     dragInfo.from.x, dragInfo.from.y, \r
4490                     dragInfo.start.x, dragInfo.start.y,\r
4491                     dragInfo.pos.x, dragInfo.pos.y,\r
4492                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4493   fprintf(debugFP, "prev:  ");\r
4494   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4495     for (column = 0; column < BOARD_WIDTH; column++) {\r
4496       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4497     }\r
4498   }\r
4499   fprintf(debugFP, "\n");\r
4500   fprintf(debugFP, "board: ");\r
4501   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4502     for (column = 0; column < BOARD_WIDTH; column++) {\r
4503       fprintf(debugFP, "%d ", board[row][column]);\r
4504     }\r
4505   }\r
4506   fprintf(debugFP, "\n");\r
4507   fflush(debugFP);\r
4508 #endif\r
4509 \r
4510   /* Create some work-DCs */\r
4511   hdcmem = CreateCompatibleDC(hdc);\r
4512   tmphdc = CreateCompatibleDC(hdc);\r
4513 \r
4514   /* If dragging is in progress, we temporarely remove the piece */\r
4515   /* [HGM] or temporarily decrease count if stacked              */\r
4516   /*       !! Moved to before board compare !!                   */\r
4517   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4518     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4519     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4520             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4521         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4522     } else \r
4523     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4524             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4525         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4526     } else \r
4527         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4528   }\r
4529 \r
4530   /* Figure out which squares need updating by comparing the \r
4531    * newest board with the last drawn board and checking if\r
4532    * flipping has changed.\r
4533    */\r
4534   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4535     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4536       for (column = 0; column < BOARD_WIDTH; column++) {\r
4537         if (lastDrawn[row][column] != board[row][column]) {\r
4538           SquareToPos(row, column, &x, &y);\r
4539           clips[num_clips++] =\r
4540             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4541         }\r
4542       }\r
4543     }\r
4544     for (i=0; i<2; i++) {\r
4545       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4546           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4547         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4548             lastDrawnHighlight.sq[i].y >= 0) {\r
4549           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4550                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4551           clips[num_clips++] =\r
4552             CreateRectRgn(x - lineGap, y - lineGap, \r
4553                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4554         }\r
4555         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4556           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4557           clips[num_clips++] =\r
4558             CreateRectRgn(x - lineGap, y - lineGap, \r
4559                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4560         }\r
4561       }\r
4562     }\r
4563     for (i=0; i<2; i++) {\r
4564       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4565           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4566         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4567             lastDrawnPremove.sq[i].y >= 0) {\r
4568           SquareToPos(lastDrawnPremove.sq[i].y,\r
4569                       lastDrawnPremove.sq[i].x, &x, &y);\r
4570           clips[num_clips++] =\r
4571             CreateRectRgn(x - lineGap, y - lineGap, \r
4572                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4573         }\r
4574         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4575             premoveHighlightInfo.sq[i].y >= 0) {\r
4576           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4577                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4578           clips[num_clips++] =\r
4579             CreateRectRgn(x - lineGap, y - lineGap, \r
4580                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4581         }\r
4582       }\r
4583     }\r
4584   } else {\r
4585     fullrepaint = TRUE;\r
4586   }\r
4587 \r
4588   /* Create a buffer bitmap - this is the actual bitmap\r
4589    * being written to.  When all the work is done, we can\r
4590    * copy it to the real DC (the screen).  This avoids\r
4591    * the problems with flickering.\r
4592    */\r
4593   GetClientRect(hwndMain, &Rect);\r
4594   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4595                                         Rect.bottom-Rect.top+1);\r
4596   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4597   if (!appData.monoMode) {\r
4598     SelectPalette(hdcmem, hPal, FALSE);\r
4599   }\r
4600 \r
4601   /* Create clips for dragging */\r
4602   if (!fullrepaint) {\r
4603     if (dragInfo.from.x >= 0) {\r
4604       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4605       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4606     }\r
4607     if (dragInfo.start.x >= 0) {\r
4608       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4609       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4610     }\r
4611     if (dragInfo.pos.x >= 0) {\r
4612       x = dragInfo.pos.x - squareSize / 2;\r
4613       y = dragInfo.pos.y - squareSize / 2;\r
4614       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4615     }\r
4616     if (dragInfo.lastpos.x >= 0) {\r
4617       x = dragInfo.lastpos.x - squareSize / 2;\r
4618       y = dragInfo.lastpos.y - squareSize / 2;\r
4619       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4620     }\r
4621   }\r
4622 \r
4623   /* Are we animating a move?  \r
4624    * If so, \r
4625    *   - remove the piece from the board (temporarely)\r
4626    *   - calculate the clipping region\r
4627    */\r
4628   if (!fullrepaint) {\r
4629     if (animInfo.piece != EmptySquare) {\r
4630       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4631       x = boardRect.left + animInfo.lastpos.x;\r
4632       y = boardRect.top + animInfo.lastpos.y;\r
4633       x2 = boardRect.left + animInfo.pos.x;\r
4634       y2 = boardRect.top + animInfo.pos.y;\r
4635       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4636       /* Slight kludge.  The real problem is that after AnimateMove is\r
4637          done, the position on the screen does not match lastDrawn.\r
4638          This currently causes trouble only on e.p. captures in\r
4639          atomic, where the piece moves to an empty square and then\r
4640          explodes.  The old and new positions both had an empty square\r
4641          at the destination, but animation has drawn a piece there and\r
4642          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4643       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4644     }\r
4645   }\r
4646 \r
4647   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4648   if (num_clips == 0)\r
4649     fullrepaint = TRUE;\r
4650 \r
4651   /* Set clipping on the memory DC */\r
4652   if (!fullrepaint) {\r
4653     SelectClipRgn(hdcmem, clips[0]);\r
4654     for (x = 1; x < num_clips; x++) {\r
4655       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4656         abort();  // this should never ever happen!\r
4657     }\r
4658   }\r
4659 \r
4660   /* Do all the drawing to the memory DC */\r
4661   if(explodeInfo.radius) { // [HGM] atomic\r
4662         HBRUSH oldBrush;\r
4663         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4664         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4665         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4666         x += squareSize/2;\r
4667         y += squareSize/2;\r
4668         if(!fullrepaint) {\r
4669           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4670           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4671         }\r
4672         DrawGridOnDC(hdcmem);\r
4673         DrawHighlightsOnDC(hdcmem);\r
4674         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4675         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4676         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4677         SelectObject(hdcmem, oldBrush);\r
4678   } else {\r
4679     DrawGridOnDC(hdcmem);\r
4680     DrawHighlightsOnDC(hdcmem);\r
4681     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4682   }\r
4683   if(logoHeight) {\r
4684         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4685         if(appData.autoLogo) {\r
4686           \r
4687           switch(gameMode) { // pick logos based on game mode\r
4688             case IcsObserving:\r
4689                 whiteLogo = second.programLogo; // ICS logo\r
4690                 blackLogo = second.programLogo;\r
4691             default:\r
4692                 break;\r
4693             case IcsPlayingWhite:\r
4694                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4695                 blackLogo = second.programLogo; // ICS logo\r
4696                 break;\r
4697             case IcsPlayingBlack:\r
4698                 whiteLogo = second.programLogo; // ICS logo\r
4699                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4700                 break;\r
4701             case TwoMachinesPlay:\r
4702                 if(first.twoMachinesColor[0] == 'b') {\r
4703                     whiteLogo = second.programLogo;\r
4704                     blackLogo = first.programLogo;\r
4705                 }\r
4706                 break;\r
4707             case MachinePlaysWhite:\r
4708                 blackLogo = userLogo;\r
4709                 break;\r
4710             case MachinePlaysBlack:\r
4711                 whiteLogo = userLogo;\r
4712                 blackLogo = first.programLogo;\r
4713           }\r
4714         }\r
4715         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4716         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4717   }\r
4718 \r
4719   if( appData.highlightMoveWithArrow ) {\r
4720     DrawArrowHighlight(hdcmem);\r
4721   }\r
4722 \r
4723   DrawCoordsOnDC(hdcmem);\r
4724 \r
4725   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4726                  /* to make sure lastDrawn contains what is actually drawn */\r
4727 \r
4728   /* Put the dragged piece back into place and draw it (out of place!) */\r
4729     if (dragged_piece != EmptySquare) {\r
4730     /* [HGM] or restack */\r
4731     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4732                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4733     else\r
4734     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4735                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4736     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4737     x = dragInfo.pos.x - squareSize / 2;\r
4738     y = dragInfo.pos.y - squareSize / 2;\r
4739     DrawPieceOnDC(hdcmem, dragged_piece,\r
4740                   ((int) dragged_piece < (int) BlackPawn), \r
4741                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4742   }   \r
4743   \r
4744   /* Put the animated piece back into place and draw it */\r
4745   if (animInfo.piece != EmptySquare) {\r
4746     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4747     x = boardRect.left + animInfo.pos.x;\r
4748     y = boardRect.top + animInfo.pos.y;\r
4749     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4750                   ((int) animInfo.piece < (int) BlackPawn),\r
4751                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4752   }\r
4753 \r
4754   /* Release the bufferBitmap by selecting in the old bitmap \r
4755    * and delete the memory DC\r
4756    */\r
4757   SelectObject(hdcmem, oldBitmap);\r
4758   DeleteDC(hdcmem);\r
4759 \r
4760   /* Set clipping on the target DC */\r
4761   if (!fullrepaint) {\r
4762     SelectClipRgn(hdc, clips[0]);\r
4763     for (x = 1; x < num_clips; x++) {\r
4764       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4765         abort();   // this should never ever happen!\r
4766     } \r
4767   }\r
4768 \r
4769   /* Copy the new bitmap onto the screen in one go.\r
4770    * This way we avoid any flickering\r
4771    */\r
4772   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4773   BitBlt(hdc, boardRect.left, boardRect.top,\r
4774          boardRect.right - boardRect.left,\r
4775          boardRect.bottom - boardRect.top,\r
4776          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4777   if(saveDiagFlag) { \r
4778     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4779     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4780 \r
4781     GetObject(bufferBitmap, sizeof(b), &b);\r
4782     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4783         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4784         bih.biWidth = b.bmWidth;\r
4785         bih.biHeight = b.bmHeight;\r
4786         bih.biPlanes = 1;\r
4787         bih.biBitCount = b.bmBitsPixel;\r
4788         bih.biCompression = 0;\r
4789         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4790         bih.biXPelsPerMeter = 0;\r
4791         bih.biYPelsPerMeter = 0;\r
4792         bih.biClrUsed = 0;\r
4793         bih.biClrImportant = 0;\r
4794 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4795 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4796         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4797 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4798 \r
4799 #if 1\r
4800         wb = b.bmWidthBytes;\r
4801         // count colors\r
4802         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4803                 int k = ((int*) pData)[i];\r
4804                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4805                 if(j >= 16) break;\r
4806                 color[j] = k;\r
4807                 if(j >= nrColors) nrColors = j+1;\r
4808         }\r
4809         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4810                 INT p = 0;\r
4811                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4812                     for(w=0; w<(wb>>2); w+=2) {\r
4813                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4814                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4815                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4816                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4817                         pData[p++] = m | j<<4;\r
4818                     }\r
4819                     while(p&3) pData[p++] = 0;\r
4820                 }\r
4821                 fac = 3;\r
4822                 wb = ((wb+31)>>5)<<2;\r
4823         }\r
4824         // write BITMAPFILEHEADER\r
4825         fprintf(diagFile, "BM");\r
4826         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4827         fputDW(diagFile, 0);\r
4828         fputDW(diagFile, 0x36 + (fac?64:0));\r
4829         // write BITMAPINFOHEADER\r
4830         fputDW(diagFile, 40);\r
4831         fputDW(diagFile, b.bmWidth);\r
4832         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4833         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4834         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4835         fputDW(diagFile, 0);\r
4836         fputDW(diagFile, 0);\r
4837         fputDW(diagFile, 0);\r
4838         fputDW(diagFile, 0);\r
4839         fputDW(diagFile, 0);\r
4840         fputDW(diagFile, 0);\r
4841         // write color table\r
4842         if(fac)\r
4843         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4844         // write bitmap data\r
4845         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4846                 fputc(pData[i], diagFile);\r
4847 #endif\r
4848      }\r
4849   }\r
4850 \r
4851   SelectObject(tmphdc, oldBitmap);\r
4852 \r
4853   /* Massive cleanup */\r
4854   for (x = 0; x < num_clips; x++)\r
4855     DeleteObject(clips[x]);\r
4856 \r
4857   DeleteDC(tmphdc);\r
4858   DeleteObject(bufferBitmap);\r
4859 \r
4860   if (releaseDC) \r
4861     ReleaseDC(hwndMain, hdc);\r
4862   \r
4863   if (lastDrawnFlipView != flipView) {\r
4864     if (flipView)\r
4865       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4866     else\r
4867       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4868   }\r
4869 \r
4870 /*  CopyBoard(lastDrawn, board);*/\r
4871   lastDrawnHighlight = highlightInfo;\r
4872   lastDrawnPremove   = premoveHighlightInfo;\r
4873   lastDrawnFlipView = flipView;\r
4874   lastDrawnValid = 1;\r
4875 }\r
4876 \r
4877 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4878 int\r
4879 SaveDiagram(f)\r
4880      FILE *f;\r
4881 {\r
4882     saveDiagFlag = 1; diagFile = f;\r
4883     HDCDrawPosition(NULL, TRUE, NULL);\r
4884 \r
4885     saveDiagFlag = 0;\r
4886 \r
4887 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4888     \r
4889     fclose(f);\r
4890     return TRUE;\r
4891 }\r
4892 \r
4893 \r
4894 /*---------------------------------------------------------------------------*\\r
4895 | CLIENT PAINT PROCEDURE\r
4896 |   This is the main event-handler for the WM_PAINT message.\r
4897 |\r
4898 \*---------------------------------------------------------------------------*/\r
4899 VOID\r
4900 PaintProc(HWND hwnd)\r
4901 {\r
4902   HDC         hdc;\r
4903   PAINTSTRUCT ps;\r
4904   HFONT       oldFont;\r
4905 \r
4906   if((hdc = BeginPaint(hwnd, &ps))) {\r
4907     if (IsIconic(hwnd)) {\r
4908       DrawIcon(hdc, 2, 2, iconCurrent);\r
4909     } else {\r
4910       if (!appData.monoMode) {\r
4911         SelectPalette(hdc, hPal, FALSE);\r
4912         RealizePalette(hdc);\r
4913       }\r
4914       HDCDrawPosition(hdc, 1, NULL);\r
4915       oldFont =\r
4916         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4917       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4918                  ETO_CLIPPED|ETO_OPAQUE,\r
4919                  &messageRect, messageText, strlen(messageText), NULL);\r
4920       SelectObject(hdc, oldFont);\r
4921       DisplayBothClocks();\r
4922     }\r
4923     EndPaint(hwnd,&ps);\r
4924   }\r
4925 \r
4926   return;\r
4927 }\r
4928 \r
4929 \r
4930 /*\r
4931  * If the user selects on a border boundary, return -1; if off the board,\r
4932  *   return -2.  Otherwise map the event coordinate to the square.\r
4933  * The offset boardRect.left or boardRect.top must already have been\r
4934  *   subtracted from x.\r
4935  */\r
4936 int\r
4937 EventToSquare(int x)\r
4938 {\r
4939   if (x <= 0)\r
4940     return -2;\r
4941   if (x < lineGap)\r
4942     return -1;\r
4943   x -= lineGap;\r
4944   if ((x % (squareSize + lineGap)) >= squareSize)\r
4945     return -1;\r
4946   x /= (squareSize + lineGap);\r
4947   if (x >= BOARD_SIZE)\r
4948     return -2;\r
4949   return x;\r
4950 }\r
4951 \r
4952 typedef struct {\r
4953   char piece;\r
4954   int command;\r
4955   char* name;\r
4956 } DropEnable;\r
4957 \r
4958 DropEnable dropEnables[] = {\r
4959   { 'P', DP_Pawn, "Pawn" },\r
4960   { 'N', DP_Knight, "Knight" },\r
4961   { 'B', DP_Bishop, "Bishop" },\r
4962   { 'R', DP_Rook, "Rook" },\r
4963   { 'Q', DP_Queen, "Queen" },\r
4964 };\r
4965 \r
4966 VOID\r
4967 SetupDropMenu(HMENU hmenu)\r
4968 {\r
4969   int i, count, enable;\r
4970   char *p;\r
4971   extern char white_holding[], black_holding[];\r
4972   char item[MSG_SIZ];\r
4973 \r
4974   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4975     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4976                dropEnables[i].piece);\r
4977     count = 0;\r
4978     while (p && *p++ == dropEnables[i].piece) count++;\r
4979     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4980     enable = count > 0 || !appData.testLegality\r
4981       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4982                       && !appData.icsActive);\r
4983     ModifyMenu(hmenu, dropEnables[i].command,\r
4984                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4985                dropEnables[i].command, item);\r
4986   }\r
4987 }\r
4988 \r
4989 /* Event handler for mouse messages */\r
4990 VOID\r
4991 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4992 {\r
4993   int x, y;\r
4994   POINT pt;\r
4995   static int recursive = 0;\r
4996   HMENU hmenu;\r
4997 //  BOOLEAN needsRedraw = FALSE;\r
4998   BOOLEAN saveAnimate;\r
4999   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
5000   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
5001   ChessMove moveType;\r
5002 \r
5003   if (recursive) {\r
5004     if (message == WM_MBUTTONUP) {\r
5005       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
5006          to the middle button: we simulate pressing the left button too!\r
5007          */\r
5008       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
5009       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
5010     }\r
5011     return;\r
5012   }\r
5013   recursive++;\r
5014   \r
5015   pt.x = LOWORD(lParam);\r
5016   pt.y = HIWORD(lParam);\r
5017   x = EventToSquare(pt.x - boardRect.left);\r
5018   y = EventToSquare(pt.y - boardRect.top);\r
5019   if (!flipView && y >= 0) {\r
5020     y = BOARD_HEIGHT - 1 - y;\r
5021   }\r
5022   if (flipView && x >= 0) {\r
5023     x = BOARD_WIDTH - 1 - x;\r
5024   }\r
5025 \r
5026   switch (message) {\r
5027   case WM_LBUTTONDOWN:\r
5028     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
5029         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
5030         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
5031         if(gameInfo.holdingsWidth && \r
5032                 (WhiteOnMove(currentMove) \r
5033                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
5034                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
5035             // click in right holdings, for determining promotion piece\r
5036             ChessSquare p = boards[currentMove][y][x];\r
5037             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
5038             if(p != EmptySquare) {\r
5039                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
5040                 fromX = fromY = -1;\r
5041                 break;\r
5042             }\r
5043         }\r
5044         DrawPosition(FALSE, boards[currentMove]);\r
5045         break;\r
5046     }\r
5047     ErrorPopDown();\r
5048     sameAgain = FALSE;\r
5049     if (y == -2) {\r
5050       /* Downclick vertically off board; check if on clock */\r
5051       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5052         if (gameMode == EditPosition) {\r
5053           SetWhiteToPlayEvent();\r
5054         } else if (gameMode == IcsPlayingBlack ||\r
5055                    gameMode == MachinePlaysWhite) {\r
5056           CallFlagEvent();\r
5057         } else if (gameMode == EditGame) {\r
5058           AdjustClock(flipClock, -1);\r
5059         }\r
5060       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5061         if (gameMode == EditPosition) {\r
5062           SetBlackToPlayEvent();\r
5063         } else if (gameMode == IcsPlayingWhite ||\r
5064                    gameMode == MachinePlaysBlack) {\r
5065           CallFlagEvent();\r
5066         } else if (gameMode == EditGame) {\r
5067           AdjustClock(!flipClock, -1);\r
5068         }\r
5069       }\r
5070       if (!appData.highlightLastMove) {\r
5071         ClearHighlights();\r
5072         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
5073       }\r
5074       fromX = fromY = -1;\r
5075       dragInfo.start.x = dragInfo.start.y = -1;\r
5076       dragInfo.from = dragInfo.start;\r
5077       break;\r
5078     } else if (x < 0 || y < 0\r
5079       /* [HGM] block clicks between board and holdings */\r
5080               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
5081               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
5082               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
5083         /* EditPosition, empty square, or different color piece;\r
5084            click-click move is possible */\r
5085                                ) {\r
5086       break;\r
5087     } else if (fromX == x && fromY == y) {\r
5088       /* Downclick on same square again */\r
5089       ClearHighlights();\r
5090       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5091       sameAgain = TRUE;  \r
5092     } else if (fromX != -1 &&\r
5093                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5094                                                                         ) {\r
5095       /* Downclick on different square. */\r
5096       /* [HGM] if on holdings file, should count as new first click ! */\r
5097       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5098         toX = x;\r
5099         toY = y;\r
5100         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5101            to make sure move is legal before showing promotion popup */\r
5102         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5103         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5104                 fromX = fromY = -1; \r
5105                 ClearHighlights();\r
5106                 DrawPosition(FALSE, boards[currentMove]);\r
5107                 break; \r
5108         } else \r
5109         if(moveType != ImpossibleMove) {\r
5110           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5111           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5112             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5113               appData.alwaysPromoteToQueen)) {\r
5114                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5115                   if (!appData.highlightLastMove) {\r
5116                       ClearHighlights();\r
5117                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5118                   }\r
5119           } else\r
5120           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5121                   SetHighlights(fromX, fromY, toX, toY);\r
5122                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5123                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5124                      If promotion to Q is legal, all are legal! */\r
5125                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5126                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5127                     // kludge to temporarily execute move on display, wthout promotng yet\r
5128                     promotionChoice = TRUE;\r
5129                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5130                     boards[currentMove][toY][toX] = p;\r
5131                     DrawPosition(FALSE, boards[currentMove]);\r
5132                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5133                     boards[currentMove][toY][toX] = q;\r
5134                   } else\r
5135                   PromotionPopup(hwnd);\r
5136           } else {       /* not a promotion */\r
5137              if (appData.animate || appData.highlightLastMove) {\r
5138                  SetHighlights(fromX, fromY, toX, toY);\r
5139              } else {\r
5140                  ClearHighlights();\r
5141              }\r
5142              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5143              if (appData.animate && !appData.highlightLastMove) {\r
5144                   ClearHighlights();\r
5145                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5146              }\r
5147           }\r
5148           fromX = fromY = -1;\r
5149           break;\r
5150         }\r
5151         if (gotPremove) {\r
5152             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5153             DrawPosition(forceFullRepaint || FALSE, NULL);\r
5154         } else ClearHighlights();\r
5155         fromX = fromY = -1;\r
5156         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5157     }\r
5158     /* First downclick, or restart on a square with same color piece */\r
5159     if (!frozen && OKToStartUserMove(x, y)) {\r
5160       fromX = x;\r
5161       fromY = y;\r
5162       dragInfo.lastpos = pt;\r
5163       dragInfo.from.x = fromX;\r
5164       dragInfo.from.y = fromY;\r
5165       dragInfo.start = dragInfo.from;\r
5166       SetCapture(hwndMain);\r
5167     } else {\r
5168       fromX = fromY = -1;\r
5169       dragInfo.start.x = dragInfo.start.y = -1;\r
5170       dragInfo.from = dragInfo.start;\r
5171       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5172     }\r
5173     break;\r
5174 \r
5175   case WM_LBUTTONUP:\r
5176     ReleaseCapture();\r
5177     if (fromX == -1) break;\r
5178     if (x == fromX && y == fromY) {\r
5179       dragInfo.from.x = dragInfo.from.y = -1;\r
5180       /* Upclick on same square */\r
5181       if (sameAgain) {\r
5182         /* Clicked same square twice: abort click-click move */\r
5183         fromX = fromY = -1;\r
5184         gotPremove = 0;\r
5185         ClearPremoveHighlights();\r
5186       } else {\r
5187         /* First square clicked: start click-click move */\r
5188         SetHighlights(fromX, fromY, -1, -1);\r
5189       }\r
5190       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5191     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5192       /* Errant click; ignore */\r
5193       break;\r
5194     } else {\r
5195       /* Finish drag move. */\r
5196     if (appData.debugMode) {\r
5197         fprintf(debugFP, "release\n");\r
5198     }\r
5199       dragInfo.from.x = dragInfo.from.y = -1;\r
5200       toX = x;\r
5201       toY = y;\r
5202       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5203       appData.animate = appData.animate && !appData.animateDragging;\r
5204       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5205       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5206                 fromX = fromY = -1; \r
5207                 ClearHighlights();\r
5208                 DrawPosition(FALSE, boards[currentMove]);\r
5209                 appData.animate = saveAnimate;\r
5210                 break; \r
5211       } else \r
5212       if(moveType != ImpossibleMove) {\r
5213           /* [HGM] use move type to determine if move is promotion.\r
5214              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5215           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5216             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5217               appData.alwaysPromoteToQueen)) \r
5218                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5219           else \r
5220           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5221                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5222                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5223                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5224                     // kludge to temporarily execute move on display, wthout promotng yet\r
5225                     promotionChoice = TRUE;\r
5226                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5227                     boards[currentMove][toY][toX] = p;\r
5228                     DrawPosition(FALSE, boards[currentMove]);\r
5229                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5230                     boards[currentMove][toY][toX] = q;\r
5231                     appData.animate = saveAnimate;\r
5232                     break;\r
5233                   } else\r
5234                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5235           } else {\r
5236             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5237                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5238                                         moveType == WhiteCapturesEnPassant || \r
5239                                         moveType == BlackCapturesEnPassant   ) )\r
5240                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5241             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5242           }\r
5243       }\r
5244       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5245       appData.animate = saveAnimate;\r
5246       fromX = fromY = -1;\r
5247       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5248         ClearHighlights();\r
5249       }\r
5250       if (appData.animate || appData.animateDragging ||\r
5251           appData.highlightDragging || gotPremove) {\r
5252         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5253       }\r
5254     }\r
5255     dragInfo.start.x = dragInfo.start.y = -1; \r
5256     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5257     break;\r
5258 \r
5259   case WM_MOUSEMOVE:\r
5260     if ((appData.animateDragging || appData.highlightDragging)\r
5261         && (wParam & MK_LBUTTON)\r
5262         && dragInfo.from.x >= 0) \r
5263     {\r
5264       BOOL full_repaint = FALSE;\r
5265 \r
5266       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5267       if (appData.animateDragging) {\r
5268         dragInfo.pos = pt;\r
5269       }\r
5270       if (appData.highlightDragging) {\r
5271         SetHighlights(fromX, fromY, x, y);\r
5272         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5273             full_repaint = TRUE;\r
5274         }\r
5275       }\r
5276       \r
5277       DrawPosition( full_repaint, NULL);\r
5278       \r
5279       dragInfo.lastpos = dragInfo.pos;\r
5280     }\r
5281     break;\r
5282 \r
5283   case WM_MOUSEWHEEL: // [DM]\r
5284     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5285        /* Mouse Wheel is being rolled forward\r
5286         * Play moves forward\r
5287         */\r
5288        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5289                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5290        /* Mouse Wheel is being rolled backward\r
5291         * Play moves backward\r
5292         */\r
5293        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5294                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5295     }\r
5296     break;\r
5297 \r
5298   case WM_MBUTTONDOWN:\r
5299   case WM_RBUTTONDOWN:\r
5300     ErrorPopDown();\r
5301     ReleaseCapture();\r
5302     fromX = fromY = -1;\r
5303     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5304     dragInfo.start.x = dragInfo.start.y = -1;\r
5305     dragInfo.from = dragInfo.start;\r
5306     dragInfo.lastpos = dragInfo.pos;\r
5307     if (appData.highlightDragging) {\r
5308       ClearHighlights();\r
5309     }\r
5310     if(y == -2) {\r
5311       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5312       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5313           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5314       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5315           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5316       }\r
5317     }\r
5318     DrawPosition(TRUE, NULL);\r
5319 \r
5320     switch (gameMode) {\r
5321     case EditPosition:\r
5322     case IcsExamining:\r
5323       if (x < 0 || y < 0) break;\r
5324       fromX = x;\r
5325       fromY = y;\r
5326       if (message == WM_MBUTTONDOWN) {\r
5327         buttonCount = 3;  /* even if system didn't think so */\r
5328         if (wParam & MK_SHIFT) \r
5329           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5330         else\r
5331           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5332       } else { /* message == WM_RBUTTONDOWN */\r
5333 #if 0\r
5334         if (buttonCount == 3) {\r
5335           if (wParam & MK_SHIFT) \r
5336             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5337           else\r
5338             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5339         } else {\r
5340           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5341         }\r
5342 #else\r
5343         /* Just have one menu, on the right button.  Windows users don't\r
5344            think to try the middle one, and sometimes other software steals\r
5345            it, or it doesn't really exist. */\r
5346         if(gameInfo.variant != VariantShogi)\r
5347             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5348         else\r
5349             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5350 #endif\r
5351       }\r
5352       break;\r
5353     case IcsPlayingWhite:\r
5354     case IcsPlayingBlack:\r
5355     case EditGame:\r
5356     case MachinePlaysWhite:\r
5357     case MachinePlaysBlack:\r
5358       if (appData.testLegality &&\r
5359           gameInfo.variant != VariantBughouse &&\r
5360           gameInfo.variant != VariantCrazyhouse) break;\r
5361       if (x < 0 || y < 0) break;\r
5362       fromX = x;\r
5363       fromY = y;\r
5364       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5365       SetupDropMenu(hmenu);\r
5366       MenuPopup(hwnd, pt, hmenu, -1);\r
5367       break;\r
5368     default:\r
5369       break;\r
5370     }\r
5371     break;\r
5372   }\r
5373 \r
5374   recursive--;\r
5375 }\r
5376 \r
5377 /* Preprocess messages for buttons in main window */\r
5378 LRESULT CALLBACK\r
5379 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5380 {\r
5381   int id = GetWindowLong(hwnd, GWL_ID);\r
5382   int i, dir;\r
5383 \r
5384   for (i=0; i<N_BUTTONS; i++) {\r
5385     if (buttonDesc[i].id == id) break;\r
5386   }\r
5387   if (i == N_BUTTONS) return 0;\r
5388   switch (message) {\r
5389   case WM_KEYDOWN:\r
5390     switch (wParam) {\r
5391     case VK_LEFT:\r
5392     case VK_RIGHT:\r
5393       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5394       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5395       return TRUE;\r
5396     }\r
5397     break;\r
5398   case WM_CHAR:\r
5399     switch (wParam) {\r
5400     case '\r':\r
5401       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5402       return TRUE;\r
5403     default:\r
5404       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5405         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5406         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5407         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5408         SetFocus(h);\r
5409         SendMessage(h, WM_CHAR, wParam, lParam);\r
5410         return TRUE;\r
5411       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5412         PopUpMoveDialog((char)wParam);\r
5413       }\r
5414       break;\r
5415     }\r
5416     break;\r
5417   }\r
5418   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5419 }\r
5420 \r
5421 /* Process messages for Promotion dialog box */\r
5422 LRESULT CALLBACK\r
5423 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5424 {\r
5425   char promoChar;\r
5426 \r
5427   switch (message) {\r
5428   case WM_INITDIALOG: /* message: initialize dialog box */\r
5429     /* Center the dialog over the application window */\r
5430     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5431     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5432       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5433        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5434                SW_SHOW : SW_HIDE);\r
5435     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5436     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5437        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5438          PieceToChar(WhiteAngel) != '~') ||\r
5439         (PieceToChar(BlackAngel) >= 'A' &&\r
5440          PieceToChar(BlackAngel) != '~')   ) ?\r
5441                SW_SHOW : SW_HIDE);\r
5442     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5443        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5444          PieceToChar(WhiteMarshall) != '~') ||\r
5445         (PieceToChar(BlackMarshall) >= 'A' &&\r
5446          PieceToChar(BlackMarshall) != '~')   ) ?\r
5447                SW_SHOW : SW_HIDE);\r
5448     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5449     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5450        gameInfo.variant != VariantShogi ?\r
5451                SW_SHOW : SW_HIDE);\r
5452     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5453        gameInfo.variant != VariantShogi ?\r
5454                SW_SHOW : SW_HIDE);\r
5455     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5456        gameInfo.variant == VariantShogi ?\r
5457                SW_SHOW : SW_HIDE);\r
5458     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5459        gameInfo.variant == VariantShogi ?\r
5460                SW_SHOW : SW_HIDE);\r
5461     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5462        gameInfo.variant == VariantSuper ?\r
5463                SW_SHOW : SW_HIDE);\r
5464     return TRUE;\r
5465 \r
5466   case WM_COMMAND: /* message: received a command */\r
5467     switch (LOWORD(wParam)) {\r
5468     case IDCANCEL:\r
5469       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5470       ClearHighlights();\r
5471       DrawPosition(FALSE, NULL);\r
5472       return TRUE;\r
5473     case PB_King:\r
5474       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5475       break;\r
5476     case PB_Queen:\r
5477       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5478       break;\r
5479     case PB_Rook:\r
5480       promoChar = PieceToChar(BlackRook);\r
5481       break;\r
5482     case PB_Bishop:\r
5483       promoChar = PieceToChar(BlackBishop);\r
5484       break;\r
5485     case PB_Chancellor:\r
5486       promoChar = PieceToChar(BlackMarshall);\r
5487       break;\r
5488     case PB_Archbishop:\r
5489       promoChar = PieceToChar(BlackAngel);\r
5490       break;\r
5491     case PB_Knight:\r
5492       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5493       break;\r
5494     default:\r
5495       return FALSE;\r
5496     }\r
5497     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5498     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5499        only show the popup when we are already sure the move is valid or\r
5500        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5501        will figure out it is a promotion from the promoChar. */\r
5502     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5503     if (!appData.highlightLastMove) {\r
5504       ClearHighlights();\r
5505       DrawPosition(FALSE, NULL);\r
5506     }\r
5507     return TRUE;\r
5508   }\r
5509   return FALSE;\r
5510 }\r
5511 \r
5512 /* Pop up promotion dialog */\r
5513 VOID\r
5514 PromotionPopup(HWND hwnd)\r
5515 {\r
5516   FARPROC lpProc;\r
5517 \r
5518   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5519   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5520     hwnd, (DLGPROC)lpProc);\r
5521   FreeProcInstance(lpProc);\r
5522 }\r
5523 \r
5524 /* Toggle ShowThinking */\r
5525 VOID\r
5526 ToggleShowThinking()\r
5527 {\r
5528   appData.showThinking = !appData.showThinking;\r
5529   ShowThinkingEvent();\r
5530 }\r
5531 \r
5532 VOID\r
5533 LoadGameDialog(HWND hwnd, char* title)\r
5534 {\r
5535   UINT number = 0;\r
5536   FILE *f;\r
5537   char fileTitle[MSG_SIZ];\r
5538   f = OpenFileDialog(hwnd, "rb", "",\r
5539                      appData.oldSaveStyle ? "gam" : "pgn",\r
5540                      GAME_FILT,\r
5541                      title, &number, fileTitle, NULL);\r
5542   if (f != NULL) {\r
5543     cmailMsgLoaded = FALSE;\r
5544     if (number == 0) {\r
5545       int error = GameListBuild(f);\r
5546       if (error) {\r
5547         DisplayError("Cannot build game list", error);\r
5548       } else if (!ListEmpty(&gameList) &&\r
5549                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5550         GameListPopUp(f, fileTitle);\r
5551         return;\r
5552       }\r
5553       GameListDestroy();\r
5554       number = 1;\r
5555     }\r
5556     LoadGame(f, number, fileTitle, FALSE);\r
5557   }\r
5558 }\r
5559 \r
5560 VOID\r
5561 ChangedConsoleFont()\r
5562 {\r
5563   CHARFORMAT cfmt;\r
5564   CHARRANGE tmpsel, sel;\r
5565   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5566   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5567   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5568   PARAFORMAT paraf;\r
5569 \r
5570   cfmt.cbSize = sizeof(CHARFORMAT);\r
5571   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5572   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5573   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5574    * size.  This was undocumented in the version of MSVC++ that I had\r
5575    * when I wrote the code, but is apparently documented now.\r
5576    */\r
5577   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5578   cfmt.bCharSet = f->lf.lfCharSet;\r
5579   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5580   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5581   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5582   /* Why are the following seemingly needed too? */\r
5583   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5584   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5585   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5586   tmpsel.cpMin = 0;\r
5587   tmpsel.cpMax = -1; /*999999?*/\r
5588   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5589   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5590   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5591    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5592    */\r
5593   paraf.cbSize = sizeof(paraf);\r
5594   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5595   paraf.dxStartIndent = 0;\r
5596   paraf.dxOffset = WRAP_INDENT;\r
5597   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5598   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5599 }\r
5600 \r
5601 /*---------------------------------------------------------------------------*\\r
5602  *\r
5603  * Window Proc for main window\r
5604  *\r
5605 \*---------------------------------------------------------------------------*/\r
5606 \r
5607 /* Process messages for main window, etc. */\r
5608 LRESULT CALLBACK\r
5609 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5610 {\r
5611   FARPROC lpProc;\r
5612   int wmId, wmEvent;\r
5613   char *defName;\r
5614   FILE *f;\r
5615   UINT number;\r
5616   char fileTitle[MSG_SIZ];\r
5617   char buf[MSG_SIZ];\r
5618   static SnapData sd;\r
5619 \r
5620   switch (message) {\r
5621 \r
5622   case WM_PAINT: /* message: repaint portion of window */\r
5623     PaintProc(hwnd);\r
5624     break;\r
5625 \r
5626   case WM_ERASEBKGND:\r
5627     if (IsIconic(hwnd)) {\r
5628       /* Cheat; change the message */\r
5629       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5630     } else {\r
5631       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5632     }\r
5633     break;\r
5634 \r
5635   case WM_LBUTTONDOWN:\r
5636   case WM_MBUTTONDOWN:\r
5637   case WM_RBUTTONDOWN:\r
5638   case WM_LBUTTONUP:\r
5639   case WM_MBUTTONUP:\r
5640   case WM_RBUTTONUP:\r
5641   case WM_MOUSEMOVE:\r
5642   case WM_MOUSEWHEEL:\r
5643     MouseEvent(hwnd, message, wParam, lParam);\r
5644     break;\r
5645 \r
5646   JAWS_KB_NAVIGATION\r
5647 \r
5648   case WM_CHAR:\r
5649     \r
5650     JAWS_ALT_INTERCEPT\r
5651 \r
5652     if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) { \r
5653         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5654         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5655         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5656         SetFocus(h);\r
5657         SendMessage(h, message, wParam, lParam);\r
5658     } else if(lParam != KF_REPEAT) {\r
5659         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5660                 PopUpMoveDialog((char)wParam);\r
5661         } else if((char)wParam == 003) CopyGameToClipboard();\r
5662          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5663     }\r
5664 \r
5665     break;\r
5666 \r
5667   case WM_PALETTECHANGED:\r
5668     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5669       int nnew;\r
5670       HDC hdc = GetDC(hwndMain);\r
5671       SelectPalette(hdc, hPal, TRUE);\r
5672       nnew = RealizePalette(hdc);\r
5673       if (nnew > 0) {\r
5674         paletteChanged = TRUE;\r
5675 #if 0\r
5676         UpdateColors(hdc);\r
5677 #else\r
5678         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5679 #endif\r
5680       }\r
5681       ReleaseDC(hwnd, hdc);\r
5682     }\r
5683     break;\r
5684 \r
5685   case WM_QUERYNEWPALETTE:\r
5686     if (!appData.monoMode /*&& paletteChanged*/) {\r
5687       int nnew;\r
5688       HDC hdc = GetDC(hwndMain);\r
5689       paletteChanged = FALSE;\r
5690       SelectPalette(hdc, hPal, FALSE);\r
5691       nnew = RealizePalette(hdc);\r
5692       if (nnew > 0) {\r
5693         InvalidateRect(hwnd, &boardRect, FALSE);\r
5694       }\r
5695       ReleaseDC(hwnd, hdc);\r
5696       return TRUE;\r
5697     }\r
5698     return FALSE;\r
5699 \r
5700   case WM_COMMAND: /* message: command from application menu */\r
5701     wmId    = LOWORD(wParam);\r
5702     wmEvent = HIWORD(wParam);\r
5703 \r
5704     switch (wmId) {\r
5705     case IDM_NewGame:\r
5706       ResetGameEvent();\r
5707       AnalysisPopDown();\r
5708       SAY("new game enter a move to play against the computer with white");\r
5709       break;\r
5710 \r
5711     case IDM_NewGameFRC:\r
5712       if( NewGameFRC() == 0 ) {\r
5713         ResetGameEvent();\r
5714         AnalysisPopDown();\r
5715       }\r
5716       break;\r
5717 \r
5718     case IDM_NewVariant:\r
5719       NewVariantPopup(hwnd);\r
5720       break;\r
5721 \r
5722     case IDM_LoadGame:\r
5723       LoadGameDialog(hwnd, "Load Game from File");\r
5724       break;\r
5725 \r
5726     case IDM_LoadNextGame:\r
5727       ReloadGame(1);\r
5728       break;\r
5729 \r
5730     case IDM_LoadPrevGame:\r
5731       ReloadGame(-1);\r
5732       break;\r
5733 \r
5734     case IDM_ReloadGame:\r
5735       ReloadGame(0);\r
5736       break;\r
5737 \r
5738     case IDM_LoadPosition:\r
5739       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5740         Reset(FALSE, TRUE);\r
5741       }\r
5742       number = 1;\r
5743       f = OpenFileDialog(hwnd, "rb", "",\r
5744                          appData.oldSaveStyle ? "pos" : "fen",\r
5745                          POSITION_FILT,\r
5746                          "Load Position from File", &number, fileTitle, NULL);\r
5747       if (f != NULL) {\r
5748         LoadPosition(f, number, fileTitle);\r
5749       }\r
5750       break;\r
5751 \r
5752     case IDM_LoadNextPosition:\r
5753       ReloadPosition(1);\r
5754       break;\r
5755 \r
5756     case IDM_LoadPrevPosition:\r
5757       ReloadPosition(-1);\r
5758       break;\r
5759 \r
5760     case IDM_ReloadPosition:\r
5761       ReloadPosition(0);\r
5762       break;\r
5763 \r
5764     case IDM_SaveGame:\r
5765       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5766       f = OpenFileDialog(hwnd, "a", defName,\r
5767                          appData.oldSaveStyle ? "gam" : "pgn",\r
5768                          GAME_FILT,\r
5769                          "Save Game to File", NULL, fileTitle, NULL);\r
5770       if (f != NULL) {\r
5771         SaveGame(f, 0, "");\r
5772       }\r
5773       break;\r
5774 \r
5775     case IDM_SavePosition:\r
5776       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5777       f = OpenFileDialog(hwnd, "a", defName,\r
5778                          appData.oldSaveStyle ? "pos" : "fen",\r
5779                          POSITION_FILT,\r
5780                          "Save Position to File", NULL, fileTitle, NULL);\r
5781       if (f != NULL) {\r
5782         SavePosition(f, 0, "");\r
5783       }\r
5784       break;\r
5785 \r
5786     case IDM_SaveDiagram:\r
5787       defName = "diagram";\r
5788       f = OpenFileDialog(hwnd, "wb", defName,\r
5789                          "bmp",\r
5790                          DIAGRAM_FILT,\r
5791                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5792       if (f != NULL) {\r
5793         SaveDiagram(f);\r
5794       }\r
5795       break;\r
5796 \r
5797     case IDM_CopyGame:\r
5798       CopyGameToClipboard();\r
5799       break;\r
5800 \r
5801     case IDM_PasteGame:\r
5802       PasteGameFromClipboard();\r
5803       break;\r
5804 \r
5805     case IDM_CopyGameListToClipboard:\r
5806       CopyGameListToClipboard();\r
5807       break;\r
5808 \r
5809     /* [AS] Autodetect FEN or PGN data */\r
5810     case IDM_PasteAny:\r
5811       PasteGameOrFENFromClipboard();\r
5812       break;\r
5813 \r
5814     /* [AS] Move history */\r
5815     case IDM_ShowMoveHistory:\r
5816         if( MoveHistoryIsUp() ) {\r
5817             MoveHistoryPopDown();\r
5818         }\r
5819         else {\r
5820             MoveHistoryPopUp();\r
5821         }\r
5822         break;\r
5823 \r
5824     /* [AS] Eval graph */\r
5825     case IDM_ShowEvalGraph:\r
5826         if( EvalGraphIsUp() ) {\r
5827             EvalGraphPopDown();\r
5828         }\r
5829         else {\r
5830             EvalGraphPopUp();\r
5831             SetFocus(hwndMain);\r
5832         }\r
5833         break;\r
5834 \r
5835     /* [AS] Engine output */\r
5836     case IDM_ShowEngineOutput:\r
5837         if( EngineOutputIsUp() ) {\r
5838             EngineOutputPopDown();\r
5839         }\r
5840         else {\r
5841             EngineOutputPopUp();\r
5842         }\r
5843         break;\r
5844 \r
5845     /* [AS] User adjudication */\r
5846     case IDM_UserAdjudication_White:\r
5847         UserAdjudicationEvent( +1 );\r
5848         break;\r
5849 \r
5850     case IDM_UserAdjudication_Black:\r
5851         UserAdjudicationEvent( -1 );\r
5852         break;\r
5853 \r
5854     case IDM_UserAdjudication_Draw:\r
5855         UserAdjudicationEvent( 0 );\r
5856         break;\r
5857 \r
5858     /* [AS] Game list options dialog */\r
5859     case IDM_GameListOptions:\r
5860       GameListOptions();\r
5861       break;\r
5862 \r
5863     case IDM_CopyPosition:\r
5864       CopyFENToClipboard();\r
5865       break;\r
5866 \r
5867     case IDM_PastePosition:\r
5868       PasteFENFromClipboard();\r
5869       break;\r
5870 \r
5871     case IDM_MailMove:\r
5872       MailMoveEvent();\r
5873       break;\r
5874 \r
5875     case IDM_ReloadCMailMsg:\r
5876       Reset(TRUE, TRUE);\r
5877       ReloadCmailMsgEvent(FALSE);\r
5878       break;\r
5879 \r
5880     case IDM_Minimize:\r
5881       ShowWindow(hwnd, SW_MINIMIZE);\r
5882       break;\r
5883 \r
5884     case IDM_Exit:\r
5885       ExitEvent(0);\r
5886       break;\r
5887 \r
5888     case IDM_MachineWhite:\r
5889       MachineWhiteEvent();\r
5890       /*\r
5891        * refresh the tags dialog only if it's visible\r
5892        */\r
5893       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5894           char *tags;\r
5895           tags = PGNTags(&gameInfo);\r
5896           TagsPopUp(tags, CmailMsg());\r
5897           free(tags);\r
5898       }\r
5899       SAY("computer starts playing white");\r
5900       break;\r
5901 \r
5902     case IDM_MachineBlack:\r
5903       MachineBlackEvent();\r
5904       /*\r
5905        * refresh the tags dialog only if it's visible\r
5906        */\r
5907       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5908           char *tags;\r
5909           tags = PGNTags(&gameInfo);\r
5910           TagsPopUp(tags, CmailMsg());\r
5911           free(tags);\r
5912       }\r
5913       SAY("computer starts playing black");\r
5914       break;\r
5915 \r
5916     case IDM_TwoMachines:\r
5917       TwoMachinesEvent();\r
5918       /*\r
5919        * refresh the tags dialog only if it's visible\r
5920        */\r
5921       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5922           char *tags;\r
5923           tags = PGNTags(&gameInfo);\r
5924           TagsPopUp(tags, CmailMsg());\r
5925           free(tags);\r
5926       }\r
5927       SAY("programs start playing each other");\r
5928       break;\r
5929 \r
5930     case IDM_AnalysisMode:\r
5931       if (!first.analysisSupport) {\r
5932         sprintf(buf, "%s does not support analysis", first.tidy);\r
5933         DisplayError(buf, 0);\r
5934       } else {\r
5935         SAY("analyzing current position");\r
5936         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5937         if (appData.icsActive) {\r
5938                if (gameMode != IcsObserving) {\r
5939                        sprintf(buf, "You are not observing a game");\r
5940                        DisplayError(buf, 0);\r
5941                        /* secure check */\r
5942                        if (appData.icsEngineAnalyze) {\r
5943                                if (appData.debugMode) \r
5944                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5945                                ExitAnalyzeMode();\r
5946                                ModeHighlight();\r
5947                                break;\r
5948                        }\r
5949                        break;\r
5950                } else {\r
5951                        /* if enable, user want disable icsEngineAnalyze */\r
5952                        if (appData.icsEngineAnalyze) {\r
5953                                ExitAnalyzeMode();\r
5954                                ModeHighlight();\r
5955                                break;\r
5956                        }\r
5957                        appData.icsEngineAnalyze = TRUE;\r
5958                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5959                }\r
5960         } \r
5961         if (!appData.showThinking) ToggleShowThinking();\r
5962         AnalyzeModeEvent();\r
5963       }\r
5964       break;\r
5965 \r
5966     case IDM_AnalyzeFile:\r
5967       if (!first.analysisSupport) {\r
5968         char buf[MSG_SIZ];\r
5969         sprintf(buf, "%s does not support analysis", first.tidy);\r
5970         DisplayError(buf, 0);\r
5971       } else {\r
5972         if (!appData.showThinking) ToggleShowThinking();\r
5973         AnalyzeFileEvent();\r
5974         LoadGameDialog(hwnd, "Analyze Game from File");\r
5975         AnalysisPeriodicEvent(1);\r
5976       }\r
5977       break;\r
5978 \r
5979     case IDM_IcsClient:\r
5980       IcsClientEvent();\r
5981       break;\r
5982 \r
5983     case IDM_EditGame:\r
5984       EditGameEvent();\r
5985       SAY("edit game");\r
5986       break;\r
5987 \r
5988     case IDM_EditPosition:\r
5989       EditPositionEvent();\r
5990       SAY("to set up a position type a FEN");\r
5991       break;\r
5992 \r
5993     case IDM_Training:\r
5994       TrainingEvent();\r
5995       break;\r
5996 \r
5997     case IDM_ShowGameList:\r
5998       ShowGameListProc();\r
5999       break;\r
6000 \r
6001     case IDM_EditTags:\r
6002       EditTagsProc();\r
6003       break;\r
6004 \r
6005     case IDM_EditComment:\r
6006       if (commentDialogUp && editComment) {\r
6007         CommentPopDown();\r
6008       } else {\r
6009         EditCommentEvent();\r
6010       }\r
6011       break;\r
6012 \r
6013     case IDM_Pause:\r
6014       PauseEvent();\r
6015       break;\r
6016 \r
6017     case IDM_Accept:\r
6018       AcceptEvent();\r
6019       break;\r
6020 \r
6021     case IDM_Decline:\r
6022       DeclineEvent();\r
6023       break;\r
6024 \r
6025     case IDM_Rematch:\r
6026       RematchEvent();\r
6027       break;\r
6028 \r
6029     case IDM_CallFlag:\r
6030       CallFlagEvent();\r
6031       break;\r
6032 \r
6033     case IDM_Draw:\r
6034       DrawEvent();\r
6035       break;\r
6036 \r
6037     case IDM_Adjourn:\r
6038       AdjournEvent();\r
6039       break;\r
6040 \r
6041     case IDM_Abort:\r
6042       AbortEvent();\r
6043       break;\r
6044 \r
6045     case IDM_Resign:\r
6046       ResignEvent();\r
6047       break;\r
6048 \r
6049     case IDM_StopObserving:\r
6050       StopObservingEvent();\r
6051       break;\r
6052 \r
6053     case IDM_StopExamining:\r
6054       StopExaminingEvent();\r
6055       break;\r
6056 \r
6057     case IDM_TypeInMove:\r
6058       PopUpMoveDialog('\000');\r
6059       break;\r
6060 \r
6061     case IDM_TypeInName:\r
6062       PopUpNameDialog('\000');\r
6063       break;\r
6064 \r
6065     case IDM_Backward:\r
6066       BackwardEvent();\r
6067       SetFocus(hwndMain);\r
6068       break;\r
6069 \r
6070     JAWS_MENU_ITEMS\r
6071 \r
6072     case IDM_Forward:\r
6073       ForwardEvent();\r
6074       SetFocus(hwndMain);\r
6075       break;\r
6076 \r
6077     case IDM_ToStart:\r
6078       ToStartEvent();\r
6079       SetFocus(hwndMain);\r
6080       break;\r
6081 \r
6082     case IDM_ToEnd:\r
6083       ToEndEvent();\r
6084       SetFocus(hwndMain);\r
6085       break;\r
6086 \r
6087     case IDM_Revert:\r
6088       RevertEvent();\r
6089       break;\r
6090 \r
6091     case IDM_TruncateGame:\r
6092       TruncateGameEvent();\r
6093       break;\r
6094 \r
6095     case IDM_MoveNow:\r
6096       MoveNowEvent();\r
6097       break;\r
6098 \r
6099     case IDM_RetractMove:\r
6100       RetractMoveEvent();\r
6101       break;\r
6102 \r
6103     case IDM_FlipView:\r
6104       flipView = !flipView;\r
6105       DrawPosition(FALSE, NULL);\r
6106       break;\r
6107 \r
6108     case IDM_FlipClock:\r
6109       flipClock = !flipClock;\r
6110       DisplayBothClocks();\r
6111       DrawPosition(FALSE, NULL);\r
6112       break;\r
6113 \r
6114     case IDM_GeneralOptions:\r
6115       GeneralOptionsPopup(hwnd);\r
6116       DrawPosition(TRUE, NULL);\r
6117       break;\r
6118 \r
6119     case IDM_BoardOptions:\r
6120       BoardOptionsPopup(hwnd);\r
6121       break;\r
6122 \r
6123     case IDM_EnginePlayOptions:\r
6124       EnginePlayOptionsPopup(hwnd);\r
6125       break;\r
6126 \r
6127     case IDM_Engine1Options:\r
6128       EngineOptionsPopup(hwnd, &first);\r
6129       break;\r
6130 \r
6131     case IDM_Engine2Options:\r
6132       EngineOptionsPopup(hwnd, &second);\r
6133       break;\r
6134 \r
6135     case IDM_OptionsUCI:\r
6136       UciOptionsPopup(hwnd);\r
6137       break;\r
6138 \r
6139     case IDM_IcsOptions:\r
6140       IcsOptionsPopup(hwnd);\r
6141       break;\r
6142 \r
6143     case IDM_Fonts:\r
6144       FontsOptionsPopup(hwnd);\r
6145       break;\r
6146 \r
6147     case IDM_Sounds:\r
6148       SoundOptionsPopup(hwnd);\r
6149       break;\r
6150 \r
6151     case IDM_CommPort:\r
6152       CommPortOptionsPopup(hwnd);\r
6153       break;\r
6154 \r
6155     case IDM_LoadOptions:\r
6156       LoadOptionsPopup(hwnd);\r
6157       break;\r
6158 \r
6159     case IDM_SaveOptions:\r
6160       SaveOptionsPopup(hwnd);\r
6161       break;\r
6162 \r
6163     case IDM_TimeControl:\r
6164       TimeControlOptionsPopup(hwnd);\r
6165       break;\r
6166 \r
6167     case IDM_SaveSettings:\r
6168       SaveSettings(settingsFileName);\r
6169       break;\r
6170 \r
6171     case IDM_SaveSettingsOnExit:\r
6172       saveSettingsOnExit = !saveSettingsOnExit;\r
6173       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6174                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6175                                          MF_CHECKED : MF_UNCHECKED));\r
6176       break;\r
6177 \r
6178     case IDM_Hint:\r
6179       HintEvent();\r
6180       break;\r
6181 \r
6182     case IDM_Book:\r
6183       BookEvent();\r
6184       break;\r
6185 \r
6186     case IDM_AboutGame:\r
6187       AboutGameEvent();\r
6188       break;\r
6189 \r
6190     case IDM_Debug:\r
6191       appData.debugMode = !appData.debugMode;\r
6192       if (appData.debugMode) {\r
6193         char dir[MSG_SIZ];\r
6194         GetCurrentDirectory(MSG_SIZ, dir);\r
6195         SetCurrentDirectory(installDir);\r
6196         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6197         SetCurrentDirectory(dir);\r
6198         setbuf(debugFP, NULL);\r
6199       } else {\r
6200         fclose(debugFP);\r
6201         debugFP = NULL;\r
6202       }\r
6203       break;\r
6204 \r
6205     case IDM_HELPCONTENTS:\r
6206       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6207           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6208           MessageBox (GetFocus(),\r
6209                     "Unable to activate help",\r
6210                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6211       }\r
6212       break;\r
6213 \r
6214     case IDM_HELPSEARCH:\r
6215         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6216             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6217         MessageBox (GetFocus(),\r
6218                     "Unable to activate help",\r
6219                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6220       }\r
6221       break;\r
6222 \r
6223     case IDM_HELPHELP:\r
6224       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6225         MessageBox (GetFocus(),\r
6226                     "Unable to activate help",\r
6227                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6228       }\r
6229       break;\r
6230 \r
6231     case IDM_ABOUT:\r
6232       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6233       DialogBox(hInst, \r
6234         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6235         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6236       FreeProcInstance(lpProc);\r
6237       break;\r
6238 \r
6239     case IDM_DirectCommand1:\r
6240       AskQuestionEvent("Direct Command",\r
6241                        "Send to chess program:", "", "1");\r
6242       break;\r
6243     case IDM_DirectCommand2:\r
6244       AskQuestionEvent("Direct Command",\r
6245                        "Send to second chess program:", "", "2");\r
6246       break;\r
6247 \r
6248     case EP_WhitePawn:\r
6249       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6250       fromX = fromY = -1;\r
6251       break;\r
6252 \r
6253     case EP_WhiteKnight:\r
6254       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6255       fromX = fromY = -1;\r
6256       break;\r
6257 \r
6258     case EP_WhiteBishop:\r
6259       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6260       fromX = fromY = -1;\r
6261       break;\r
6262 \r
6263     case EP_WhiteRook:\r
6264       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6265       fromX = fromY = -1;\r
6266       break;\r
6267 \r
6268     case EP_WhiteQueen:\r
6269       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6270       fromX = fromY = -1;\r
6271       break;\r
6272 \r
6273     case EP_WhiteFerz:\r
6274       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6275       fromX = fromY = -1;\r
6276       break;\r
6277 \r
6278     case EP_WhiteWazir:\r
6279       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6280       fromX = fromY = -1;\r
6281       break;\r
6282 \r
6283     case EP_WhiteAlfil:\r
6284       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6285       fromX = fromY = -1;\r
6286       break;\r
6287 \r
6288     case EP_WhiteCannon:\r
6289       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6290       fromX = fromY = -1;\r
6291       break;\r
6292 \r
6293     case EP_WhiteCardinal:\r
6294       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6295       fromX = fromY = -1;\r
6296       break;\r
6297 \r
6298     case EP_WhiteMarshall:\r
6299       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6300       fromX = fromY = -1;\r
6301       break;\r
6302 \r
6303     case EP_WhiteKing:\r
6304       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6305       fromX = fromY = -1;\r
6306       break;\r
6307 \r
6308     case EP_BlackPawn:\r
6309       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6310       fromX = fromY = -1;\r
6311       break;\r
6312 \r
6313     case EP_BlackKnight:\r
6314       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6315       fromX = fromY = -1;\r
6316       break;\r
6317 \r
6318     case EP_BlackBishop:\r
6319       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6320       fromX = fromY = -1;\r
6321       break;\r
6322 \r
6323     case EP_BlackRook:\r
6324       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6325       fromX = fromY = -1;\r
6326       break;\r
6327 \r
6328     case EP_BlackQueen:\r
6329       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6330       fromX = fromY = -1;\r
6331       break;\r
6332 \r
6333     case EP_BlackFerz:\r
6334       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6335       fromX = fromY = -1;\r
6336       break;\r
6337 \r
6338     case EP_BlackWazir:\r
6339       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6340       fromX = fromY = -1;\r
6341       break;\r
6342 \r
6343     case EP_BlackAlfil:\r
6344       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6345       fromX = fromY = -1;\r
6346       break;\r
6347 \r
6348     case EP_BlackCannon:\r
6349       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6350       fromX = fromY = -1;\r
6351       break;\r
6352 \r
6353     case EP_BlackCardinal:\r
6354       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6355       fromX = fromY = -1;\r
6356       break;\r
6357 \r
6358     case EP_BlackMarshall:\r
6359       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6360       fromX = fromY = -1;\r
6361       break;\r
6362 \r
6363     case EP_BlackKing:\r
6364       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6365       fromX = fromY = -1;\r
6366       break;\r
6367 \r
6368     case EP_EmptySquare:\r
6369       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6370       fromX = fromY = -1;\r
6371       break;\r
6372 \r
6373     case EP_ClearBoard:\r
6374       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6375       fromX = fromY = -1;\r
6376       break;\r
6377 \r
6378     case EP_White:\r
6379       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6380       fromX = fromY = -1;\r
6381       break;\r
6382 \r
6383     case EP_Black:\r
6384       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6385       fromX = fromY = -1;\r
6386       break;\r
6387 \r
6388     case EP_Promote:\r
6389       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6390       fromX = fromY = -1;\r
6391       break;\r
6392 \r
6393     case EP_Demote:\r
6394       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6395       fromX = fromY = -1;\r
6396       break;\r
6397 \r
6398     case DP_Pawn:\r
6399       DropMenuEvent(WhitePawn, fromX, fromY);\r
6400       fromX = fromY = -1;\r
6401       break;\r
6402 \r
6403     case DP_Knight:\r
6404       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6405       fromX = fromY = -1;\r
6406       break;\r
6407 \r
6408     case DP_Bishop:\r
6409       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6410       fromX = fromY = -1;\r
6411       break;\r
6412 \r
6413     case DP_Rook:\r
6414       DropMenuEvent(WhiteRook, fromX, fromY);\r
6415       fromX = fromY = -1;\r
6416       break;\r
6417 \r
6418     case DP_Queen:\r
6419       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6420       fromX = fromY = -1;\r
6421       break;\r
6422 \r
6423     default:\r
6424       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6425     }\r
6426     break;\r
6427 \r
6428   case WM_TIMER:\r
6429     switch (wParam) {\r
6430     case CLOCK_TIMER_ID:\r
6431       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6432       clockTimerEvent = 0;\r
6433       DecrementClocks(); /* call into back end */\r
6434       break;\r
6435     case LOAD_GAME_TIMER_ID:\r
6436       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6437       loadGameTimerEvent = 0;\r
6438       AutoPlayGameLoop(); /* call into back end */\r
6439       break;\r
6440     case ANALYSIS_TIMER_ID:\r
6441       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6442                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6443         AnalysisPeriodicEvent(0);\r
6444       } else {\r
6445         KillTimer(hwnd, analysisTimerEvent);\r
6446         analysisTimerEvent = 0;\r
6447       }\r
6448       break;\r
6449     case DELAYED_TIMER_ID:\r
6450       KillTimer(hwnd, delayedTimerEvent);\r
6451       delayedTimerEvent = 0;\r
6452       delayedTimerCallback();\r
6453       break;\r
6454     }\r
6455     break;\r
6456 \r
6457   case WM_USER_Input:\r
6458     InputEvent(hwnd, message, wParam, lParam);\r
6459     break;\r
6460 \r
6461   /* [AS] Also move "attached" child windows */\r
6462   case WM_WINDOWPOSCHANGING:\r
6463 \r
6464     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6465         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6466 \r
6467         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6468             /* Window is moving */\r
6469             RECT rcMain;\r
6470 \r
6471 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6472             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6473             rcMain.right  = boardX + winWidth;\r
6474             rcMain.top    = boardY;\r
6475             rcMain.bottom = boardY + winHeight;\r
6476             \r
6477             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6478             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6479             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6480             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6481             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6482             boardX = lpwp->x;\r
6483             boardY = lpwp->y;\r
6484         }\r
6485     }\r
6486     break;\r
6487 \r
6488   /* [AS] Snapping */\r
6489   case WM_ENTERSIZEMOVE:\r
6490     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6491     if (hwnd == hwndMain) {\r
6492       doingSizing = TRUE;\r
6493       lastSizing = 0;\r
6494     }\r
6495     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6496     break;\r
6497 \r
6498   case WM_SIZING:\r
6499     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6500     if (hwnd == hwndMain) {\r
6501       lastSizing = wParam;\r
6502     }\r
6503     break;\r
6504 \r
6505   case WM_MOVING:\r
6506     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6507       return OnMoving( &sd, hwnd, wParam, lParam );\r
6508 \r
6509   case WM_EXITSIZEMOVE:\r
6510     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6511     if (hwnd == hwndMain) {\r
6512       RECT client;\r
6513       doingSizing = FALSE;\r
6514       InvalidateRect(hwnd, &boardRect, FALSE);\r
6515       GetClientRect(hwnd, &client);\r
6516       ResizeBoard(client.right, client.bottom, lastSizing);\r
6517       lastSizing = 0;\r
6518       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6519     }\r
6520     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6521     break;\r
6522 \r
6523   case WM_DESTROY: /* message: window being destroyed */\r
6524     PostQuitMessage(0);\r
6525     break;\r
6526 \r
6527   case WM_CLOSE:\r
6528     if (hwnd == hwndMain) {\r
6529       ExitEvent(0);\r
6530     }\r
6531     break;\r
6532 \r
6533   default:      /* Passes it on if unprocessed */\r
6534     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6535   }\r
6536   return 0;\r
6537 }\r
6538 \r
6539 /*---------------------------------------------------------------------------*\\r
6540  *\r
6541  * Misc utility routines\r
6542  *\r
6543 \*---------------------------------------------------------------------------*/\r
6544 \r
6545 /*\r
6546  * Decent random number generator, at least not as bad as Windows\r
6547  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6548  */\r
6549 unsigned int randstate;\r
6550 \r
6551 int\r
6552 myrandom(void)\r
6553 {\r
6554   randstate = randstate * 1664525 + 1013904223;\r
6555   return (int) randstate & 0x7fffffff;\r
6556 }\r
6557 \r
6558 void\r
6559 mysrandom(unsigned int seed)\r
6560 {\r
6561   randstate = seed;\r
6562 }\r
6563 \r
6564 \r
6565 /* \r
6566  * returns TRUE if user selects a different color, FALSE otherwise \r
6567  */\r
6568 \r
6569 BOOL\r
6570 ChangeColor(HWND hwnd, COLORREF *which)\r
6571 {\r
6572   static BOOL firstTime = TRUE;\r
6573   static DWORD customColors[16];\r
6574   CHOOSECOLOR cc;\r
6575   COLORREF newcolor;\r
6576   int i;\r
6577   ColorClass ccl;\r
6578 \r
6579   if (firstTime) {\r
6580     /* Make initial colors in use available as custom colors */\r
6581     /* Should we put the compiled-in defaults here instead? */\r
6582     i = 0;\r
6583     customColors[i++] = lightSquareColor & 0xffffff;\r
6584     customColors[i++] = darkSquareColor & 0xffffff;\r
6585     customColors[i++] = whitePieceColor & 0xffffff;\r
6586     customColors[i++] = blackPieceColor & 0xffffff;\r
6587     customColors[i++] = highlightSquareColor & 0xffffff;\r
6588     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6589 \r
6590     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6591       customColors[i++] = textAttribs[ccl].color;\r
6592     }\r
6593     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6594     firstTime = FALSE;\r
6595   }\r
6596 \r
6597   cc.lStructSize = sizeof(cc);\r
6598   cc.hwndOwner = hwnd;\r
6599   cc.hInstance = NULL;\r
6600   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6601   cc.lpCustColors = (LPDWORD) customColors;\r
6602   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6603 \r
6604   if (!ChooseColor(&cc)) return FALSE;\r
6605 \r
6606   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6607   if (newcolor == *which) return FALSE;\r
6608   *which = newcolor;\r
6609   return TRUE;\r
6610 \r
6611   /*\r
6612   InitDrawingColors();\r
6613   InvalidateRect(hwnd, &boardRect, FALSE);\r
6614   */\r
6615 }\r
6616 \r
6617 BOOLEAN\r
6618 MyLoadSound(MySound *ms)\r
6619 {\r
6620   BOOL ok = FALSE;\r
6621   struct stat st;\r
6622   FILE *f;\r
6623 \r
6624   if (ms->data) free(ms->data);\r
6625   ms->data = NULL;\r
6626 \r
6627   switch (ms->name[0]) {\r
6628   case NULLCHAR:\r
6629     /* Silence */\r
6630     ok = TRUE;\r
6631     break;\r
6632   case '$':\r
6633     /* System sound from Control Panel.  Don't preload here. */\r
6634     ok = TRUE;\r
6635     break;\r
6636   case '!':\r
6637     if (ms->name[1] == NULLCHAR) {\r
6638       /* "!" alone = silence */\r
6639       ok = TRUE;\r
6640     } else {\r
6641       /* Builtin wave resource.  Error if not found. */\r
6642       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6643       if (h == NULL) break;\r
6644       ms->data = (void *)LoadResource(hInst, h);\r
6645       if (h == NULL) break;\r
6646       ok = TRUE;\r
6647     }\r
6648     break;\r
6649   default:\r
6650     /* .wav file.  Error if not found. */\r
6651     f = fopen(ms->name, "rb");\r
6652     if (f == NULL) break;\r
6653     if (fstat(fileno(f), &st) < 0) break;\r
6654     ms->data = malloc(st.st_size);\r
6655     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6656     fclose(f);\r
6657     ok = TRUE;\r
6658     break;\r
6659   }\r
6660   if (!ok) {\r
6661     char buf[MSG_SIZ];\r
6662     sprintf(buf, "Error loading sound %s", ms->name);\r
6663     DisplayError(buf, GetLastError());\r
6664   }\r
6665   return ok;\r
6666 }\r
6667 \r
6668 BOOLEAN\r
6669 MyPlaySound(MySound *ms)\r
6670 {\r
6671   BOOLEAN ok = FALSE;\r
6672 \r
6673   switch (ms->name[0]) {\r
6674   case NULLCHAR:\r
6675         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6676     /* Silence */\r
6677     ok = TRUE;\r
6678     break;\r
6679   case '$':\r
6680     /* System sound from Control Panel (deprecated feature).\r
6681        "$" alone or an unset sound name gets default beep (still in use). */\r
6682     if (ms->name[1]) {\r
6683       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6684     }\r
6685     if (!ok) ok = MessageBeep(MB_OK);\r
6686     break; \r
6687   case '!':\r
6688     /* Builtin wave resource, or "!" alone for silence */\r
6689     if (ms->name[1]) {\r
6690       if (ms->data == NULL) return FALSE;\r
6691       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6692     } else {\r
6693       ok = TRUE;\r
6694     }\r
6695     break;\r
6696   default:\r
6697     /* .wav file.  Error if not found. */\r
6698     if (ms->data == NULL) return FALSE;\r
6699     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6700     break;\r
6701   }\r
6702   /* Don't print an error: this can happen innocently if the sound driver\r
6703      is busy; for instance, if another instance of WinBoard is playing\r
6704      a sound at about the same time. */\r
6705 #if 0\r
6706   if (!ok) {\r
6707     char buf[MSG_SIZ];\r
6708     sprintf(buf, "Error playing sound %s", ms->name);\r
6709     DisplayError(buf, GetLastError());\r
6710   }\r
6711 #endif\r
6712   return ok;\r
6713 }\r
6714 \r
6715 \r
6716 LRESULT CALLBACK\r
6717 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6718 {\r
6719   BOOL ok;\r
6720   OPENFILENAME *ofn;\r
6721   static UINT *number; /* gross that this is static */\r
6722 \r
6723   switch (message) {\r
6724   case WM_INITDIALOG: /* message: initialize dialog box */\r
6725     /* Center the dialog over the application window */\r
6726     ofn = (OPENFILENAME *) lParam;\r
6727     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6728       number = (UINT *) ofn->lCustData;\r
6729       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6730     } else {\r
6731       number = NULL;\r
6732     }\r
6733     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6734     return FALSE;  /* Allow for further processing */\r
6735 \r
6736   case WM_COMMAND:\r
6737     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6738       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6739     }\r
6740     return FALSE;  /* Allow for further processing */\r
6741   }\r
6742   return FALSE;\r
6743 }\r
6744 \r
6745 UINT APIENTRY\r
6746 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6747 {\r
6748   static UINT *number;\r
6749   OPENFILENAME *ofname;\r
6750   OFNOTIFY *ofnot;\r
6751   switch (uiMsg) {\r
6752   case WM_INITDIALOG:\r
6753     ofname = (OPENFILENAME *)lParam;\r
6754     number = (UINT *)(ofname->lCustData);\r
6755     break;\r
6756   case WM_NOTIFY:\r
6757     ofnot = (OFNOTIFY *)lParam;\r
6758     if (ofnot->hdr.code == CDN_FILEOK) {\r
6759       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6760     }\r
6761     break;\r
6762   }\r
6763   return 0;\r
6764 }\r
6765 \r
6766 \r
6767 FILE *\r
6768 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6769                char *nameFilt, char *dlgTitle, UINT *number,\r
6770                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6771 {\r
6772   OPENFILENAME openFileName;\r
6773   char buf1[MSG_SIZ];\r
6774   FILE *f;\r
6775 \r
6776   if (fileName == NULL) fileName = buf1;\r
6777   if (defName == NULL) {\r
6778     strcpy(fileName, "*.");\r
6779     strcat(fileName, defExt);\r
6780   } else {\r
6781     strcpy(fileName, defName);\r
6782   }\r
6783   if (fileTitle) strcpy(fileTitle, "");\r
6784   if (number) *number = 0;\r
6785 \r
6786   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6787   openFileName.hwndOwner         = hwnd;\r
6788   openFileName.hInstance         = (HANDLE) hInst;\r
6789   openFileName.lpstrFilter       = nameFilt;\r
6790   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6791   openFileName.nMaxCustFilter    = 0L;\r
6792   openFileName.nFilterIndex      = 1L;\r
6793   openFileName.lpstrFile         = fileName;\r
6794   openFileName.nMaxFile          = MSG_SIZ;\r
6795   openFileName.lpstrFileTitle    = fileTitle;\r
6796   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6797   openFileName.lpstrInitialDir   = NULL;\r
6798   openFileName.lpstrTitle        = dlgTitle;\r
6799   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6800     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6801     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6802     | (oldDialog ? 0 : OFN_EXPLORER);\r
6803   openFileName.nFileOffset       = 0;\r
6804   openFileName.nFileExtension    = 0;\r
6805   openFileName.lpstrDefExt       = defExt;\r
6806   openFileName.lCustData         = (LONG) number;\r
6807   openFileName.lpfnHook          = oldDialog ?\r
6808     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6809   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6810 \r
6811   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6812                         GetOpenFileName(&openFileName)) {\r
6813     /* open the file */\r
6814     f = fopen(openFileName.lpstrFile, write);\r
6815     if (f == NULL) {\r
6816       MessageBox(hwnd, "File open failed", NULL,\r
6817                  MB_OK|MB_ICONEXCLAMATION);\r
6818       return NULL;\r
6819     }\r
6820   } else {\r
6821     int err = CommDlgExtendedError();\r
6822     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6823     return FALSE;\r
6824   }\r
6825   return f;\r
6826 }\r
6827 \r
6828 \r
6829 \r
6830 VOID APIENTRY\r
6831 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6832 {\r
6833   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6834 \r
6835   /*\r
6836    * Get the first pop-up menu in the menu template. This is the\r
6837    * menu that TrackPopupMenu displays.\r
6838    */\r
6839   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6840 \r
6841   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6842 \r
6843   /*\r
6844    * TrackPopup uses screen coordinates, so convert the\r
6845    * coordinates of the mouse click to screen coordinates.\r
6846    */\r
6847   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6848 \r
6849   /* Draw and track the floating pop-up menu. */\r
6850   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6851                  pt.x, pt.y, 0, hwnd, NULL);\r
6852 \r
6853   /* Destroy the menu.*/\r
6854   DestroyMenu(hmenu);\r
6855 }\r
6856    \r
6857 typedef struct {\r
6858   HWND hDlg, hText;\r
6859   int sizeX, sizeY, newSizeX, newSizeY;\r
6860   HDWP hdwp;\r
6861 } ResizeEditPlusButtonsClosure;\r
6862 \r
6863 BOOL CALLBACK\r
6864 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6865 {\r
6866   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6867   RECT rect;\r
6868   POINT pt;\r
6869 \r
6870   if (hChild == cl->hText) return TRUE;\r
6871   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6872   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6873   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6874   ScreenToClient(cl->hDlg, &pt);\r
6875   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6876     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6877   return TRUE;\r
6878 }\r
6879 \r
6880 /* Resize a dialog that has a (rich) edit field filling most of\r
6881    the top, with a row of buttons below */\r
6882 VOID\r
6883 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6884 {\r
6885   RECT rectText;\r
6886   int newTextHeight, newTextWidth;\r
6887   ResizeEditPlusButtonsClosure cl;\r
6888   \r
6889   /*if (IsIconic(hDlg)) return;*/\r
6890   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6891   \r
6892   cl.hdwp = BeginDeferWindowPos(8);\r
6893 \r
6894   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6895   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6896   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6897   if (newTextHeight < 0) {\r
6898     newSizeY += -newTextHeight;\r
6899     newTextHeight = 0;\r
6900   }\r
6901   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6902     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6903 \r
6904   cl.hDlg = hDlg;\r
6905   cl.hText = hText;\r
6906   cl.sizeX = sizeX;\r
6907   cl.sizeY = sizeY;\r
6908   cl.newSizeX = newSizeX;\r
6909   cl.newSizeY = newSizeY;\r
6910   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6911 \r
6912   EndDeferWindowPos(cl.hdwp);\r
6913 }\r
6914 \r
6915 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6916 {\r
6917     RECT    rChild, rParent;\r
6918     int     wChild, hChild, wParent, hParent;\r
6919     int     wScreen, hScreen, xNew, yNew;\r
6920     HDC     hdc;\r
6921 \r
6922     /* Get the Height and Width of the child window */\r
6923     GetWindowRect (hwndChild, &rChild);\r
6924     wChild = rChild.right - rChild.left;\r
6925     hChild = rChild.bottom - rChild.top;\r
6926 \r
6927     /* Get the Height and Width of the parent window */\r
6928     GetWindowRect (hwndParent, &rParent);\r
6929     wParent = rParent.right - rParent.left;\r
6930     hParent = rParent.bottom - rParent.top;\r
6931 \r
6932     /* Get the display limits */\r
6933     hdc = GetDC (hwndChild);\r
6934     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6935     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6936     ReleaseDC(hwndChild, hdc);\r
6937 \r
6938     /* Calculate new X position, then adjust for screen */\r
6939     xNew = rParent.left + ((wParent - wChild) /2);\r
6940     if (xNew < 0) {\r
6941         xNew = 0;\r
6942     } else if ((xNew+wChild) > wScreen) {\r
6943         xNew = wScreen - wChild;\r
6944     }\r
6945 \r
6946     /* Calculate new Y position, then adjust for screen */\r
6947     if( mode == 0 ) {\r
6948         yNew = rParent.top  + ((hParent - hChild) /2);\r
6949     }\r
6950     else {\r
6951         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6952     }\r
6953 \r
6954     if (yNew < 0) {\r
6955         yNew = 0;\r
6956     } else if ((yNew+hChild) > hScreen) {\r
6957         yNew = hScreen - hChild;\r
6958     }\r
6959 \r
6960     /* Set it, and return */\r
6961     return SetWindowPos (hwndChild, NULL,\r
6962                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6963 }\r
6964 \r
6965 /* Center one window over another */\r
6966 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6967 {\r
6968     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6969 }\r
6970 \r
6971 /*---------------------------------------------------------------------------*\\r
6972  *\r
6973  * Startup Dialog functions\r
6974  *\r
6975 \*---------------------------------------------------------------------------*/\r
6976 void\r
6977 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6978 {\r
6979   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6980 \r
6981   while (*cd != NULL) {\r
6982     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6983     cd++;\r
6984   }\r
6985 }\r
6986 \r
6987 void\r
6988 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6989 {\r
6990   char buf1[ARG_MAX];\r
6991   int len;\r
6992 \r
6993   if (str[0] == '@') {\r
6994     FILE* f = fopen(str + 1, "r");\r
6995     if (f == NULL) {\r
6996       DisplayFatalError(str + 1, errno, 2);\r
6997       return;\r
6998     }\r
6999     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
7000     fclose(f);\r
7001     buf1[len] = NULLCHAR;\r
7002     str = buf1;\r
7003   }\r
7004 \r
7005   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7006 \r
7007   for (;;) {\r
7008     char buf[MSG_SIZ];\r
7009     char *end = strchr(str, '\n');\r
7010     if (end == NULL) return;\r
7011     memcpy(buf, str, end - str);\r
7012     buf[end - str] = NULLCHAR;\r
7013     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
7014     str = end + 1;\r
7015   }\r
7016 }\r
7017 \r
7018 void\r
7019 SetStartupDialogEnables(HWND hDlg)\r
7020 {\r
7021   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
7022     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7023     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
7024   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7025     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
7026   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
7027     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
7028   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
7029     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
7030   EnableWindow(GetDlgItem(hDlg, IDOK),\r
7031     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7032     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
7033     IsDlgButtonChecked(hDlg, OPT_View));\r
7034 }\r
7035 \r
7036 char *\r
7037 QuoteForFilename(char *filename)\r
7038 {\r
7039   int dquote, space;\r
7040   dquote = strchr(filename, '"') != NULL;\r
7041   space = strchr(filename, ' ') != NULL;\r
7042   if (dquote || space) {\r
7043     if (dquote) {\r
7044       return "'";\r
7045     } else {\r
7046       return "\"";\r
7047     }\r
7048   } else {\r
7049     return "";\r
7050   }\r
7051 }\r
7052 \r
7053 VOID\r
7054 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
7055 {\r
7056   char buf[MSG_SIZ];\r
7057   char *q;\r
7058 \r
7059   InitComboStringsFromOption(hwndCombo, nthnames);\r
7060   q = QuoteForFilename(nthcp);\r
7061   sprintf(buf, "%s%s%s", q, nthcp, q);\r
7062   if (*nthdir != NULLCHAR) {\r
7063     q = QuoteForFilename(nthdir);\r
7064     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
7065   }\r
7066   if (*nthcp == NULLCHAR) {\r
7067     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7068   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7069     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7070     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7071   }\r
7072 }\r
7073 \r
7074 LRESULT CALLBACK\r
7075 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7076 {\r
7077   char buf[MSG_SIZ];\r
7078   HANDLE hwndCombo;\r
7079   char *p;\r
7080 \r
7081   switch (message) {\r
7082   case WM_INITDIALOG:\r
7083     /* Center the dialog */\r
7084     CenterWindow (hDlg, GetDesktopWindow());\r
7085     /* Initialize the dialog items */\r
7086     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7087                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7088                   firstChessProgramNames);\r
7089     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7090                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7091                   secondChessProgramNames);\r
7092     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7093     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7094     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7095     if (*appData.icsHelper != NULLCHAR) {\r
7096       char *q = QuoteForFilename(appData.icsHelper);\r
7097       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7098     }\r
7099     if (*appData.icsHost == NULLCHAR) {\r
7100       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7101       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7102     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7103       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7104       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7105     }\r
7106 \r
7107     if (appData.icsActive) {\r
7108       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7109     }\r
7110     else if (appData.noChessProgram) {\r
7111       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7112     }\r
7113     else {\r
7114       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7115     }\r
7116 \r
7117     SetStartupDialogEnables(hDlg);\r
7118     return TRUE;\r
7119 \r
7120   case WM_COMMAND:\r
7121     switch (LOWORD(wParam)) {\r
7122     case IDOK:\r
7123       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7124         strcpy(buf, "/fcp=");\r
7125         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7126         p = buf;\r
7127         ParseArgs(StringGet, &p);\r
7128         strcpy(buf, "/scp=");\r
7129         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7130         p = buf;\r
7131         ParseArgs(StringGet, &p);\r
7132         appData.noChessProgram = FALSE;\r
7133         appData.icsActive = FALSE;\r
7134       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7135         strcpy(buf, "/ics /icshost=");\r
7136         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7137         p = buf;\r
7138         ParseArgs(StringGet, &p);\r
7139         if (appData.zippyPlay) {\r
7140           strcpy(buf, "/fcp=");\r
7141           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7142           p = buf;\r
7143           ParseArgs(StringGet, &p);\r
7144         }\r
7145       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7146         appData.noChessProgram = TRUE;\r
7147         appData.icsActive = FALSE;\r
7148       } else {\r
7149         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7150                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7151         return TRUE;\r
7152       }\r
7153       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7154         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7155         p = buf;\r
7156         ParseArgs(StringGet, &p);\r
7157       }\r
7158       EndDialog(hDlg, TRUE);\r
7159       return TRUE;\r
7160 \r
7161     case IDCANCEL:\r
7162       ExitEvent(0);\r
7163       return TRUE;\r
7164 \r
7165     case IDM_HELPCONTENTS:\r
7166       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7167         MessageBox (GetFocus(),\r
7168                     "Unable to activate help",\r
7169                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7170       }\r
7171       break;\r
7172 \r
7173     default:\r
7174       SetStartupDialogEnables(hDlg);\r
7175       break;\r
7176     }\r
7177     break;\r
7178   }\r
7179   return FALSE;\r
7180 }\r
7181 \r
7182 /*---------------------------------------------------------------------------*\\r
7183  *\r
7184  * About box dialog functions\r
7185  *\r
7186 \*---------------------------------------------------------------------------*/\r
7187 \r
7188 /* Process messages for "About" dialog box */\r
7189 LRESULT CALLBACK\r
7190 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7191 {\r
7192   switch (message) {\r
7193   case WM_INITDIALOG: /* message: initialize dialog box */\r
7194     /* Center the dialog over the application window */\r
7195     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7196     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7197     JAWS_COPYRIGHT\r
7198     return (TRUE);\r
7199 \r
7200   case WM_COMMAND: /* message: received a command */\r
7201     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7202         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7203       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7204       return (TRUE);\r
7205     }\r
7206     break;\r
7207   }\r
7208   return (FALSE);\r
7209 }\r
7210 \r
7211 /*---------------------------------------------------------------------------*\\r
7212  *\r
7213  * Comment Dialog functions\r
7214  *\r
7215 \*---------------------------------------------------------------------------*/\r
7216 \r
7217 LRESULT CALLBACK\r
7218 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7219 {\r
7220   static HANDLE hwndText = NULL;\r
7221   int len, newSizeX, newSizeY, flags;\r
7222   static int sizeX, sizeY;\r
7223   char *str;\r
7224   RECT rect;\r
7225   MINMAXINFO *mmi;\r
7226 \r
7227   switch (message) {\r
7228   case WM_INITDIALOG: /* message: initialize dialog box */\r
7229     /* Initialize the dialog items */\r
7230     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7231     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7232     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7233     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7234     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7235     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7236     SetWindowText(hDlg, commentTitle);\r
7237     if (editComment) {\r
7238       SetFocus(hwndText);\r
7239     } else {\r
7240       SetFocus(GetDlgItem(hDlg, IDOK));\r
7241     }\r
7242     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7243                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7244                 MAKELPARAM(FALSE, 0));\r
7245     /* Size and position the dialog */\r
7246     if (!commentDialog) {\r
7247       commentDialog = hDlg;\r
7248       flags = SWP_NOZORDER;\r
7249       GetClientRect(hDlg, &rect);\r
7250       sizeX = rect.right;\r
7251       sizeY = rect.bottom;\r
7252       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7253           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7254         WINDOWPLACEMENT wp;\r
7255         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7256         wp.length = sizeof(WINDOWPLACEMENT);\r
7257         wp.flags = 0;\r
7258         wp.showCmd = SW_SHOW;\r
7259         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7260         wp.rcNormalPosition.left = commentX;\r
7261         wp.rcNormalPosition.right = commentX + commentW;\r
7262         wp.rcNormalPosition.top = commentY;\r
7263         wp.rcNormalPosition.bottom = commentY + commentH;\r
7264         SetWindowPlacement(hDlg, &wp);\r
7265 \r
7266         GetClientRect(hDlg, &rect);\r
7267         newSizeX = rect.right;\r
7268         newSizeY = rect.bottom;\r
7269         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7270                               newSizeX, newSizeY);\r
7271         sizeX = newSizeX;\r
7272         sizeY = newSizeY;\r
7273       }\r
7274     }\r
7275     return FALSE;\r
7276 \r
7277   case WM_COMMAND: /* message: received a command */\r
7278     switch (LOWORD(wParam)) {\r
7279     case IDOK:\r
7280       if (editComment) {\r
7281         char *p, *q;\r
7282         /* Read changed options from the dialog box */\r
7283         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7284         len = GetWindowTextLength(hwndText);\r
7285         str = (char *) malloc(len + 1);\r
7286         GetWindowText(hwndText, str, len + 1);\r
7287         p = q = str;\r
7288         while (*q) {\r
7289           if (*q == '\r')\r
7290             q++;\r
7291           else\r
7292             *p++ = *q++;\r
7293         }\r
7294         *p = NULLCHAR;\r
7295         ReplaceComment(commentIndex, str);\r
7296         free(str);\r
7297       }\r
7298       CommentPopDown();\r
7299       return TRUE;\r
7300 \r
7301     case IDCANCEL:\r
7302     case OPT_CancelComment:\r
7303       CommentPopDown();\r
7304       return TRUE;\r
7305 \r
7306     case OPT_ClearComment:\r
7307       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7308       break;\r
7309 \r
7310     case OPT_EditComment:\r
7311       EditCommentEvent();\r
7312       return TRUE;\r
7313 \r
7314     default:\r
7315       break;\r
7316     }\r
7317     break;\r
7318 \r
7319   case WM_SIZE:\r
7320     newSizeX = LOWORD(lParam);\r
7321     newSizeY = HIWORD(lParam);\r
7322     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7323     sizeX = newSizeX;\r
7324     sizeY = newSizeY;\r
7325     break;\r
7326 \r
7327   case WM_GETMINMAXINFO:\r
7328     /* Prevent resizing window too small */\r
7329     mmi = (MINMAXINFO *) lParam;\r
7330     mmi->ptMinTrackSize.x = 100;\r
7331     mmi->ptMinTrackSize.y = 100;\r
7332     break;\r
7333   }\r
7334   return FALSE;\r
7335 }\r
7336 \r
7337 VOID\r
7338 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7339 {\r
7340   FARPROC lpProc;\r
7341   char *p, *q;\r
7342 \r
7343   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7344 \r
7345   if (str == NULL) str = "";\r
7346   p = (char *) malloc(2 * strlen(str) + 2);\r
7347   q = p;\r
7348   while (*str) {\r
7349     if (*str == '\n') *q++ = '\r';\r
7350     *q++ = *str++;\r
7351   }\r
7352   *q = NULLCHAR;\r
7353   if (commentText != NULL) free(commentText);\r
7354 \r
7355   commentIndex = index;\r
7356   commentTitle = title;\r
7357   commentText = p;\r
7358   editComment = edit;\r
7359 \r
7360   if (commentDialog) {\r
7361     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7362     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7363   } else {\r
7364     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7365     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7366                  hwndMain, (DLGPROC)lpProc);\r
7367     FreeProcInstance(lpProc);\r
7368   }\r
7369   commentDialogUp = TRUE;\r
7370 }\r
7371 \r
7372 \r
7373 /*---------------------------------------------------------------------------*\\r
7374  *\r
7375  * Type-in move dialog functions\r
7376  * \r
7377 \*---------------------------------------------------------------------------*/\r
7378 \r
7379 LRESULT CALLBACK\r
7380 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7381 {\r
7382   char move[MSG_SIZ];\r
7383   HWND hInput;\r
7384   ChessMove moveType;\r
7385   int fromX, fromY, toX, toY;\r
7386   char promoChar;\r
7387 \r
7388   switch (message) {\r
7389   case WM_INITDIALOG:\r
7390     move[0] = (char) lParam;\r
7391     move[1] = NULLCHAR;\r
7392     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7393     hInput = GetDlgItem(hDlg, OPT_Move);\r
7394     SetWindowText(hInput, move);\r
7395     SetFocus(hInput);\r
7396     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7397     return FALSE;\r
7398 \r
7399   case WM_COMMAND:\r
7400     switch (LOWORD(wParam)) {\r
7401     case IDOK:\r
7402       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7403       { int n; Board board;\r
7404         // [HGM] FENedit\r
7405         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7406                 EditPositionPasteFEN(move);\r
7407                 EndDialog(hDlg, TRUE);\r
7408                 return TRUE;\r
7409         }\r
7410         // [HGM] movenum: allow move number to be typed in any mode\r
7411         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7412           currentMove = 2*n-1;\r
7413           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7414           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7415           EndDialog(hDlg, TRUE);\r
7416           DrawPosition(TRUE, boards[currentMove]);\r
7417           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7418           else DisplayMessage("", "");\r
7419           return TRUE;\r
7420         }\r
7421       }\r
7422       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7423         gameMode != Training) {\r
7424         DisplayMoveError("Displayed move is not current");\r
7425       } else {\r
7426 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7427         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7428           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7429         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7430         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7431           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7432           if (gameMode != Training)\r
7433               forwardMostMove = currentMove;\r
7434           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7435         } else {\r
7436           DisplayMoveError("Could not parse move");\r
7437         }\r
7438       }\r
7439       EndDialog(hDlg, TRUE);\r
7440       return TRUE;\r
7441     case IDCANCEL:\r
7442       EndDialog(hDlg, FALSE);\r
7443       return TRUE;\r
7444     default:\r
7445       break;\r
7446     }\r
7447     break;\r
7448   }\r
7449   return FALSE;\r
7450 }\r
7451 \r
7452 VOID\r
7453 PopUpMoveDialog(char firstchar)\r
7454 {\r
7455     FARPROC lpProc;\r
7456     \r
7457     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7458         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7459         gameMode == AnalyzeMode || gameMode == EditGame || \r
7460         gameMode == EditPosition || gameMode == IcsExamining ||\r
7461         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7462         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7463                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7464                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7465         gameMode == Training) {\r
7466       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7467       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7468         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7469       FreeProcInstance(lpProc);\r
7470     }\r
7471 }\r
7472 \r
7473 /*---------------------------------------------------------------------------*\\r
7474  *\r
7475  * Type-in name dialog functions\r
7476  * \r
7477 \*---------------------------------------------------------------------------*/\r
7478 \r
7479 LRESULT CALLBACK\r
7480 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7481 {\r
7482   char move[MSG_SIZ];\r
7483   HWND hInput;\r
7484 \r
7485   switch (message) {\r
7486   case WM_INITDIALOG:\r
7487     move[0] = (char) lParam;\r
7488     move[1] = NULLCHAR;\r
7489     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7490     hInput = GetDlgItem(hDlg, OPT_Name);\r
7491     SetWindowText(hInput, move);\r
7492     SetFocus(hInput);\r
7493     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7494     return FALSE;\r
7495 \r
7496   case WM_COMMAND:\r
7497     switch (LOWORD(wParam)) {\r
7498     case IDOK:\r
7499       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7500       appData.userName = strdup(move);\r
7501       SetUserLogo();\r
7502 \r
7503       EndDialog(hDlg, TRUE);\r
7504       return TRUE;\r
7505     case IDCANCEL:\r
7506       EndDialog(hDlg, FALSE);\r
7507       return TRUE;\r
7508     default:\r
7509       break;\r
7510     }\r
7511     break;\r
7512   }\r
7513   return FALSE;\r
7514 }\r
7515 \r
7516 VOID\r
7517 PopUpNameDialog(char firstchar)\r
7518 {\r
7519     FARPROC lpProc;\r
7520     \r
7521       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7522       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7523         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7524       FreeProcInstance(lpProc);\r
7525 }\r
7526 \r
7527 /*---------------------------------------------------------------------------*\\r
7528  *\r
7529  *  Error dialogs\r
7530  * \r
7531 \*---------------------------------------------------------------------------*/\r
7532 \r
7533 /* Nonmodal error box */\r
7534 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7535                              WPARAM wParam, LPARAM lParam);\r
7536 \r
7537 VOID\r
7538 ErrorPopUp(char *title, char *content)\r
7539 {\r
7540   FARPROC lpProc;\r
7541   char *p, *q;\r
7542   BOOLEAN modal = hwndMain == NULL;\r
7543 \r
7544   p = content;\r
7545   q = errorMessage;\r
7546   while (*p) {\r
7547     if (*p == '\n') {\r
7548       if (modal) {\r
7549         *q++ = ' ';\r
7550         p++;\r
7551       } else {\r
7552         *q++ = '\r';\r
7553         *q++ = *p++;\r
7554       }\r
7555     } else {\r
7556       *q++ = *p++;\r
7557     }\r
7558   }\r
7559   *q = NULLCHAR;\r
7560   strncpy(errorTitle, title, sizeof(errorTitle));\r
7561   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7562   \r
7563   if (modal) {\r
7564     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7565   } else {\r
7566     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7567     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7568                  hwndMain, (DLGPROC)lpProc);\r
7569     FreeProcInstance(lpProc);\r
7570   }\r
7571 }\r
7572 \r
7573 VOID\r
7574 ErrorPopDown()\r
7575 {\r
7576   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7577   if (errorDialog == NULL) return;\r
7578   DestroyWindow(errorDialog);\r
7579   errorDialog = NULL;\r
7580 }\r
7581 \r
7582 LRESULT CALLBACK\r
7583 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7584 {\r
7585   HANDLE hwndText;\r
7586   RECT rChild;\r
7587 \r
7588   switch (message) {\r
7589   case WM_INITDIALOG:\r
7590     GetWindowRect(hDlg, &rChild);\r
7591 \r
7592     /*\r
7593     SetWindowPos(hDlg, NULL, rChild.left,\r
7594       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7595       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7596     */\r
7597 \r
7598     /* \r
7599         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7600         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7601         and it doesn't work when you resize the dialog.\r
7602         For now, just give it a default position.\r
7603     */\r
7604     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7605 \r
7606     errorDialog = hDlg;\r
7607     SetWindowText(hDlg, errorTitle);\r
7608     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7609     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7610     return FALSE;\r
7611 \r
7612   case WM_COMMAND:\r
7613     switch (LOWORD(wParam)) {\r
7614     case IDOK:\r
7615     case IDCANCEL:\r
7616       if (errorDialog == hDlg) errorDialog = NULL;\r
7617       DestroyWindow(hDlg);\r
7618       return TRUE;\r
7619 \r
7620     default:\r
7621       break;\r
7622     }\r
7623     break;\r
7624   }\r
7625   return FALSE;\r
7626 }\r
7627 \r
7628 #ifdef GOTHIC\r
7629 HWND gothicDialog = NULL;\r
7630 \r
7631 LRESULT CALLBACK\r
7632 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7633 {\r
7634   HANDLE hwndText;\r
7635   RECT rChild;\r
7636   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7637 \r
7638   switch (message) {\r
7639   case WM_INITDIALOG:\r
7640     GetWindowRect(hDlg, &rChild);\r
7641 \r
7642     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7643                                                              SWP_NOZORDER);\r
7644 \r
7645     /* \r
7646         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7647         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7648         and it doesn't work when you resize the dialog.\r
7649         For now, just give it a default position.\r
7650     */\r
7651     gothicDialog = hDlg;\r
7652     SetWindowText(hDlg, errorTitle);\r
7653     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7654     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7655     return FALSE;\r
7656 \r
7657   case WM_COMMAND:\r
7658     switch (LOWORD(wParam)) {\r
7659     case IDOK:\r
7660     case IDCANCEL:\r
7661       if (errorDialog == hDlg) errorDialog = NULL;\r
7662       DestroyWindow(hDlg);\r
7663       return TRUE;\r
7664 \r
7665     default:\r
7666       break;\r
7667     }\r
7668     break;\r
7669   }\r
7670   return FALSE;\r
7671 }\r
7672 \r
7673 VOID\r
7674 GothicPopUp(char *title, VariantClass variant)\r
7675 {\r
7676   FARPROC lpProc;\r
7677   static char *lastTitle;\r
7678 \r
7679   strncpy(errorTitle, title, sizeof(errorTitle));\r
7680   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7681 \r
7682   if(lastTitle != title && gothicDialog != NULL) {\r
7683     DestroyWindow(gothicDialog);\r
7684     gothicDialog = NULL;\r
7685   }\r
7686   if(variant != VariantNormal && gothicDialog == NULL) {\r
7687     title = lastTitle;\r
7688     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7689     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7690                  hwndMain, (DLGPROC)lpProc);\r
7691     FreeProcInstance(lpProc);\r
7692   }\r
7693 }\r
7694 #endif\r
7695 \r
7696 /*---------------------------------------------------------------------------*\\r
7697  *\r
7698  *  Ics Interaction console functions\r
7699  *\r
7700 \*---------------------------------------------------------------------------*/\r
7701 \r
7702 #define HISTORY_SIZE 64\r
7703 static char *history[HISTORY_SIZE];\r
7704 int histIn = 0, histP = 0;\r
7705 \r
7706 VOID\r
7707 SaveInHistory(char *cmd)\r
7708 {\r
7709   if (history[histIn] != NULL) {\r
7710     free(history[histIn]);\r
7711     history[histIn] = NULL;\r
7712   }\r
7713   if (*cmd == NULLCHAR) return;\r
7714   history[histIn] = StrSave(cmd);\r
7715   histIn = (histIn + 1) % HISTORY_SIZE;\r
7716   if (history[histIn] != NULL) {\r
7717     free(history[histIn]);\r
7718     history[histIn] = NULL;\r
7719   }\r
7720   histP = histIn;\r
7721 }\r
7722 \r
7723 char *\r
7724 PrevInHistory(char *cmd)\r
7725 {\r
7726   int newhp;\r
7727   if (histP == histIn) {\r
7728     if (history[histIn] != NULL) free(history[histIn]);\r
7729     history[histIn] = StrSave(cmd);\r
7730   }\r
7731   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7732   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7733   histP = newhp;\r
7734   return history[histP];\r
7735 }\r
7736 \r
7737 char *\r
7738 NextInHistory()\r
7739 {\r
7740   if (histP == histIn) return NULL;\r
7741   histP = (histP + 1) % HISTORY_SIZE;\r
7742   return history[histP];\r
7743 }\r
7744 \r
7745 typedef struct {\r
7746   char *item;\r
7747   char *command;\r
7748   BOOLEAN getname;\r
7749   BOOLEAN immediate;\r
7750 } IcsTextMenuEntry;\r
7751 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7752 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7753 \r
7754 void\r
7755 ParseIcsTextMenu(char *icsTextMenuString)\r
7756 {\r
7757 //  int flags = 0;\r
7758   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7759   char *p = icsTextMenuString;\r
7760   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7761     free(e->item);\r
7762     e->item = NULL;\r
7763     if (e->command != NULL) {\r
7764       free(e->command);\r
7765       e->command = NULL;\r
7766     }\r
7767     e++;\r
7768   }\r
7769   e = icsTextMenuEntry;\r
7770   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7771     if (*p == ';' || *p == '\n') {\r
7772       e->item = strdup("-");\r
7773       e->command = NULL;\r
7774       p++;\r
7775     } else if (*p == '-') {\r
7776       e->item = strdup("-");\r
7777       e->command = NULL;\r
7778       p++;\r
7779       if (*p) p++;\r
7780     } else {\r
7781       char *q, *r, *s, *t;\r
7782       char c;\r
7783       q = strchr(p, ',');\r
7784       if (q == NULL) break;\r
7785       *q = NULLCHAR;\r
7786       r = strchr(q + 1, ',');\r
7787       if (r == NULL) break;\r
7788       *r = NULLCHAR;\r
7789       s = strchr(r + 1, ',');\r
7790       if (s == NULL) break;\r
7791       *s = NULLCHAR;\r
7792       c = ';';\r
7793       t = strchr(s + 1, c);\r
7794       if (t == NULL) {\r
7795         c = '\n';\r
7796         t = strchr(s + 1, c);\r
7797       }\r
7798       if (t != NULL) *t = NULLCHAR;\r
7799       e->item = strdup(p);\r
7800       e->command = strdup(q + 1);\r
7801       e->getname = *(r + 1) != '0';\r
7802       e->immediate = *(s + 1) != '0';\r
7803       *q = ',';\r
7804       *r = ',';\r
7805       *s = ',';\r
7806       if (t == NULL) break;\r
7807       *t = c;\r
7808       p = t + 1;\r
7809     }\r
7810     e++;\r
7811   } \r
7812 }\r
7813 \r
7814 HMENU\r
7815 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7816 {\r
7817   HMENU hmenu, h;\r
7818   int i = 0;\r
7819   hmenu = LoadMenu(hInst, "TextMenu");\r
7820   h = GetSubMenu(hmenu, 0);\r
7821   while (e->item) {\r
7822     if (strcmp(e->item, "-") == 0) {\r
7823       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7824     } else {\r
7825       if (e->item[0] == '|') {\r
7826         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7827                    IDM_CommandX + i, &e->item[1]);\r
7828       } else {\r
7829         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7830       }\r
7831     }\r
7832     e++;\r
7833     i++;\r
7834   } \r
7835   return hmenu;\r
7836 }\r
7837 \r
7838 WNDPROC consoleTextWindowProc;\r
7839 \r
7840 void\r
7841 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7842 {\r
7843   char buf[MSG_SIZ], name[MSG_SIZ];\r
7844   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7845   CHARRANGE sel;\r
7846 \r
7847   if (!getname) {\r
7848     SetWindowText(hInput, command);\r
7849     if (immediate) {\r
7850       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7851     } else {\r
7852       sel.cpMin = 999999;\r
7853       sel.cpMax = 999999;\r
7854       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7855       SetFocus(hInput);\r
7856     }\r
7857     return;\r
7858   }    \r
7859   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7860   if (sel.cpMin == sel.cpMax) {\r
7861     /* Expand to surrounding word */\r
7862     TEXTRANGE tr;\r
7863     do {\r
7864       tr.chrg.cpMax = sel.cpMin;\r
7865       tr.chrg.cpMin = --sel.cpMin;\r
7866       if (sel.cpMin < 0) break;\r
7867       tr.lpstrText = name;\r
7868       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7869     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7870     sel.cpMin++;\r
7871 \r
7872     do {\r
7873       tr.chrg.cpMin = sel.cpMax;\r
7874       tr.chrg.cpMax = ++sel.cpMax;\r
7875       tr.lpstrText = name;\r
7876       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7877     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7878     sel.cpMax--;\r
7879 \r
7880     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7881       MessageBeep(MB_ICONEXCLAMATION);\r
7882       return;\r
7883     }\r
7884     tr.chrg = sel;\r
7885     tr.lpstrText = name;\r
7886     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7887   } else {\r
7888     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7889       MessageBeep(MB_ICONEXCLAMATION);\r
7890       return;\r
7891     }\r
7892     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7893   }\r
7894   if (immediate) {\r
7895     sprintf(buf, "%s %s", command, name);\r
7896     SetWindowText(hInput, buf);\r
7897     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7898   } else {\r
7899     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7900     SetWindowText(hInput, buf);\r
7901     sel.cpMin = 999999;\r
7902     sel.cpMax = 999999;\r
7903     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7904     SetFocus(hInput);\r
7905   }\r
7906 }\r
7907 \r
7908 LRESULT CALLBACK \r
7909 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7910 {\r
7911   HWND hInput;\r
7912   CHARRANGE sel;\r
7913 \r
7914   switch (message) {\r
7915   case WM_KEYDOWN:\r
7916     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7917     switch (wParam) {\r
7918     case VK_PRIOR:\r
7919       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7920       return 0;\r
7921     case VK_NEXT:\r
7922       sel.cpMin = 999999;\r
7923       sel.cpMax = 999999;\r
7924       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7925       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7926       return 0;\r
7927     }\r
7928     break;\r
7929   case WM_CHAR:\r
7930    if(wParam != '\022') {\r
7931     if (wParam == '\t') {\r
7932       if (GetKeyState(VK_SHIFT) < 0) {\r
7933         /* shifted */\r
7934         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7935         if (buttonDesc[0].hwnd) {\r
7936           SetFocus(buttonDesc[0].hwnd);\r
7937         } else {\r
7938           SetFocus(hwndMain);\r
7939         }\r
7940       } else {\r
7941         /* unshifted */\r
7942         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7943       }\r
7944     } else {\r
7945       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7946       JAWS_DELETE( SetFocus(hInput); )\r
7947       SendMessage(hInput, message, wParam, lParam);\r
7948     }\r
7949     return 0;\r
7950    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7951   case WM_RBUTTONUP:\r
7952     if (GetKeyState(VK_SHIFT) & ~1) {\r
7953       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7954         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7955     } else {\r
7956       POINT pt;\r
7957       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7958       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7959       if (sel.cpMin == sel.cpMax) {\r
7960         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7961         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7962       }\r
7963       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7964         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7965       }\r
7966       pt.x = LOWORD(lParam);\r
7967       pt.y = HIWORD(lParam);\r
7968       MenuPopup(hwnd, pt, hmenu, -1);\r
7969     }\r
7970     return 0;\r
7971   case WM_PASTE:\r
7972     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7973     SetFocus(hInput);\r
7974     return SendMessage(hInput, message, wParam, lParam);\r
7975   case WM_MBUTTONDOWN:\r
7976     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7977   case WM_RBUTTONDOWN:\r
7978     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7979       /* Move selection here if it was empty */\r
7980       POINT pt;\r
7981       pt.x = LOWORD(lParam);\r
7982       pt.y = HIWORD(lParam);\r
7983       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7984       if (sel.cpMin == sel.cpMax) {\r
7985         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7986         sel.cpMax = sel.cpMin;\r
7987         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7988       }\r
7989       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7990     }\r
7991     return 0;\r
7992   case WM_COMMAND:\r
7993     switch (LOWORD(wParam)) {\r
7994     case IDM_QuickPaste:\r
7995       {\r
7996         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7997         if (sel.cpMin == sel.cpMax) {\r
7998           MessageBeep(MB_ICONEXCLAMATION);\r
7999           return 0;\r
8000         }\r
8001         SendMessage(hwnd, WM_COPY, 0, 0);\r
8002         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8003         SendMessage(hInput, WM_PASTE, 0, 0);\r
8004         SetFocus(hInput);\r
8005         return 0;\r
8006       }\r
8007     case IDM_Cut:\r
8008       SendMessage(hwnd, WM_CUT, 0, 0);\r
8009       return 0;\r
8010     case IDM_Paste:\r
8011       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8012       return 0;\r
8013     case IDM_Copy:\r
8014       SendMessage(hwnd, WM_COPY, 0, 0);\r
8015       return 0;\r
8016     default:\r
8017       {\r
8018         int i = LOWORD(wParam) - IDM_CommandX;\r
8019         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
8020             icsTextMenuEntry[i].command != NULL) {\r
8021           CommandX(hwnd, icsTextMenuEntry[i].command,\r
8022                    icsTextMenuEntry[i].getname,\r
8023                    icsTextMenuEntry[i].immediate);\r
8024           return 0;\r
8025         }\r
8026       }\r
8027       break;\r
8028     }\r
8029     break;\r
8030   }\r
8031   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
8032 }\r
8033 \r
8034 WNDPROC consoleInputWindowProc;\r
8035 \r
8036 LRESULT CALLBACK\r
8037 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8038 {\r
8039   char buf[MSG_SIZ];\r
8040   char *p;\r
8041   static BOOL sendNextChar = FALSE;\r
8042   static BOOL quoteNextChar = FALSE;\r
8043   InputSource *is = consoleInputSource;\r
8044   CHARFORMAT cf;\r
8045   CHARRANGE sel;\r
8046 \r
8047   switch (message) {\r
8048   case WM_CHAR:\r
8049     if (!appData.localLineEditing || sendNextChar) {\r
8050       is->buf[0] = (CHAR) wParam;\r
8051       is->count = 1;\r
8052       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8053       sendNextChar = FALSE;\r
8054       return 0;\r
8055     }\r
8056     if (quoteNextChar) {\r
8057       buf[0] = (char) wParam;\r
8058       buf[1] = NULLCHAR;\r
8059       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
8060       quoteNextChar = FALSE;\r
8061       return 0;\r
8062     }\r
8063     switch (wParam) {\r
8064     case '\r':   /* Enter key */\r
8065       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
8066       if (consoleEcho) SaveInHistory(is->buf);\r
8067       is->buf[is->count++] = '\n';\r
8068       is->buf[is->count] = NULLCHAR;\r
8069       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8070       if (consoleEcho) {\r
8071         ConsoleOutput(is->buf, is->count, TRUE);\r
8072       } else if (appData.localLineEditing) {\r
8073         ConsoleOutput("\n", 1, TRUE);\r
8074       }\r
8075       /* fall thru */\r
8076     case '\033': /* Escape key */\r
8077       SetWindowText(hwnd, "");\r
8078       cf.cbSize = sizeof(CHARFORMAT);\r
8079       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8080       if (consoleEcho) {\r
8081         cf.crTextColor = textAttribs[ColorNormal].color;\r
8082       } else {\r
8083         cf.crTextColor = COLOR_ECHOOFF;\r
8084       }\r
8085       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8086       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8087       return 0;\r
8088     case '\t':   /* Tab key */\r
8089       if (GetKeyState(VK_SHIFT) < 0) {\r
8090         /* shifted */\r
8091         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8092       } else {\r
8093         /* unshifted */\r
8094         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8095         if (buttonDesc[0].hwnd) {\r
8096           SetFocus(buttonDesc[0].hwnd);\r
8097         } else {\r
8098           SetFocus(hwndMain);\r
8099         }\r
8100       }\r
8101       return 0;\r
8102     case '\023': /* Ctrl+S */\r
8103       sendNextChar = TRUE;\r
8104       return 0;\r
8105     case '\021': /* Ctrl+Q */\r
8106       quoteNextChar = TRUE;\r
8107       return 0;\r
8108     JAWS_REPLAY\r
8109     default:\r
8110       break;\r
8111     }\r
8112     break;\r
8113   case WM_KEYDOWN:\r
8114     switch (wParam) {\r
8115     case VK_UP:\r
8116       GetWindowText(hwnd, buf, MSG_SIZ);\r
8117       p = PrevInHistory(buf);\r
8118       if (p != NULL) {\r
8119         SetWindowText(hwnd, p);\r
8120         sel.cpMin = 999999;\r
8121         sel.cpMax = 999999;\r
8122         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8123         return 0;\r
8124       }\r
8125       break;\r
8126     case VK_DOWN:\r
8127       p = NextInHistory();\r
8128       if (p != NULL) {\r
8129         SetWindowText(hwnd, p);\r
8130         sel.cpMin = 999999;\r
8131         sel.cpMax = 999999;\r
8132         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8133         return 0;\r
8134       }\r
8135       break;\r
8136     case VK_HOME:\r
8137     case VK_END:\r
8138       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8139       /* fall thru */\r
8140     case VK_PRIOR:\r
8141     case VK_NEXT:\r
8142       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8143       return 0;\r
8144     }\r
8145     break;\r
8146   case WM_MBUTTONDOWN:\r
8147     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8148       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8149     break;\r
8150   case WM_RBUTTONUP:\r
8151     if (GetKeyState(VK_SHIFT) & ~1) {\r
8152       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8153         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8154     } else {\r
8155       POINT pt;\r
8156       HMENU hmenu;\r
8157       hmenu = LoadMenu(hInst, "InputMenu");\r
8158       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8159       if (sel.cpMin == sel.cpMax) {\r
8160         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8161         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8162       }\r
8163       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8164         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8165       }\r
8166       pt.x = LOWORD(lParam);\r
8167       pt.y = HIWORD(lParam);\r
8168       MenuPopup(hwnd, pt, hmenu, -1);\r
8169     }\r
8170     return 0;\r
8171   case WM_COMMAND:\r
8172     switch (LOWORD(wParam)) { \r
8173     case IDM_Undo:\r
8174       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8175       return 0;\r
8176     case IDM_SelectAll:\r
8177       sel.cpMin = 0;\r
8178       sel.cpMax = -1; /*999999?*/\r
8179       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8180       return 0;\r
8181     case IDM_Cut:\r
8182       SendMessage(hwnd, WM_CUT, 0, 0);\r
8183       return 0;\r
8184     case IDM_Paste:\r
8185       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8186       return 0;\r
8187     case IDM_Copy:\r
8188       SendMessage(hwnd, WM_COPY, 0, 0);\r
8189       return 0;\r
8190     }\r
8191     break;\r
8192   }\r
8193   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8194 }\r
8195 \r
8196 #define CO_MAX  100000\r
8197 #define CO_TRIM   1000\r
8198 \r
8199 LRESULT CALLBACK\r
8200 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8201 {\r
8202   static SnapData sd;\r
8203   static HWND hText, hInput /*, hFocus*/;\r
8204 //  InputSource *is = consoleInputSource;\r
8205   RECT rect;\r
8206   static int sizeX, sizeY;\r
8207   int newSizeX, newSizeY;\r
8208   MINMAXINFO *mmi;\r
8209 \r
8210   switch (message) {\r
8211   case WM_INITDIALOG: /* message: initialize dialog box */\r
8212     hwndConsole = hDlg;\r
8213     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8214     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8215     SetFocus(hInput);\r
8216     consoleTextWindowProc = (WNDPROC)\r
8217       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8218     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8219     consoleInputWindowProc = (WNDPROC)\r
8220       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8221     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8222     Colorize(ColorNormal, TRUE);\r
8223     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8224     ChangedConsoleFont();\r
8225     GetClientRect(hDlg, &rect);\r
8226     sizeX = rect.right;\r
8227     sizeY = rect.bottom;\r
8228     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8229         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8230       WINDOWPLACEMENT wp;\r
8231       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8232       wp.length = sizeof(WINDOWPLACEMENT);\r
8233       wp.flags = 0;\r
8234       wp.showCmd = SW_SHOW;\r
8235       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8236       wp.rcNormalPosition.left = wpConsole.x;\r
8237       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8238       wp.rcNormalPosition.top = wpConsole.y;\r
8239       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8240       SetWindowPlacement(hDlg, &wp);\r
8241     }\r
8242 #if 1\r
8243    // [HGM] Chessknight's change 2004-07-13\r
8244    else { /* Determine Defaults */\r
8245        WINDOWPLACEMENT wp;\r
8246        wpConsole.x = winWidth + 1;\r
8247        wpConsole.y = boardY;\r
8248        wpConsole.width = screenWidth -  winWidth;\r
8249        wpConsole.height = winHeight;\r
8250        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8251        wp.length = sizeof(WINDOWPLACEMENT);\r
8252        wp.flags = 0;\r
8253        wp.showCmd = SW_SHOW;\r
8254        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8255        wp.rcNormalPosition.left = wpConsole.x;\r
8256        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8257        wp.rcNormalPosition.top = wpConsole.y;\r
8258        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8259        SetWindowPlacement(hDlg, &wp);\r
8260     }\r
8261 #endif\r
8262     return FALSE;\r
8263 \r
8264   case WM_SETFOCUS:\r
8265     SetFocus(hInput);\r
8266     return 0;\r
8267 \r
8268   case WM_CLOSE:\r
8269     ExitEvent(0);\r
8270     /* not reached */\r
8271     break;\r
8272 \r
8273   case WM_SIZE:\r
8274     if (IsIconic(hDlg)) break;\r
8275     newSizeX = LOWORD(lParam);\r
8276     newSizeY = HIWORD(lParam);\r
8277     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8278       RECT rectText, rectInput;\r
8279       POINT pt;\r
8280       int newTextHeight, newTextWidth;\r
8281       GetWindowRect(hText, &rectText);\r
8282       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8283       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8284       if (newTextHeight < 0) {\r
8285         newSizeY += -newTextHeight;\r
8286         newTextHeight = 0;\r
8287       }\r
8288       SetWindowPos(hText, NULL, 0, 0,\r
8289         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8290       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8291       pt.x = rectInput.left;\r
8292       pt.y = rectInput.top + newSizeY - sizeY;\r
8293       ScreenToClient(hDlg, &pt);\r
8294       SetWindowPos(hInput, NULL, \r
8295         pt.x, pt.y, /* needs client coords */   \r
8296         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8297         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8298     }\r
8299     sizeX = newSizeX;\r
8300     sizeY = newSizeY;\r
8301     break;\r
8302 \r
8303   case WM_GETMINMAXINFO:\r
8304     /* Prevent resizing window too small */\r
8305     mmi = (MINMAXINFO *) lParam;\r
8306     mmi->ptMinTrackSize.x = 100;\r
8307     mmi->ptMinTrackSize.y = 100;\r
8308     break;\r
8309 \r
8310   /* [AS] Snapping */\r
8311   case WM_ENTERSIZEMOVE:\r
8312     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8313 \r
8314   case WM_SIZING:\r
8315     return OnSizing( &sd, hDlg, wParam, lParam );\r
8316 \r
8317   case WM_MOVING:\r
8318     return OnMoving( &sd, hDlg, wParam, lParam );\r
8319 \r
8320   case WM_EXITSIZEMOVE:\r
8321     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8322   }\r
8323 \r
8324   return DefWindowProc(hDlg, message, wParam, lParam);\r
8325 }\r
8326 \r
8327 \r
8328 VOID\r
8329 ConsoleCreate()\r
8330 {\r
8331   HWND hCons;\r
8332   if (hwndConsole) return;\r
8333   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8334   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8335 }\r
8336 \r
8337 \r
8338 VOID\r
8339 ConsoleOutput(char* data, int length, int forceVisible)\r
8340 {\r
8341   HWND hText;\r
8342   int trim, exlen;\r
8343   char *p, *q;\r
8344   char buf[CO_MAX+1];\r
8345   POINT pEnd;\r
8346   RECT rect;\r
8347   static int delayLF = 0;\r
8348   CHARRANGE savesel, sel;\r
8349 \r
8350   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8351   p = data;\r
8352   q = buf;\r
8353   if (delayLF) {\r
8354     *q++ = '\r';\r
8355     *q++ = '\n';\r
8356     delayLF = 0;\r
8357   }\r
8358   while (length--) {\r
8359     if (*p == '\n') {\r
8360       if (*++p) {\r
8361         *q++ = '\r';\r
8362         *q++ = '\n';\r
8363       } else {\r
8364         delayLF = 1;\r
8365       }\r
8366     } else if (*p == '\007') {\r
8367        MyPlaySound(&sounds[(int)SoundBell]);\r
8368        p++;\r
8369     } else {\r
8370       *q++ = *p++;\r
8371     }\r
8372   }\r
8373   *q = NULLCHAR;\r
8374   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8375   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8376   /* Save current selection */\r
8377   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8378   exlen = GetWindowTextLength(hText);\r
8379   /* Find out whether current end of text is visible */\r
8380   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8381   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8382   /* Trim existing text if it's too long */\r
8383   if (exlen + (q - buf) > CO_MAX) {\r
8384     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8385     sel.cpMin = 0;\r
8386     sel.cpMax = trim;\r
8387     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8388     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8389     exlen -= trim;\r
8390     savesel.cpMin -= trim;\r
8391     savesel.cpMax -= trim;\r
8392     if (exlen < 0) exlen = 0;\r
8393     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8394     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8395   }\r
8396   /* Append the new text */\r
8397   sel.cpMin = exlen;\r
8398   sel.cpMax = exlen;\r
8399   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8400   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8401   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8402   if (forceVisible || exlen == 0 ||\r
8403       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8404        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8405     /* Scroll to make new end of text visible if old end of text\r
8406        was visible or new text is an echo of user typein */\r
8407     sel.cpMin = 9999999;\r
8408     sel.cpMax = 9999999;\r
8409     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8410     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8411     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8412     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8413   }\r
8414   if (savesel.cpMax == exlen || forceVisible) {\r
8415     /* Move insert point to new end of text if it was at the old\r
8416        end of text or if the new text is an echo of user typein */\r
8417     sel.cpMin = 9999999;\r
8418     sel.cpMax = 9999999;\r
8419     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8420   } else {\r
8421     /* Restore previous selection */\r
8422     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8423   }\r
8424   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8425 }\r
8426 \r
8427 /*---------*/\r
8428 \r
8429 \r
8430 void\r
8431 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8432 {\r
8433   char buf[100];\r
8434   char *str;\r
8435   COLORREF oldFg, oldBg;\r
8436   HFONT oldFont;\r
8437   RECT rect;\r
8438 \r
8439   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8440 \r
8441   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8442   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8443   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8444 \r
8445   rect.left = x;\r
8446   rect.right = x + squareSize;\r
8447   rect.top  = y;\r
8448   rect.bottom = y + squareSize;\r
8449   str = buf;\r
8450 \r
8451   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8452                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8453              y, ETO_CLIPPED|ETO_OPAQUE,\r
8454              &rect, str, strlen(str), NULL);\r
8455 \r
8456   (void) SetTextColor(hdc, oldFg);\r
8457   (void) SetBkColor(hdc, oldBg);\r
8458   (void) SelectObject(hdc, oldFont);\r
8459 }\r
8460 \r
8461 void\r
8462 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8463               RECT *rect, char *color, char *flagFell)\r
8464 {\r
8465   char buf[100];\r
8466   char *str;\r
8467   COLORREF oldFg, oldBg;\r
8468   HFONT oldFont;\r
8469 \r
8470   if (appData.clockMode) {\r
8471     if (tinyLayout)\r
8472       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8473     else\r
8474       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8475     str = buf;\r
8476   } else {\r
8477     str = color;\r
8478   }\r
8479 \r
8480   if (highlight) {\r
8481     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8482     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8483   } else {\r
8484     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8485     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8486   }\r
8487   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8488 \r
8489   JAWS_SILENCE\r
8490 \r
8491   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8492              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8493              rect, str, strlen(str), NULL);\r
8494   if(logoHeight > 0 && appData.clockMode) {\r
8495       RECT r;\r
8496       sprintf(buf, "%s %s", buf+7, flagFell);\r
8497       r.top = rect->top + logoHeight/2;\r
8498       r.left = rect->left;\r
8499       r.right = rect->right;\r
8500       r.bottom = rect->bottom;\r
8501       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8502                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8503                  &r, str, strlen(str), NULL);\r
8504   }\r
8505   (void) SetTextColor(hdc, oldFg);\r
8506   (void) SetBkColor(hdc, oldBg);\r
8507   (void) SelectObject(hdc, oldFont);\r
8508 }\r
8509 \r
8510 \r
8511 int\r
8512 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8513            OVERLAPPED *ovl)\r
8514 {\r
8515   int ok, err;\r
8516 \r
8517   /* [AS]  */\r
8518   if( count <= 0 ) {\r
8519     if (appData.debugMode) {\r
8520       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8521     }\r
8522 \r
8523     return ERROR_INVALID_USER_BUFFER;\r
8524   }\r
8525 \r
8526   ResetEvent(ovl->hEvent);\r
8527   ovl->Offset = ovl->OffsetHigh = 0;\r
8528   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8529   if (ok) {\r
8530     err = NO_ERROR;\r
8531   } else {\r
8532     err = GetLastError();\r
8533     if (err == ERROR_IO_PENDING) {\r
8534       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8535       if (ok)\r
8536         err = NO_ERROR;\r
8537       else\r
8538         err = GetLastError();\r
8539     }\r
8540   }\r
8541   return err;\r
8542 }\r
8543 \r
8544 int\r
8545 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8546             OVERLAPPED *ovl)\r
8547 {\r
8548   int ok, err;\r
8549 \r
8550   ResetEvent(ovl->hEvent);\r
8551   ovl->Offset = ovl->OffsetHigh = 0;\r
8552   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8553   if (ok) {\r
8554     err = NO_ERROR;\r
8555   } else {\r
8556     err = GetLastError();\r
8557     if (err == ERROR_IO_PENDING) {\r
8558       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8559       if (ok)\r
8560         err = NO_ERROR;\r
8561       else\r
8562         err = GetLastError();\r
8563     }\r
8564   }\r
8565   return err;\r
8566 }\r
8567 \r
8568 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8569 void CheckForInputBufferFull( InputSource * is )\r
8570 {\r
8571     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8572         /* Look for end of line */\r
8573         char * p = is->buf;\r
8574         \r
8575         while( p < is->next && *p != '\n' ) {\r
8576             p++;\r
8577         }\r
8578 \r
8579         if( p >= is->next ) {\r
8580             if (appData.debugMode) {\r
8581                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8582             }\r
8583 \r
8584             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8585             is->count = (DWORD) -1;\r
8586             is->next = is->buf;\r
8587         }\r
8588     }\r
8589 }\r
8590 \r
8591 DWORD\r
8592 InputThread(LPVOID arg)\r
8593 {\r
8594   InputSource *is;\r
8595   OVERLAPPED ovl;\r
8596 \r
8597   is = (InputSource *) arg;\r
8598   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8599   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8600   while (is->hThread != NULL) {\r
8601     is->error = DoReadFile(is->hFile, is->next,\r
8602                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8603                            &is->count, &ovl);\r
8604     if (is->error == NO_ERROR) {\r
8605       is->next += is->count;\r
8606     } else {\r
8607       if (is->error == ERROR_BROKEN_PIPE) {\r
8608         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8609         is->count = 0;\r
8610       } else {\r
8611         is->count = (DWORD) -1;\r
8612         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8613         break; \r
8614       }\r
8615     }\r
8616 \r
8617     CheckForInputBufferFull( is );\r
8618 \r
8619     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8620 \r
8621     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8622 \r
8623     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8624   }\r
8625 \r
8626   CloseHandle(ovl.hEvent);\r
8627   CloseHandle(is->hFile);\r
8628 \r
8629   if (appData.debugMode) {\r
8630     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8631   }\r
8632 \r
8633   return 0;\r
8634 }\r
8635 \r
8636 \r
8637 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8638 DWORD\r
8639 NonOvlInputThread(LPVOID arg)\r
8640 {\r
8641   InputSource *is;\r
8642   char *p, *q;\r
8643   int i;\r
8644   char prev;\r
8645 \r
8646   is = (InputSource *) arg;\r
8647   while (is->hThread != NULL) {\r
8648     is->error = ReadFile(is->hFile, is->next,\r
8649                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8650                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8651     if (is->error == NO_ERROR) {\r
8652       /* Change CRLF to LF */\r
8653       if (is->next > is->buf) {\r
8654         p = is->next - 1;\r
8655         i = is->count + 1;\r
8656       } else {\r
8657         p = is->next;\r
8658         i = is->count;\r
8659       }\r
8660       q = p;\r
8661       prev = NULLCHAR;\r
8662       while (i > 0) {\r
8663         if (prev == '\r' && *p == '\n') {\r
8664           *(q-1) = '\n';\r
8665           is->count--;\r
8666         } else { \r
8667           *q++ = *p;\r
8668         }\r
8669         prev = *p++;\r
8670         i--;\r
8671       }\r
8672       *q = NULLCHAR;\r
8673       is->next = q;\r
8674     } else {\r
8675       if (is->error == ERROR_BROKEN_PIPE) {\r
8676         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8677         is->count = 0; \r
8678       } else {\r
8679         is->count = (DWORD) -1;\r
8680       }\r
8681     }\r
8682 \r
8683     CheckForInputBufferFull( is );\r
8684 \r
8685     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8686 \r
8687     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8688 \r
8689     if (is->count < 0) break;  /* Quit on error */\r
8690   }\r
8691   CloseHandle(is->hFile);\r
8692   return 0;\r
8693 }\r
8694 \r
8695 DWORD\r
8696 SocketInputThread(LPVOID arg)\r
8697 {\r
8698   InputSource *is;\r
8699 \r
8700   is = (InputSource *) arg;\r
8701   while (is->hThread != NULL) {\r
8702     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8703     if ((int)is->count == SOCKET_ERROR) {\r
8704       is->count = (DWORD) -1;\r
8705       is->error = WSAGetLastError();\r
8706     } else {\r
8707       is->error = NO_ERROR;\r
8708       is->next += is->count;\r
8709       if (is->count == 0 && is->second == is) {\r
8710         /* End of file on stderr; quit with no message */\r
8711         break;\r
8712       }\r
8713     }\r
8714     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8715 \r
8716     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8717 \r
8718     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8719   }\r
8720   return 0;\r
8721 }\r
8722 \r
8723 VOID\r
8724 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8725 {\r
8726   InputSource *is;\r
8727 \r
8728   is = (InputSource *) lParam;\r
8729   if (is->lineByLine) {\r
8730     /* Feed in lines one by one */\r
8731     char *p = is->buf;\r
8732     char *q = p;\r
8733     while (q < is->next) {\r
8734       if (*q++ == '\n') {\r
8735         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8736         p = q;\r
8737       }\r
8738     }\r
8739     \r
8740     /* Move any partial line to the start of the buffer */\r
8741     q = is->buf;\r
8742     while (p < is->next) {\r
8743       *q++ = *p++;\r
8744     }\r
8745     is->next = q;\r
8746 \r
8747     if (is->error != NO_ERROR || is->count == 0) {\r
8748       /* Notify backend of the error.  Note: If there was a partial\r
8749          line at the end, it is not flushed through. */\r
8750       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8751     }\r
8752   } else {\r
8753     /* Feed in the whole chunk of input at once */\r
8754     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8755     is->next = is->buf;\r
8756   }\r
8757 }\r
8758 \r
8759 /*---------------------------------------------------------------------------*\\r
8760  *\r
8761  *  Menu enables. Used when setting various modes.\r
8762  *\r
8763 \*---------------------------------------------------------------------------*/\r
8764 \r
8765 typedef struct {\r
8766   int item;\r
8767   int flags;\r
8768 } Enables;\r
8769 \r
8770 VOID\r
8771 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8772 {\r
8773   while (enab->item > 0) {\r
8774     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8775     enab++;\r
8776   }\r
8777 }\r
8778 \r
8779 Enables gnuEnables[] = {\r
8780   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8781   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8782   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8783   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8784   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8785   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8786   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8787   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8788   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8789   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8790   { -1, -1 }\r
8791 };\r
8792 \r
8793 Enables icsEnables[] = {\r
8794   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8795   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8796   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8797   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8798   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8799   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8800   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8801   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8802   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8803   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8804   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8805   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8806   { -1, -1 }\r
8807 };\r
8808 \r
8809 #ifdef ZIPPY\r
8810 Enables zippyEnables[] = {\r
8811   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8812   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8813   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8814   { -1, -1 }\r
8815 };\r
8816 #endif\r
8817 \r
8818 Enables ncpEnables[] = {\r
8819   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8820   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8821   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8822   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8823   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8824   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8825   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8826   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8827   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8828   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8829   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8830   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8831   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8832   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8833   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8834   { -1, -1 }\r
8835 };\r
8836 \r
8837 Enables trainingOnEnables[] = {\r
8838   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8839   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8840   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8841   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8842   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8843   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8844   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8845   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8846   { -1, -1 }\r
8847 };\r
8848 \r
8849 Enables trainingOffEnables[] = {\r
8850   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8851   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8852   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8853   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8854   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8855   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8856   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8857   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8858   { -1, -1 }\r
8859 };\r
8860 \r
8861 /* These modify either ncpEnables or gnuEnables */\r
8862 Enables cmailEnables[] = {\r
8863   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8864   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8865   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8866   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8867   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8868   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8869   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8870   { -1, -1 }\r
8871 };\r
8872 \r
8873 Enables machineThinkingEnables[] = {\r
8874   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8875   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8876   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8877   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8878   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8879   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8880   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8881   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8882   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8883   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8884   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8885   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8886   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8887   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8888   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8889   { -1, -1 }\r
8890 };\r
8891 \r
8892 Enables userThinkingEnables[] = {\r
8893   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8894   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8895   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8896   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8897   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8898   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8899   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8900   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8901   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8902   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8903   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8904   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8905   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8906   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8907   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8908   { -1, -1 }\r
8909 };\r
8910 \r
8911 /*---------------------------------------------------------------------------*\\r
8912  *\r
8913  *  Front-end interface functions exported by XBoard.\r
8914  *  Functions appear in same order as prototypes in frontend.h.\r
8915  * \r
8916 \*---------------------------------------------------------------------------*/\r
8917 VOID\r
8918 ModeHighlight()\r
8919 {\r
8920   static UINT prevChecked = 0;\r
8921   static int prevPausing = 0;\r
8922   UINT nowChecked;\r
8923 \r
8924   if (pausing != prevPausing) {\r
8925     prevPausing = pausing;\r
8926     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8927                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8928     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8929   }\r
8930 \r
8931   switch (gameMode) {\r
8932   case BeginningOfGame:\r
8933     if (appData.icsActive)\r
8934       nowChecked = IDM_IcsClient;\r
8935     else if (appData.noChessProgram)\r
8936       nowChecked = IDM_EditGame;\r
8937     else\r
8938       nowChecked = IDM_MachineBlack;\r
8939     break;\r
8940   case MachinePlaysBlack:\r
8941     nowChecked = IDM_MachineBlack;\r
8942     break;\r
8943   case MachinePlaysWhite:\r
8944     nowChecked = IDM_MachineWhite;\r
8945     break;\r
8946   case TwoMachinesPlay:\r
8947     nowChecked = IDM_TwoMachines;\r
8948     break;\r
8949   case AnalyzeMode:\r
8950     nowChecked = IDM_AnalysisMode;\r
8951     break;\r
8952   case AnalyzeFile:\r
8953     nowChecked = IDM_AnalyzeFile;\r
8954     break;\r
8955   case EditGame:\r
8956     nowChecked = IDM_EditGame;\r
8957     break;\r
8958   case PlayFromGameFile:\r
8959     nowChecked = IDM_LoadGame;\r
8960     break;\r
8961   case EditPosition:\r
8962     nowChecked = IDM_EditPosition;\r
8963     break;\r
8964   case Training:\r
8965     nowChecked = IDM_Training;\r
8966     break;\r
8967   case IcsPlayingWhite:\r
8968   case IcsPlayingBlack:\r
8969   case IcsObserving:\r
8970   case IcsIdle:\r
8971     nowChecked = IDM_IcsClient;\r
8972     break;\r
8973   default:\r
8974   case EndOfGame:\r
8975     nowChecked = 0;\r
8976     break;\r
8977   }\r
8978   if (prevChecked != 0)\r
8979     (void) CheckMenuItem(GetMenu(hwndMain),\r
8980                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8981   if (nowChecked != 0)\r
8982     (void) CheckMenuItem(GetMenu(hwndMain),\r
8983                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8984 \r
8985   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8986     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8987                           MF_BYCOMMAND|MF_ENABLED);\r
8988   } else {\r
8989     (void) EnableMenuItem(GetMenu(hwndMain), \r
8990                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8991   }\r
8992 \r
8993   prevChecked = nowChecked;\r
8994 \r
8995   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8996   if (appData.icsActive) {\r
8997        if (appData.icsEngineAnalyze) {\r
8998                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8999                        MF_BYCOMMAND|MF_CHECKED);\r
9000        } else {\r
9001                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9002                        MF_BYCOMMAND|MF_UNCHECKED);\r
9003        }\r
9004   }\r
9005 }\r
9006 \r
9007 VOID\r
9008 SetICSMode()\r
9009 {\r
9010   HMENU hmenu = GetMenu(hwndMain);\r
9011   SetMenuEnables(hmenu, icsEnables);\r
9012   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
9013     MF_BYPOSITION|MF_ENABLED);\r
9014 #ifdef ZIPPY\r
9015   if (appData.zippyPlay) {\r
9016     SetMenuEnables(hmenu, zippyEnables);\r
9017     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
9018          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9019           MF_BYCOMMAND|MF_ENABLED);\r
9020   }\r
9021 #endif\r
9022 }\r
9023 \r
9024 VOID\r
9025 SetGNUMode()\r
9026 {\r
9027   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
9028 }\r
9029 \r
9030 VOID\r
9031 SetNCPMode()\r
9032 {\r
9033   HMENU hmenu = GetMenu(hwndMain);\r
9034   SetMenuEnables(hmenu, ncpEnables);\r
9035   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
9036     MF_BYPOSITION|MF_GRAYED);\r
9037     DrawMenuBar(hwndMain);\r
9038 }\r
9039 \r
9040 VOID\r
9041 SetCmailMode()\r
9042 {\r
9043   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9044 }\r
9045 \r
9046 VOID \r
9047 SetTrainingModeOn()\r
9048 {\r
9049   int i;\r
9050   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9051   for (i = 0; i < N_BUTTONS; i++) {\r
9052     if (buttonDesc[i].hwnd != NULL)\r
9053       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9054   }\r
9055   CommentPopDown();\r
9056 }\r
9057 \r
9058 VOID SetTrainingModeOff()\r
9059 {\r
9060   int i;\r
9061   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9062   for (i = 0; i < N_BUTTONS; i++) {\r
9063     if (buttonDesc[i].hwnd != NULL)\r
9064       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9065   }\r
9066 }\r
9067 \r
9068 \r
9069 VOID\r
9070 SetUserThinkingEnables()\r
9071 {\r
9072   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9073 }\r
9074 \r
9075 VOID\r
9076 SetMachineThinkingEnables()\r
9077 {\r
9078   HMENU hMenu = GetMenu(hwndMain);\r
9079   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9080 \r
9081   SetMenuEnables(hMenu, machineThinkingEnables);\r
9082 \r
9083   if (gameMode == MachinePlaysBlack) {\r
9084     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9085   } else if (gameMode == MachinePlaysWhite) {\r
9086     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9087   } else if (gameMode == TwoMachinesPlay) {\r
9088     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9089   }\r
9090 }\r
9091 \r
9092 \r
9093 VOID\r
9094 DisplayTitle(char *str)\r
9095 {\r
9096   char title[MSG_SIZ], *host;\r
9097   if (str[0] != NULLCHAR) {\r
9098     strcpy(title, str);\r
9099   } else if (appData.icsActive) {\r
9100     if (appData.icsCommPort[0] != NULLCHAR)\r
9101       host = "ICS";\r
9102     else \r
9103       host = appData.icsHost;\r
9104     sprintf(title, "%s: %s", szTitle, host);\r
9105   } else if (appData.noChessProgram) {\r
9106     strcpy(title, szTitle);\r
9107   } else {\r
9108     strcpy(title, szTitle);\r
9109     strcat(title, ": ");\r
9110     strcat(title, first.tidy);\r
9111   }\r
9112   SetWindowText(hwndMain, title);\r
9113 }\r
9114 \r
9115 \r
9116 VOID\r
9117 DisplayMessage(char *str1, char *str2)\r
9118 {\r
9119   HDC hdc;\r
9120   HFONT oldFont;\r
9121   int remain = MESSAGE_TEXT_MAX - 1;\r
9122   int len;\r
9123 \r
9124   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9125   messageText[0] = NULLCHAR;\r
9126   if (*str1) {\r
9127     len = strlen(str1);\r
9128     if (len > remain) len = remain;\r
9129     strncpy(messageText, str1, len);\r
9130     messageText[len] = NULLCHAR;\r
9131     remain -= len;\r
9132   }\r
9133   if (*str2 && remain >= 2) {\r
9134     if (*str1) {\r
9135       strcat(messageText, "  ");\r
9136       remain -= 2;\r
9137     }\r
9138     len = strlen(str2);\r
9139     if (len > remain) len = remain;\r
9140     strncat(messageText, str2, len);\r
9141   }\r
9142   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9143 \r
9144   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9145 \r
9146   SAYMACHINEMOVE();\r
9147 \r
9148   hdc = GetDC(hwndMain);\r
9149   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9150   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9151              &messageRect, messageText, strlen(messageText), NULL);\r
9152   (void) SelectObject(hdc, oldFont);\r
9153   (void) ReleaseDC(hwndMain, hdc);\r
9154 }\r
9155 \r
9156 VOID\r
9157 DisplayError(char *str, int error)\r
9158 {\r
9159   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9160   int len;\r
9161 \r
9162   if (error == 0) {\r
9163     strcpy(buf, str);\r
9164   } else {\r
9165     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9166                         NULL, error, LANG_NEUTRAL,\r
9167                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9168     if (len > 0) {\r
9169       sprintf(buf, "%s:\n%s", str, buf2);\r
9170     } else {\r
9171       ErrorMap *em = errmap;\r
9172       while (em->err != 0 && em->err != error) em++;\r
9173       if (em->err != 0) {\r
9174         sprintf(buf, "%s:\n%s", str, em->msg);\r
9175       } else {\r
9176         sprintf(buf, "%s:\nError code %d", str, error);\r
9177       }\r
9178     }\r
9179   }\r
9180   \r
9181   ErrorPopUp("Error", buf);\r
9182 }\r
9183 \r
9184 \r
9185 VOID\r
9186 DisplayMoveError(char *str)\r
9187 {\r
9188   fromX = fromY = -1;\r
9189   ClearHighlights();\r
9190   DrawPosition(FALSE, NULL);\r
9191   if (appData.popupMoveErrors) {\r
9192     ErrorPopUp("Error", str);\r
9193   } else {\r
9194     DisplayMessage(str, "");\r
9195     moveErrorMessageUp = TRUE;\r
9196   }\r
9197 }\r
9198 \r
9199 VOID\r
9200 DisplayFatalError(char *str, int error, int exitStatus)\r
9201 {\r
9202   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9203   int len;\r
9204   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9205 \r
9206   if (error != 0) {\r
9207     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9208                         NULL, error, LANG_NEUTRAL,\r
9209                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9210     if (len > 0) {\r
9211       sprintf(buf, "%s:\n%s", str, buf2);\r
9212     } else {\r
9213       ErrorMap *em = errmap;\r
9214       while (em->err != 0 && em->err != error) em++;\r
9215       if (em->err != 0) {\r
9216         sprintf(buf, "%s:\n%s", str, em->msg);\r
9217       } else {\r
9218         sprintf(buf, "%s:\nError code %d", str, error);\r
9219       }\r
9220     }\r
9221     str = buf;\r
9222   }\r
9223   if (appData.debugMode) {\r
9224     fprintf(debugFP, "%s: %s\n", label, str);\r
9225   }\r
9226   if (appData.popupExitMessage) {\r
9227     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9228                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9229   }\r
9230   ExitEvent(exitStatus);\r
9231 }\r
9232 \r
9233 \r
9234 VOID\r
9235 DisplayInformation(char *str)\r
9236 {\r
9237   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9238 }\r
9239 \r
9240 \r
9241 VOID\r
9242 DisplayNote(char *str)\r
9243 {\r
9244   ErrorPopUp("Note", str);\r
9245 }\r
9246 \r
9247 \r
9248 typedef struct {\r
9249   char *title, *question, *replyPrefix;\r
9250   ProcRef pr;\r
9251 } QuestionParams;\r
9252 \r
9253 LRESULT CALLBACK\r
9254 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9255 {\r
9256   static QuestionParams *qp;\r
9257   char reply[MSG_SIZ];\r
9258   int len, err;\r
9259 \r
9260   switch (message) {\r
9261   case WM_INITDIALOG:\r
9262     qp = (QuestionParams *) lParam;\r
9263     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9264     SetWindowText(hDlg, qp->title);\r
9265     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9266     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9267     return FALSE;\r
9268 \r
9269   case WM_COMMAND:\r
9270     switch (LOWORD(wParam)) {\r
9271     case IDOK:\r
9272       strcpy(reply, qp->replyPrefix);\r
9273       if (*reply) strcat(reply, " ");\r
9274       len = strlen(reply);\r
9275       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9276       strcat(reply, "\n");\r
9277       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9278       EndDialog(hDlg, TRUE);\r
9279       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9280       return TRUE;\r
9281     case IDCANCEL:\r
9282       EndDialog(hDlg, FALSE);\r
9283       return TRUE;\r
9284     default:\r
9285       break;\r
9286     }\r
9287     break;\r
9288   }\r
9289   return FALSE;\r
9290 }\r
9291 \r
9292 VOID\r
9293 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9294 {\r
9295     QuestionParams qp;\r
9296     FARPROC lpProc;\r
9297     \r
9298     qp.title = title;\r
9299     qp.question = question;\r
9300     qp.replyPrefix = replyPrefix;\r
9301     qp.pr = pr;\r
9302     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9303     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9304       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9305     FreeProcInstance(lpProc);\r
9306 }\r
9307 \r
9308 /* [AS] Pick FRC position */\r
9309 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9310 {\r
9311     static int * lpIndexFRC;\r
9312     BOOL index_is_ok;\r
9313     char buf[16];\r
9314 \r
9315     switch( message )\r
9316     {\r
9317     case WM_INITDIALOG:\r
9318         lpIndexFRC = (int *) lParam;\r
9319 \r
9320         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9321 \r
9322         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9323         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9324         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9325         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9326 \r
9327         break;\r
9328 \r
9329     case WM_COMMAND:\r
9330         switch( LOWORD(wParam) ) {\r
9331         case IDOK:\r
9332             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9333             EndDialog( hDlg, 0 );\r
9334             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9335             return TRUE;\r
9336         case IDCANCEL:\r
9337             EndDialog( hDlg, 1 );   \r
9338             return TRUE;\r
9339         case IDC_NFG_Edit:\r
9340             if( HIWORD(wParam) == EN_CHANGE ) {\r
9341                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9342 \r
9343                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9344             }\r
9345             return TRUE;\r
9346         case IDC_NFG_Random:\r
9347             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9348             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9349             return TRUE;\r
9350         }\r
9351 \r
9352         break;\r
9353     }\r
9354 \r
9355     return FALSE;\r
9356 }\r
9357 \r
9358 int NewGameFRC()\r
9359 {\r
9360     int result;\r
9361     int index = appData.defaultFrcPosition;\r
9362     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9363 \r
9364     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9365 \r
9366     if( result == 0 ) {\r
9367         appData.defaultFrcPosition = index;\r
9368     }\r
9369 \r
9370     return result;\r
9371 }\r
9372 \r
9373 /* [AS] Game list options */\r
9374 typedef struct {\r
9375     char id;\r
9376     char * name;\r
9377 } GLT_Item;\r
9378 \r
9379 static GLT_Item GLT_ItemInfo[] = {\r
9380     { GLT_EVENT,      "Event" },\r
9381     { GLT_SITE,       "Site" },\r
9382     { GLT_DATE,       "Date" },\r
9383     { GLT_ROUND,      "Round" },\r
9384     { GLT_PLAYERS,    "Players" },\r
9385     { GLT_RESULT,     "Result" },\r
9386     { GLT_WHITE_ELO,  "White Rating" },\r
9387     { GLT_BLACK_ELO,  "Black Rating" },\r
9388     { GLT_TIME_CONTROL,"Time Control" },\r
9389     { GLT_VARIANT,    "Variant" },\r
9390     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9391     { 0, 0 }\r
9392 };\r
9393 \r
9394 const char * GLT_FindItem( char id )\r
9395 {\r
9396     const char * result = 0;\r
9397 \r
9398     GLT_Item * list = GLT_ItemInfo;\r
9399 \r
9400     while( list->id != 0 ) {\r
9401         if( list->id == id ) {\r
9402             result = list->name;\r
9403             break;\r
9404         }\r
9405 \r
9406         list++;\r
9407     }\r
9408 \r
9409     return result;\r
9410 }\r
9411 \r
9412 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9413 {\r
9414     const char * name = GLT_FindItem( id );\r
9415 \r
9416     if( name != 0 ) {\r
9417         if( index >= 0 ) {\r
9418             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9419         }\r
9420         else {\r
9421             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9422         }\r
9423     }\r
9424 }\r
9425 \r
9426 void GLT_TagsToList( HWND hDlg, char * tags )\r
9427 {\r
9428     char * pc = tags;\r
9429 \r
9430     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9431 \r
9432     while( *pc ) {\r
9433         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9434         pc++;\r
9435     }\r
9436 \r
9437     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9438 \r
9439     pc = GLT_ALL_TAGS;\r
9440 \r
9441     while( *pc ) {\r
9442         if( strchr( tags, *pc ) == 0 ) {\r
9443             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9444         }\r
9445         pc++;\r
9446     }\r
9447 \r
9448     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9449 }\r
9450 \r
9451 char GLT_ListItemToTag( HWND hDlg, int index )\r
9452 {\r
9453     char result = '\0';\r
9454     char name[128];\r
9455 \r
9456     GLT_Item * list = GLT_ItemInfo;\r
9457 \r
9458     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9459         while( list->id != 0 ) {\r
9460             if( strcmp( list->name, name ) == 0 ) {\r
9461                 result = list->id;\r
9462                 break;\r
9463             }\r
9464 \r
9465             list++;\r
9466         }\r
9467     }\r
9468 \r
9469     return result;\r
9470 }\r
9471 \r
9472 void GLT_MoveSelection( HWND hDlg, int delta )\r
9473 {\r
9474     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9475     int idx2 = idx1 + delta;\r
9476     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9477 \r
9478     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9479         char buf[128];\r
9480 \r
9481         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9482         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9483         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9484         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9485     }\r
9486 }\r
9487 \r
9488 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9489 {\r
9490     static char glt[64];\r
9491     static char * lpUserGLT;\r
9492 \r
9493     switch( message )\r
9494     {\r
9495     case WM_INITDIALOG:\r
9496         lpUserGLT = (char *) lParam;\r
9497         \r
9498         strcpy( glt, lpUserGLT );\r
9499 \r
9500         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9501 \r
9502         /* Initialize list */\r
9503         GLT_TagsToList( hDlg, glt );\r
9504 \r
9505         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9506 \r
9507         break;\r
9508 \r
9509     case WM_COMMAND:\r
9510         switch( LOWORD(wParam) ) {\r
9511         case IDOK:\r
9512             {\r
9513                 char * pc = lpUserGLT;\r
9514                 int idx = 0;\r
9515 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9516                 char id;\r
9517 \r
9518                 do {\r
9519                     id = GLT_ListItemToTag( hDlg, idx );\r
9520 \r
9521                     *pc++ = id;\r
9522                     idx++;\r
9523                 } while( id != '\0' );\r
9524             }\r
9525             EndDialog( hDlg, 0 );\r
9526             return TRUE;\r
9527         case IDCANCEL:\r
9528             EndDialog( hDlg, 1 );\r
9529             return TRUE;\r
9530 \r
9531         case IDC_GLT_Default:\r
9532             strcpy( glt, GLT_DEFAULT_TAGS );\r
9533             GLT_TagsToList( hDlg, glt );\r
9534             return TRUE;\r
9535 \r
9536         case IDC_GLT_Restore:\r
9537             strcpy( glt, lpUserGLT );\r
9538             GLT_TagsToList( hDlg, glt );\r
9539             return TRUE;\r
9540 \r
9541         case IDC_GLT_Up:\r
9542             GLT_MoveSelection( hDlg, -1 );\r
9543             return TRUE;\r
9544 \r
9545         case IDC_GLT_Down:\r
9546             GLT_MoveSelection( hDlg, +1 );\r
9547             return TRUE;\r
9548         }\r
9549 \r
9550         break;\r
9551     }\r
9552 \r
9553     return FALSE;\r
9554 }\r
9555 \r
9556 int GameListOptions()\r
9557 {\r
9558     char glt[64];\r
9559     int result;\r
9560     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9561 \r
9562     strcpy( glt, appData.gameListTags );\r
9563 \r
9564     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9565 \r
9566     if( result == 0 ) {\r
9567         /* [AS] Memory leak here! */\r
9568         appData.gameListTags = strdup( glt ); \r
9569     }\r
9570 \r
9571     return result;\r
9572 }\r
9573 \r
9574 \r
9575 VOID\r
9576 DisplayIcsInteractionTitle(char *str)\r
9577 {\r
9578   char consoleTitle[MSG_SIZ];\r
9579 \r
9580   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9581   SetWindowText(hwndConsole, consoleTitle);\r
9582 }\r
9583 \r
9584 void\r
9585 DrawPosition(int fullRedraw, Board board)\r
9586 {\r
9587   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9588 }\r
9589 \r
9590 \r
9591 VOID\r
9592 ResetFrontEnd()\r
9593 {\r
9594   fromX = fromY = -1;\r
9595   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9596     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9597     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9598     dragInfo.lastpos = dragInfo.pos;\r
9599     dragInfo.start.x = dragInfo.start.y = -1;\r
9600     dragInfo.from = dragInfo.start;\r
9601     ReleaseCapture();\r
9602     DrawPosition(TRUE, NULL);\r
9603   }\r
9604 }\r
9605 \r
9606 \r
9607 VOID\r
9608 CommentPopUp(char *title, char *str)\r
9609 {\r
9610   HWND hwnd = GetActiveWindow();\r
9611   EitherCommentPopUp(0, title, str, FALSE);\r
9612   SetActiveWindow(hwnd);\r
9613 }\r
9614 \r
9615 VOID\r
9616 CommentPopDown(void)\r
9617 {\r
9618   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9619   if (commentDialog) {\r
9620     ShowWindow(commentDialog, SW_HIDE);\r
9621   }\r
9622   commentDialogUp = FALSE;\r
9623 }\r
9624 \r
9625 VOID\r
9626 EditCommentPopUp(int index, char *title, char *str)\r
9627 {\r
9628   EitherCommentPopUp(index, title, str, TRUE);\r
9629 }\r
9630 \r
9631 \r
9632 VOID\r
9633 RingBell()\r
9634 {\r
9635   MyPlaySound(&sounds[(int)SoundMove]);\r
9636 }\r
9637 \r
9638 VOID PlayIcsWinSound()\r
9639 {\r
9640   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9641 }\r
9642 \r
9643 VOID PlayIcsLossSound()\r
9644 {\r
9645   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9646 }\r
9647 \r
9648 VOID PlayIcsDrawSound()\r
9649 {\r
9650   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9651 }\r
9652 \r
9653 VOID PlayIcsUnfinishedSound()\r
9654 {\r
9655   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9656 }\r
9657 \r
9658 VOID\r
9659 PlayAlarmSound()\r
9660 {\r
9661   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9662 }\r
9663 \r
9664 \r
9665 VOID\r
9666 EchoOn()\r
9667 {\r
9668   HWND hInput;\r
9669   consoleEcho = TRUE;\r
9670   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9671   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9672   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9673 }\r
9674 \r
9675 \r
9676 VOID\r
9677 EchoOff()\r
9678 {\r
9679   CHARFORMAT cf;\r
9680   HWND hInput;\r
9681   consoleEcho = FALSE;\r
9682   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9683   /* This works OK: set text and background both to the same color */\r
9684   cf = consoleCF;\r
9685   cf.crTextColor = COLOR_ECHOOFF;\r
9686   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9687   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9688 }\r
9689 \r
9690 /* No Raw()...? */\r
9691 \r
9692 void Colorize(ColorClass cc, int continuation)\r
9693 {\r
9694   currentColorClass = cc;\r
9695   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9696   consoleCF.crTextColor = textAttribs[cc].color;\r
9697   consoleCF.dwEffects = textAttribs[cc].effects;\r
9698   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9699 }\r
9700 \r
9701 char *\r
9702 UserName()\r
9703 {\r
9704   static char buf[MSG_SIZ];\r
9705   DWORD bufsiz = MSG_SIZ;\r
9706 \r
9707   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9708         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9709   }\r
9710   if (!GetUserName(buf, &bufsiz)) {\r
9711     /*DisplayError("Error getting user name", GetLastError());*/\r
9712     strcpy(buf, "User");\r
9713   }\r
9714   return buf;\r
9715 }\r
9716 \r
9717 char *\r
9718 HostName()\r
9719 {\r
9720   static char buf[MSG_SIZ];\r
9721   DWORD bufsiz = MSG_SIZ;\r
9722 \r
9723   if (!GetComputerName(buf, &bufsiz)) {\r
9724     /*DisplayError("Error getting host name", GetLastError());*/\r
9725     strcpy(buf, "Unknown");\r
9726   }\r
9727   return buf;\r
9728 }\r
9729 \r
9730 \r
9731 int\r
9732 ClockTimerRunning()\r
9733 {\r
9734   return clockTimerEvent != 0;\r
9735 }\r
9736 \r
9737 int\r
9738 StopClockTimer()\r
9739 {\r
9740   if (clockTimerEvent == 0) return FALSE;\r
9741   KillTimer(hwndMain, clockTimerEvent);\r
9742   clockTimerEvent = 0;\r
9743   return TRUE;\r
9744 }\r
9745 \r
9746 void\r
9747 StartClockTimer(long millisec)\r
9748 {\r
9749   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9750                              (UINT) millisec, NULL);\r
9751 }\r
9752 \r
9753 void\r
9754 DisplayWhiteClock(long timeRemaining, int highlight)\r
9755 {\r
9756   HDC hdc;\r
9757   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9758 \r
9759   if(appData.noGUI) return;\r
9760   hdc = GetDC(hwndMain);\r
9761   if (!IsIconic(hwndMain)) {\r
9762     DisplayAClock(hdc, timeRemaining, highlight, \r
9763                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9764   }\r
9765   if (highlight && iconCurrent == iconBlack) {\r
9766     iconCurrent = iconWhite;\r
9767     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9768     if (IsIconic(hwndMain)) {\r
9769       DrawIcon(hdc, 2, 2, iconCurrent);\r
9770     }\r
9771   }\r
9772   (void) ReleaseDC(hwndMain, hdc);\r
9773   if (hwndConsole)\r
9774     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9775 }\r
9776 \r
9777 void\r
9778 DisplayBlackClock(long timeRemaining, int highlight)\r
9779 {\r
9780   HDC hdc;\r
9781   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9782 \r
9783   if(appData.noGUI) return;\r
9784   hdc = GetDC(hwndMain);\r
9785   if (!IsIconic(hwndMain)) {\r
9786     DisplayAClock(hdc, timeRemaining, highlight, \r
9787                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9788   }\r
9789   if (highlight && iconCurrent == iconWhite) {\r
9790     iconCurrent = iconBlack;\r
9791     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9792     if (IsIconic(hwndMain)) {\r
9793       DrawIcon(hdc, 2, 2, iconCurrent);\r
9794     }\r
9795   }\r
9796   (void) ReleaseDC(hwndMain, hdc);\r
9797   if (hwndConsole)\r
9798     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9799 }\r
9800 \r
9801 \r
9802 int\r
9803 LoadGameTimerRunning()\r
9804 {\r
9805   return loadGameTimerEvent != 0;\r
9806 }\r
9807 \r
9808 int\r
9809 StopLoadGameTimer()\r
9810 {\r
9811   if (loadGameTimerEvent == 0) return FALSE;\r
9812   KillTimer(hwndMain, loadGameTimerEvent);\r
9813   loadGameTimerEvent = 0;\r
9814   return TRUE;\r
9815 }\r
9816 \r
9817 void\r
9818 StartLoadGameTimer(long millisec)\r
9819 {\r
9820   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9821                                 (UINT) millisec, NULL);\r
9822 }\r
9823 \r
9824 void\r
9825 AutoSaveGame()\r
9826 {\r
9827   char *defName;\r
9828   FILE *f;\r
9829   char fileTitle[MSG_SIZ];\r
9830 \r
9831   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9832   f = OpenFileDialog(hwndMain, "a", defName,\r
9833                      appData.oldSaveStyle ? "gam" : "pgn",\r
9834                      GAME_FILT, \r
9835                      "Save Game to File", NULL, fileTitle, NULL);\r
9836   if (f != NULL) {\r
9837     SaveGame(f, 0, "");\r
9838     fclose(f);\r
9839   }\r
9840 }\r
9841 \r
9842 \r
9843 void\r
9844 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9845 {\r
9846   if (delayedTimerEvent != 0) {\r
9847     if (appData.debugMode) {\r
9848       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9849     }\r
9850     KillTimer(hwndMain, delayedTimerEvent);\r
9851     delayedTimerEvent = 0;\r
9852     delayedTimerCallback();\r
9853   }\r
9854   delayedTimerCallback = cb;\r
9855   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9856                                 (UINT) millisec, NULL);\r
9857 }\r
9858 \r
9859 DelayedEventCallback\r
9860 GetDelayedEvent()\r
9861 {\r
9862   if (delayedTimerEvent) {\r
9863     return delayedTimerCallback;\r
9864   } else {\r
9865     return NULL;\r
9866   }\r
9867 }\r
9868 \r
9869 void\r
9870 CancelDelayedEvent()\r
9871 {\r
9872   if (delayedTimerEvent) {\r
9873     KillTimer(hwndMain, delayedTimerEvent);\r
9874     delayedTimerEvent = 0;\r
9875   }\r
9876 }\r
9877 \r
9878 DWORD GetWin32Priority(int nice)\r
9879 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9880 /*\r
9881 REALTIME_PRIORITY_CLASS     0x00000100\r
9882 HIGH_PRIORITY_CLASS         0x00000080\r
9883 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9884 NORMAL_PRIORITY_CLASS       0x00000020\r
9885 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9886 IDLE_PRIORITY_CLASS         0x00000040\r
9887 */\r
9888         if (nice < -15) return 0x00000080;\r
9889         if (nice < 0)   return 0x00008000;\r
9890         if (nice == 0)  return 0x00000020;\r
9891         if (nice < 15)  return 0x00004000;\r
9892         return 0x00000040;\r
9893 }\r
9894 \r
9895 /* Start a child process running the given program.\r
9896    The process's standard output can be read from "from", and its\r
9897    standard input can be written to "to".\r
9898    Exit with fatal error if anything goes wrong.\r
9899    Returns an opaque pointer that can be used to destroy the process\r
9900    later.\r
9901 */\r
9902 int\r
9903 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9904 {\r
9905 #define BUFSIZE 4096\r
9906 \r
9907   HANDLE hChildStdinRd, hChildStdinWr,\r
9908     hChildStdoutRd, hChildStdoutWr;\r
9909   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9910   SECURITY_ATTRIBUTES saAttr;\r
9911   BOOL fSuccess;\r
9912   PROCESS_INFORMATION piProcInfo;\r
9913   STARTUPINFO siStartInfo;\r
9914   ChildProc *cp;\r
9915   char buf[MSG_SIZ];\r
9916   DWORD err;\r
9917 \r
9918   if (appData.debugMode) {\r
9919     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9920   }\r
9921 \r
9922   *pr = NoProc;\r
9923 \r
9924   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9925   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9926   saAttr.bInheritHandle = TRUE;\r
9927   saAttr.lpSecurityDescriptor = NULL;\r
9928 \r
9929   /*\r
9930    * The steps for redirecting child's STDOUT:\r
9931    *     1. Create anonymous pipe to be STDOUT for child.\r
9932    *     2. Create a noninheritable duplicate of read handle,\r
9933    *         and close the inheritable read handle.\r
9934    */\r
9935 \r
9936   /* Create a pipe for the child's STDOUT. */\r
9937   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9938     return GetLastError();\r
9939   }\r
9940 \r
9941   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9942   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9943                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9944                              FALSE,     /* not inherited */\r
9945                              DUPLICATE_SAME_ACCESS);\r
9946   if (! fSuccess) {\r
9947     return GetLastError();\r
9948   }\r
9949   CloseHandle(hChildStdoutRd);\r
9950 \r
9951   /*\r
9952    * The steps for redirecting child's STDIN:\r
9953    *     1. Create anonymous pipe to be STDIN for child.\r
9954    *     2. Create a noninheritable duplicate of write handle,\r
9955    *         and close the inheritable write handle.\r
9956    */\r
9957 \r
9958   /* Create a pipe for the child's STDIN. */\r
9959   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9960     return GetLastError();\r
9961   }\r
9962 \r
9963   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9964   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9965                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9966                              FALSE,     /* not inherited */\r
9967                              DUPLICATE_SAME_ACCESS);\r
9968   if (! fSuccess) {\r
9969     return GetLastError();\r
9970   }\r
9971   CloseHandle(hChildStdinWr);\r
9972 \r
9973   /* Arrange to (1) look in dir for the child .exe file, and\r
9974    * (2) have dir be the child's working directory.  Interpret\r
9975    * dir relative to the directory WinBoard loaded from. */\r
9976   GetCurrentDirectory(MSG_SIZ, buf);\r
9977   SetCurrentDirectory(installDir);\r
9978   SetCurrentDirectory(dir);\r
9979 \r
9980   /* Now create the child process. */\r
9981 \r
9982   siStartInfo.cb = sizeof(STARTUPINFO);\r
9983   siStartInfo.lpReserved = NULL;\r
9984   siStartInfo.lpDesktop = NULL;\r
9985   siStartInfo.lpTitle = NULL;\r
9986   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9987   siStartInfo.cbReserved2 = 0;\r
9988   siStartInfo.lpReserved2 = NULL;\r
9989   siStartInfo.hStdInput = hChildStdinRd;\r
9990   siStartInfo.hStdOutput = hChildStdoutWr;\r
9991   siStartInfo.hStdError = hChildStdoutWr;\r
9992 \r
9993   fSuccess = CreateProcess(NULL,\r
9994                            cmdLine,        /* command line */\r
9995                            NULL,           /* process security attributes */\r
9996                            NULL,           /* primary thread security attrs */\r
9997                            TRUE,           /* handles are inherited */\r
9998                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9999                            NULL,           /* use parent's environment */\r
10000                            NULL,\r
10001                            &siStartInfo, /* STARTUPINFO pointer */\r
10002                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
10003 \r
10004   err = GetLastError();\r
10005   SetCurrentDirectory(buf); /* return to prev directory */\r
10006   if (! fSuccess) {\r
10007     return err;\r
10008   }\r
10009 \r
10010   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
10011     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
10012     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
10013   }\r
10014 \r
10015   /* Close the handles we don't need in the parent */\r
10016   CloseHandle(piProcInfo.hThread);\r
10017   CloseHandle(hChildStdinRd);\r
10018   CloseHandle(hChildStdoutWr);\r
10019 \r
10020   /* Prepare return value */\r
10021   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10022   cp->kind = CPReal;\r
10023   cp->hProcess = piProcInfo.hProcess;\r
10024   cp->pid = piProcInfo.dwProcessId;\r
10025   cp->hFrom = hChildStdoutRdDup;\r
10026   cp->hTo = hChildStdinWrDup;\r
10027 \r
10028   *pr = (void *) cp;\r
10029 \r
10030   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10031      2000 where engines sometimes don't see the initial command(s)\r
10032      from WinBoard and hang.  I don't understand how that can happen,\r
10033      but the Sleep is harmless, so I've put it in.  Others have also\r
10034      reported what may be the same problem, so hopefully this will fix\r
10035      it for them too.  */\r
10036   Sleep(500);\r
10037 \r
10038   return NO_ERROR;\r
10039 }\r
10040 \r
10041 \r
10042 void\r
10043 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10044 {\r
10045   ChildProc *cp; int result;\r
10046 \r
10047   cp = (ChildProc *) pr;\r
10048   if (cp == NULL) return;\r
10049 \r
10050   switch (cp->kind) {\r
10051   case CPReal:\r
10052     /* TerminateProcess is considered harmful, so... */\r
10053     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10054     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10055     /* The following doesn't work because the chess program\r
10056        doesn't "have the same console" as WinBoard.  Maybe\r
10057        we could arrange for this even though neither WinBoard\r
10058        nor the chess program uses a console for stdio? */\r
10059     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10060 \r
10061     /* [AS] Special termination modes for misbehaving programs... */\r
10062     if( signal == 9 ) { \r
10063         result = TerminateProcess( cp->hProcess, 0 );\r
10064 \r
10065         if ( appData.debugMode) {\r
10066             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10067         }\r
10068     }\r
10069     else if( signal == 10 ) {\r
10070         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10071 \r
10072         if( dw != WAIT_OBJECT_0 ) {\r
10073             result = TerminateProcess( cp->hProcess, 0 );\r
10074 \r
10075             if ( appData.debugMode) {\r
10076                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10077             }\r
10078 \r
10079         }\r
10080     }\r
10081 \r
10082     CloseHandle(cp->hProcess);\r
10083     break;\r
10084 \r
10085   case CPComm:\r
10086     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10087     break;\r
10088 \r
10089   case CPSock:\r
10090     closesocket(cp->sock);\r
10091     WSACleanup();\r
10092     break;\r
10093 \r
10094   case CPRcmd:\r
10095     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10096     closesocket(cp->sock);\r
10097     closesocket(cp->sock2);\r
10098     WSACleanup();\r
10099     break;\r
10100   }\r
10101   free(cp);\r
10102 }\r
10103 \r
10104 void\r
10105 InterruptChildProcess(ProcRef pr)\r
10106 {\r
10107   ChildProc *cp;\r
10108 \r
10109   cp = (ChildProc *) pr;\r
10110   if (cp == NULL) return;\r
10111   switch (cp->kind) {\r
10112   case CPReal:\r
10113     /* The following doesn't work because the chess program\r
10114        doesn't "have the same console" as WinBoard.  Maybe\r
10115        we could arrange for this even though neither WinBoard\r
10116        nor the chess program uses a console for stdio */\r
10117     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10118     break;\r
10119 \r
10120   case CPComm:\r
10121   case CPSock:\r
10122     /* Can't interrupt */\r
10123     break;\r
10124 \r
10125   case CPRcmd:\r
10126     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10127     break;\r
10128   }\r
10129 }\r
10130 \r
10131 \r
10132 int\r
10133 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10134 {\r
10135   char cmdLine[MSG_SIZ];\r
10136 \r
10137   if (port[0] == NULLCHAR) {\r
10138     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10139   } else {\r
10140     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10141   }\r
10142   return StartChildProcess(cmdLine, "", pr);\r
10143 }\r
10144 \r
10145 \r
10146 /* Code to open TCP sockets */\r
10147 \r
10148 int\r
10149 OpenTCP(char *host, char *port, ProcRef *pr)\r
10150 {\r
10151   ChildProc *cp;\r
10152   int err;\r
10153   SOCKET s;\r
10154   struct sockaddr_in sa, mysa;\r
10155   struct hostent FAR *hp;\r
10156   unsigned short uport;\r
10157   WORD wVersionRequested;\r
10158   WSADATA wsaData;\r
10159 \r
10160   /* Initialize socket DLL */\r
10161   wVersionRequested = MAKEWORD(1, 1);\r
10162   err = WSAStartup(wVersionRequested, &wsaData);\r
10163   if (err != 0) return err;\r
10164 \r
10165   /* Make socket */\r
10166   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10167     err = WSAGetLastError();\r
10168     WSACleanup();\r
10169     return err;\r
10170   }\r
10171 \r
10172   /* Bind local address using (mostly) don't-care values.\r
10173    */\r
10174   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10175   mysa.sin_family = AF_INET;\r
10176   mysa.sin_addr.s_addr = INADDR_ANY;\r
10177   uport = (unsigned short) 0;\r
10178   mysa.sin_port = htons(uport);\r
10179   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10180       == SOCKET_ERROR) {\r
10181     err = WSAGetLastError();\r
10182     WSACleanup();\r
10183     return err;\r
10184   }\r
10185 \r
10186   /* Resolve remote host name */\r
10187   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10188   if (!(hp = gethostbyname(host))) {\r
10189     unsigned int b0, b1, b2, b3;\r
10190 \r
10191     err = WSAGetLastError();\r
10192 \r
10193     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10194       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10195       hp->h_addrtype = AF_INET;\r
10196       hp->h_length = 4;\r
10197       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10198       hp->h_addr_list[0] = (char *) malloc(4);\r
10199       hp->h_addr_list[0][0] = (char) b0;\r
10200       hp->h_addr_list[0][1] = (char) b1;\r
10201       hp->h_addr_list[0][2] = (char) b2;\r
10202       hp->h_addr_list[0][3] = (char) b3;\r
10203     } else {\r
10204       WSACleanup();\r
10205       return err;\r
10206     }\r
10207   }\r
10208   sa.sin_family = hp->h_addrtype;\r
10209   uport = (unsigned short) atoi(port);\r
10210   sa.sin_port = htons(uport);\r
10211   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10212 \r
10213   /* Make connection */\r
10214   if (connect(s, (struct sockaddr *) &sa,\r
10215               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10216     err = WSAGetLastError();\r
10217     WSACleanup();\r
10218     return err;\r
10219   }\r
10220 \r
10221   /* Prepare return value */\r
10222   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10223   cp->kind = CPSock;\r
10224   cp->sock = s;\r
10225   *pr = (ProcRef *) cp;\r
10226 \r
10227   return NO_ERROR;\r
10228 }\r
10229 \r
10230 int\r
10231 OpenCommPort(char *name, ProcRef *pr)\r
10232 {\r
10233   HANDLE h;\r
10234   COMMTIMEOUTS ct;\r
10235   ChildProc *cp;\r
10236   char fullname[MSG_SIZ];\r
10237 \r
10238   if (*name != '\\')\r
10239     sprintf(fullname, "\\\\.\\%s", name);\r
10240   else\r
10241     strcpy(fullname, name);\r
10242 \r
10243   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10244                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10245   if (h == (HANDLE) -1) {\r
10246     return GetLastError();\r
10247   }\r
10248   hCommPort = h;\r
10249 \r
10250   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10251 \r
10252   /* Accumulate characters until a 100ms pause, then parse */\r
10253   ct.ReadIntervalTimeout = 100;\r
10254   ct.ReadTotalTimeoutMultiplier = 0;\r
10255   ct.ReadTotalTimeoutConstant = 0;\r
10256   ct.WriteTotalTimeoutMultiplier = 0;\r
10257   ct.WriteTotalTimeoutConstant = 0;\r
10258   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10259 \r
10260   /* Prepare return value */\r
10261   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10262   cp->kind = CPComm;\r
10263   cp->hFrom = h;\r
10264   cp->hTo = h;\r
10265   *pr = (ProcRef *) cp;\r
10266 \r
10267   return NO_ERROR;\r
10268 }\r
10269 \r
10270 int\r
10271 OpenLoopback(ProcRef *pr)\r
10272 {\r
10273   DisplayFatalError("Not implemented", 0, 1);\r
10274   return NO_ERROR;\r
10275 }\r
10276 \r
10277 \r
10278 int\r
10279 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10280 {\r
10281   ChildProc *cp;\r
10282   int err;\r
10283   SOCKET s, s2, s3;\r
10284   struct sockaddr_in sa, mysa;\r
10285   struct hostent FAR *hp;\r
10286   unsigned short uport;\r
10287   WORD wVersionRequested;\r
10288   WSADATA wsaData;\r
10289   int fromPort;\r
10290   char stderrPortStr[MSG_SIZ];\r
10291 \r
10292   /* Initialize socket DLL */\r
10293   wVersionRequested = MAKEWORD(1, 1);\r
10294   err = WSAStartup(wVersionRequested, &wsaData);\r
10295   if (err != 0) return err;\r
10296 \r
10297   /* Resolve remote host name */\r
10298   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10299   if (!(hp = gethostbyname(host))) {\r
10300     unsigned int b0, b1, b2, b3;\r
10301 \r
10302     err = WSAGetLastError();\r
10303 \r
10304     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10305       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10306       hp->h_addrtype = AF_INET;\r
10307       hp->h_length = 4;\r
10308       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10309       hp->h_addr_list[0] = (char *) malloc(4);\r
10310       hp->h_addr_list[0][0] = (char) b0;\r
10311       hp->h_addr_list[0][1] = (char) b1;\r
10312       hp->h_addr_list[0][2] = (char) b2;\r
10313       hp->h_addr_list[0][3] = (char) b3;\r
10314     } else {\r
10315       WSACleanup();\r
10316       return err;\r
10317     }\r
10318   }\r
10319   sa.sin_family = hp->h_addrtype;\r
10320   uport = (unsigned short) 514;\r
10321   sa.sin_port = htons(uport);\r
10322   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10323 \r
10324   /* Bind local socket to unused "privileged" port address\r
10325    */\r
10326   s = INVALID_SOCKET;\r
10327   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10328   mysa.sin_family = AF_INET;\r
10329   mysa.sin_addr.s_addr = INADDR_ANY;\r
10330   for (fromPort = 1023;; fromPort--) {\r
10331     if (fromPort < 0) {\r
10332       WSACleanup();\r
10333       return WSAEADDRINUSE;\r
10334     }\r
10335     if (s == INVALID_SOCKET) {\r
10336       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10337         err = WSAGetLastError();\r
10338         WSACleanup();\r
10339         return err;\r
10340       }\r
10341     }\r
10342     uport = (unsigned short) fromPort;\r
10343     mysa.sin_port = htons(uport);\r
10344     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10345         == SOCKET_ERROR) {\r
10346       err = WSAGetLastError();\r
10347       if (err == WSAEADDRINUSE) continue;\r
10348       WSACleanup();\r
10349       return err;\r
10350     }\r
10351     if (connect(s, (struct sockaddr *) &sa,\r
10352       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10353       err = WSAGetLastError();\r
10354       if (err == WSAEADDRINUSE) {\r
10355         closesocket(s);\r
10356         s = -1;\r
10357         continue;\r
10358       }\r
10359       WSACleanup();\r
10360       return err;\r
10361     }\r
10362     break;\r
10363   }\r
10364 \r
10365   /* Bind stderr local socket to unused "privileged" port address\r
10366    */\r
10367   s2 = INVALID_SOCKET;\r
10368   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10369   mysa.sin_family = AF_INET;\r
10370   mysa.sin_addr.s_addr = INADDR_ANY;\r
10371   for (fromPort = 1023;; fromPort--) {\r
10372     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10373     if (fromPort < 0) {\r
10374       (void) closesocket(s);\r
10375       WSACleanup();\r
10376       return WSAEADDRINUSE;\r
10377     }\r
10378     if (s2 == INVALID_SOCKET) {\r
10379       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10380         err = WSAGetLastError();\r
10381         closesocket(s);\r
10382         WSACleanup();\r
10383         return err;\r
10384       }\r
10385     }\r
10386     uport = (unsigned short) fromPort;\r
10387     mysa.sin_port = htons(uport);\r
10388     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10389         == SOCKET_ERROR) {\r
10390       err = WSAGetLastError();\r
10391       if (err == WSAEADDRINUSE) continue;\r
10392       (void) closesocket(s);\r
10393       WSACleanup();\r
10394       return err;\r
10395     }\r
10396     if (listen(s2, 1) == SOCKET_ERROR) {\r
10397       err = WSAGetLastError();\r
10398       if (err == WSAEADDRINUSE) {\r
10399         closesocket(s2);\r
10400         s2 = INVALID_SOCKET;\r
10401         continue;\r
10402       }\r
10403       (void) closesocket(s);\r
10404       (void) closesocket(s2);\r
10405       WSACleanup();\r
10406       return err;\r
10407     }\r
10408     break;\r
10409   }\r
10410   prevStderrPort = fromPort; // remember port used\r
10411   sprintf(stderrPortStr, "%d", fromPort);\r
10412 \r
10413   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10414     err = WSAGetLastError();\r
10415     (void) closesocket(s);\r
10416     (void) closesocket(s2);\r
10417     WSACleanup();\r
10418     return err;\r
10419   }\r
10420 \r
10421   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10422     err = WSAGetLastError();\r
10423     (void) closesocket(s);\r
10424     (void) closesocket(s2);\r
10425     WSACleanup();\r
10426     return err;\r
10427   }\r
10428   if (*user == NULLCHAR) user = UserName();\r
10429   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10430     err = WSAGetLastError();\r
10431     (void) closesocket(s);\r
10432     (void) closesocket(s2);\r
10433     WSACleanup();\r
10434     return err;\r
10435   }\r
10436   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10437     err = WSAGetLastError();\r
10438     (void) closesocket(s);\r
10439     (void) closesocket(s2);\r
10440     WSACleanup();\r
10441     return err;\r
10442   }\r
10443 \r
10444   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10445     err = WSAGetLastError();\r
10446     (void) closesocket(s);\r
10447     (void) closesocket(s2);\r
10448     WSACleanup();\r
10449     return err;\r
10450   }\r
10451   (void) closesocket(s2);  /* Stop listening */\r
10452 \r
10453   /* Prepare return value */\r
10454   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10455   cp->kind = CPRcmd;\r
10456   cp->sock = s;\r
10457   cp->sock2 = s3;\r
10458   *pr = (ProcRef *) cp;\r
10459 \r
10460   return NO_ERROR;\r
10461 }\r
10462 \r
10463 \r
10464 InputSourceRef\r
10465 AddInputSource(ProcRef pr, int lineByLine,\r
10466                InputCallback func, VOIDSTAR closure)\r
10467 {\r
10468   InputSource *is, *is2 = NULL;\r
10469   ChildProc *cp = (ChildProc *) pr;\r
10470 \r
10471   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10472   is->lineByLine = lineByLine;\r
10473   is->func = func;\r
10474   is->closure = closure;\r
10475   is->second = NULL;\r
10476   is->next = is->buf;\r
10477   if (pr == NoProc) {\r
10478     is->kind = CPReal;\r
10479     consoleInputSource = is;\r
10480   } else {\r
10481     is->kind = cp->kind;\r
10482     /* \r
10483         [AS] Try to avoid a race condition if the thread is given control too early:\r
10484         we create all threads suspended so that the is->hThread variable can be\r
10485         safely assigned, then let the threads start with ResumeThread.\r
10486     */\r
10487     switch (cp->kind) {\r
10488     case CPReal:\r
10489       is->hFile = cp->hFrom;\r
10490       cp->hFrom = NULL; /* now owned by InputThread */\r
10491       is->hThread =\r
10492         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10493                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10494       break;\r
10495 \r
10496     case CPComm:\r
10497       is->hFile = cp->hFrom;\r
10498       cp->hFrom = NULL; /* now owned by InputThread */\r
10499       is->hThread =\r
10500         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10501                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10502       break;\r
10503 \r
10504     case CPSock:\r
10505       is->sock = cp->sock;\r
10506       is->hThread =\r
10507         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10508                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10509       break;\r
10510 \r
10511     case CPRcmd:\r
10512       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10513       *is2 = *is;\r
10514       is->sock = cp->sock;\r
10515       is->second = is2;\r
10516       is2->sock = cp->sock2;\r
10517       is2->second = is2;\r
10518       is->hThread =\r
10519         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10520                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10521       is2->hThread =\r
10522         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10523                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10524       break;\r
10525     }\r
10526 \r
10527     if( is->hThread != NULL ) {\r
10528         ResumeThread( is->hThread );\r
10529     }\r
10530 \r
10531     if( is2 != NULL && is2->hThread != NULL ) {\r
10532         ResumeThread( is2->hThread );\r
10533     }\r
10534   }\r
10535 \r
10536   return (InputSourceRef) is;\r
10537 }\r
10538 \r
10539 void\r
10540 RemoveInputSource(InputSourceRef isr)\r
10541 {\r
10542   InputSource *is;\r
10543 \r
10544   is = (InputSource *) isr;\r
10545   is->hThread = NULL;  /* tell thread to stop */\r
10546   CloseHandle(is->hThread);\r
10547   if (is->second != NULL) {\r
10548     is->second->hThread = NULL;\r
10549     CloseHandle(is->second->hThread);\r
10550   }\r
10551 }\r
10552 \r
10553 \r
10554 int\r
10555 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10556 {\r
10557   DWORD dOutCount;\r
10558   int outCount = SOCKET_ERROR;\r
10559   ChildProc *cp = (ChildProc *) pr;\r
10560   static OVERLAPPED ovl;\r
10561 \r
10562   if (pr == NoProc) {\r
10563     ConsoleOutput(message, count, FALSE);\r
10564     return count;\r
10565   } \r
10566 \r
10567   if (ovl.hEvent == NULL) {\r
10568     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10569   }\r
10570   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10571 \r
10572   switch (cp->kind) {\r
10573   case CPSock:\r
10574   case CPRcmd:\r
10575     outCount = send(cp->sock, message, count, 0);\r
10576     if (outCount == SOCKET_ERROR) {\r
10577       *outError = WSAGetLastError();\r
10578     } else {\r
10579       *outError = NO_ERROR;\r
10580     }\r
10581     break;\r
10582 \r
10583   case CPReal:\r
10584     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10585                   &dOutCount, NULL)) {\r
10586       *outError = NO_ERROR;\r
10587       outCount = (int) dOutCount;\r
10588     } else {\r
10589       *outError = GetLastError();\r
10590     }\r
10591     break;\r
10592 \r
10593   case CPComm:\r
10594     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10595                             &dOutCount, &ovl);\r
10596     if (*outError == NO_ERROR) {\r
10597       outCount = (int) dOutCount;\r
10598     }\r
10599     break;\r
10600   }\r
10601   return outCount;\r
10602 }\r
10603 \r
10604 int\r
10605 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10606                        long msdelay)\r
10607 {\r
10608   /* Ignore delay, not implemented for WinBoard */\r
10609   return OutputToProcess(pr, message, count, outError);\r
10610 }\r
10611 \r
10612 \r
10613 void\r
10614 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10615                         char *buf, int count, int error)\r
10616 {\r
10617   DisplayFatalError("Not implemented", 0, 1);\r
10618 }\r
10619 \r
10620 /* see wgamelist.c for Game List functions */\r
10621 /* see wedittags.c for Edit Tags functions */\r
10622 \r
10623 \r
10624 VOID\r
10625 ICSInitScript()\r
10626 {\r
10627   FILE *f;\r
10628   char buf[MSG_SIZ];\r
10629   char *dummy;\r
10630 \r
10631   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10632     f = fopen(buf, "r");\r
10633     if (f != NULL) {\r
10634       ProcessICSInitScript(f);\r
10635       fclose(f);\r
10636     }\r
10637   }\r
10638 }\r
10639 \r
10640 \r
10641 VOID\r
10642 StartAnalysisClock()\r
10643 {\r
10644   if (analysisTimerEvent) return;\r
10645   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10646                                         (UINT) 2000, NULL);\r
10647 }\r
10648 \r
10649 LRESULT CALLBACK\r
10650 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10651 {\r
10652   static HANDLE hwndText;\r
10653   RECT rect;\r
10654   static int sizeX, sizeY;\r
10655   int newSizeX, newSizeY, flags;\r
10656   MINMAXINFO *mmi;\r
10657 \r
10658   switch (message) {\r
10659   case WM_INITDIALOG: /* message: initialize dialog box */\r
10660     /* Initialize the dialog items */\r
10661     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10662     SetWindowText(hDlg, analysisTitle);\r
10663     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10664     /* Size and position the dialog */\r
10665     if (!analysisDialog) {\r
10666       analysisDialog = hDlg;\r
10667       flags = SWP_NOZORDER;\r
10668       GetClientRect(hDlg, &rect);\r
10669       sizeX = rect.right;\r
10670       sizeY = rect.bottom;\r
10671       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10672           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10673         WINDOWPLACEMENT wp;\r
10674         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10675         wp.length = sizeof(WINDOWPLACEMENT);\r
10676         wp.flags = 0;\r
10677         wp.showCmd = SW_SHOW;\r
10678         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10679         wp.rcNormalPosition.left = analysisX;\r
10680         wp.rcNormalPosition.right = analysisX + analysisW;\r
10681         wp.rcNormalPosition.top = analysisY;\r
10682         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10683         SetWindowPlacement(hDlg, &wp);\r
10684 \r
10685         GetClientRect(hDlg, &rect);\r
10686         newSizeX = rect.right;\r
10687         newSizeY = rect.bottom;\r
10688         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10689                               newSizeX, newSizeY);\r
10690         sizeX = newSizeX;\r
10691         sizeY = newSizeY;\r
10692       }\r
10693     }\r
10694     return FALSE;\r
10695 \r
10696   case WM_COMMAND: /* message: received a command */\r
10697     switch (LOWORD(wParam)) {\r
10698     case IDCANCEL:\r
10699       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10700           ExitAnalyzeMode();\r
10701           ModeHighlight();\r
10702           return TRUE;\r
10703       }\r
10704       EditGameEvent();\r
10705       return TRUE;\r
10706     default:\r
10707       break;\r
10708     }\r
10709     break;\r
10710 \r
10711   case WM_SIZE:\r
10712     newSizeX = LOWORD(lParam);\r
10713     newSizeY = HIWORD(lParam);\r
10714     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10715     sizeX = newSizeX;\r
10716     sizeY = newSizeY;\r
10717     break;\r
10718 \r
10719   case WM_GETMINMAXINFO:\r
10720     /* Prevent resizing window too small */\r
10721     mmi = (MINMAXINFO *) lParam;\r
10722     mmi->ptMinTrackSize.x = 100;\r
10723     mmi->ptMinTrackSize.y = 100;\r
10724     break;\r
10725   }\r
10726   return FALSE;\r
10727 }\r
10728 \r
10729 VOID\r
10730 AnalysisPopUp(char* title, char* str)\r
10731 {\r
10732   FARPROC lpProc;\r
10733   char *p, *q;\r
10734 \r
10735   /* [AS] */\r
10736   EngineOutputPopUp();\r
10737   return;\r
10738 \r
10739   if (str == NULL) str = "";\r
10740   p = (char *) malloc(2 * strlen(str) + 2);\r
10741   q = p;\r
10742   while (*str) {\r
10743     if (*str == '\n') *q++ = '\r';\r
10744     *q++ = *str++;\r
10745   }\r
10746   *q = NULLCHAR;\r
10747   if (analysisText != NULL) free(analysisText);\r
10748   analysisText = p;\r
10749 \r
10750   if (analysisDialog) {\r
10751     SetWindowText(analysisDialog, title);\r
10752     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10753     ShowWindow(analysisDialog, SW_SHOW);\r
10754   } else {\r
10755     analysisTitle = title;\r
10756     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10757     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10758                  hwndMain, (DLGPROC)lpProc);\r
10759     FreeProcInstance(lpProc);\r
10760   }\r
10761   analysisDialogUp = TRUE;  \r
10762 }\r
10763 \r
10764 VOID\r
10765 AnalysisPopDown()\r
10766 {\r
10767   if (analysisDialog) {\r
10768     ShowWindow(analysisDialog, SW_HIDE);\r
10769   }\r
10770   analysisDialogUp = FALSE;  \r
10771 }\r
10772 \r
10773 \r
10774 VOID\r
10775 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10776 {\r
10777   highlightInfo.sq[0].x = fromX;\r
10778   highlightInfo.sq[0].y = fromY;\r
10779   highlightInfo.sq[1].x = toX;\r
10780   highlightInfo.sq[1].y = toY;\r
10781 }\r
10782 \r
10783 VOID\r
10784 ClearHighlights()\r
10785 {\r
10786   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10787     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10788 }\r
10789 \r
10790 VOID\r
10791 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10792 {\r
10793   premoveHighlightInfo.sq[0].x = fromX;\r
10794   premoveHighlightInfo.sq[0].y = fromY;\r
10795   premoveHighlightInfo.sq[1].x = toX;\r
10796   premoveHighlightInfo.sq[1].y = toY;\r
10797 }\r
10798 \r
10799 VOID\r
10800 ClearPremoveHighlights()\r
10801 {\r
10802   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10803     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10804 }\r
10805 \r
10806 VOID\r
10807 ShutDownFrontEnd()\r
10808 {\r
10809   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10810   DeleteClipboardTempFiles();\r
10811 }\r
10812 \r
10813 void\r
10814 BoardToTop()\r
10815 {\r
10816     if (IsIconic(hwndMain))\r
10817       ShowWindow(hwndMain, SW_RESTORE);\r
10818 \r
10819     SetActiveWindow(hwndMain);\r
10820 }\r
10821 \r
10822 /*\r
10823  * Prototypes for animation support routines\r
10824  */\r
10825 static void ScreenSquare(int column, int row, POINT * pt);\r
10826 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10827      POINT frames[], int * nFrames);\r
10828 \r
10829 \r
10830 void\r
10831 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10832 {       // [HGM] atomic: animate blast wave\r
10833         int i;\r
10834 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10835         explodeInfo.fromX = fromX;\r
10836         explodeInfo.fromY = fromY;\r
10837         explodeInfo.toX = toX;\r
10838         explodeInfo.toY = toY;\r
10839         for(i=1; i<nFrames; i++) {\r
10840             explodeInfo.radius = (i*180)/(nFrames-1);\r
10841             DrawPosition(FALSE, NULL);\r
10842             Sleep(appData.animSpeed);\r
10843         }\r
10844         explodeInfo.radius = 0;\r
10845         DrawPosition(TRUE, NULL);\r
10846 }\r
10847 \r
10848 #define kFactor 4\r
10849 \r
10850 void\r
10851 AnimateMove(board, fromX, fromY, toX, toY)\r
10852      Board board;\r
10853      int fromX;\r
10854      int fromY;\r
10855      int toX;\r
10856      int toY;\r
10857 {\r
10858   ChessSquare piece;\r
10859   POINT start, finish, mid;\r
10860   POINT frames[kFactor * 2 + 1];\r
10861   int nFrames, n;\r
10862 \r
10863   if (!appData.animate) return;\r
10864   if (doingSizing) return;\r
10865   if (fromY < 0 || fromX < 0) return;\r
10866   piece = board[fromY][fromX];\r
10867   if (piece >= EmptySquare) return;\r
10868 \r
10869   ScreenSquare(fromX, fromY, &start);\r
10870   ScreenSquare(toX, toY, &finish);\r
10871 \r
10872   /* All pieces except knights move in straight line */\r
10873   if (piece != WhiteKnight && piece != BlackKnight) {\r
10874     mid.x = start.x + (finish.x - start.x) / 2;\r
10875     mid.y = start.y + (finish.y - start.y) / 2;\r
10876   } else {\r
10877     /* Knight: make diagonal movement then straight */\r
10878     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10879        mid.x = start.x + (finish.x - start.x) / 2;\r
10880        mid.y = finish.y;\r
10881      } else {\r
10882        mid.x = finish.x;\r
10883        mid.y = start.y + (finish.y - start.y) / 2;\r
10884      }\r
10885   }\r
10886   \r
10887   /* Don't use as many frames for very short moves */\r
10888   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10889     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10890   else\r
10891     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10892 \r
10893   animInfo.from.x = fromX;\r
10894   animInfo.from.y = fromY;\r
10895   animInfo.to.x = toX;\r
10896   animInfo.to.y = toY;\r
10897   animInfo.lastpos = start;\r
10898   animInfo.piece = piece;\r
10899   for (n = 0; n < nFrames; n++) {\r
10900     animInfo.pos = frames[n];\r
10901     DrawPosition(FALSE, NULL);\r
10902     animInfo.lastpos = animInfo.pos;\r
10903     Sleep(appData.animSpeed);\r
10904   }\r
10905   animInfo.pos = finish;\r
10906   DrawPosition(FALSE, NULL);\r
10907   animInfo.piece = EmptySquare;\r
10908   if(gameInfo.variant == VariantAtomic && \r
10909      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10910         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10911 }\r
10912 \r
10913 /*      Convert board position to corner of screen rect and color       */\r
10914 \r
10915 static void\r
10916 ScreenSquare(column, row, pt)\r
10917      int column; int row; POINT * pt;\r
10918 {\r
10919   if (flipView) {\r
10920     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10921     pt->y = lineGap + row * (squareSize + lineGap);\r
10922   } else {\r
10923     pt->x = lineGap + column * (squareSize + lineGap);\r
10924     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10925   }\r
10926 }\r
10927 \r
10928 /*      Generate a series of frame coords from start->mid->finish.\r
10929         The movement rate doubles until the half way point is\r
10930         reached, then halves back down to the final destination,\r
10931         which gives a nice slow in/out effect. The algorithmn\r
10932         may seem to generate too many intermediates for short\r
10933         moves, but remember that the purpose is to attract the\r
10934         viewers attention to the piece about to be moved and\r
10935         then to where it ends up. Too few frames would be less\r
10936         noticeable.                                             */\r
10937 \r
10938 static void\r
10939 Tween(start, mid, finish, factor, frames, nFrames)\r
10940      POINT * start; POINT * mid;\r
10941      POINT * finish; int factor;\r
10942      POINT frames[]; int * nFrames;\r
10943 {\r
10944   int n, fraction = 1, count = 0;\r
10945 \r
10946   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10947   for (n = 0; n < factor; n++)\r
10948     fraction *= 2;\r
10949   for (n = 0; n < factor; n++) {\r
10950     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10951     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10952     count ++;\r
10953     fraction = fraction / 2;\r
10954   }\r
10955   \r
10956   /* Midpoint */\r
10957   frames[count] = *mid;\r
10958   count ++;\r
10959   \r
10960   /* Slow out, stepping 1/2, then 1/4, ... */\r
10961   fraction = 2;\r
10962   for (n = 0; n < factor; n++) {\r
10963     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10964     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10965     count ++;\r
10966     fraction = fraction * 2;\r
10967   }\r
10968   *nFrames = count;\r
10969 }\r
10970 \r
10971 void\r
10972 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10973 {\r
10974 #if 0\r
10975     char buf[256];\r
10976 \r
10977     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10978         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10979 \r
10980     OutputDebugString( buf );\r
10981 #endif\r
10982 \r
10983     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10984 \r
10985     EvalGraphSet( first, last, current, pvInfoList );\r
10986 }\r
10987 \r
10988 void SetProgramStats( FrontEndProgramStats * stats )\r
10989 {\r
10990 #if 0\r
10991     char buf[1024];\r
10992 \r
10993     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10994         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10995 \r
10996     OutputDebugString( buf );\r
10997 #endif\r
10998 \r
10999     EngineOutputUpdate( stats );\r
11000 }\r