added JAWS support; help files for Vista; bugfixes for winboard.c
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
6  * Massachusetts.  Enhancements Copyright\r
7  * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software\r
8  * Foundation, Inc.\r
9  *\r
10  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
11  * which was written and is copyrighted by Wayne Christopher.\r
12  *\r
13  * The following terms apply to Digital Equipment Corporation's copyright\r
14  * interest in XBoard:\r
15  * ------------------------------------------------------------------------\r
16  * All Rights Reserved\r
17  *\r
18  * Permission to use, copy, modify, and distribute this software and its\r
19  * documentation for any purpose and without fee is hereby granted,\r
20  * provided that the above copyright notice appear in all copies and that\r
21  * both that copyright notice and this permission notice appear in\r
22  * supporting documentation, and that the name of Digital not be\r
23  * used in advertising or publicity pertaining to distribution of the\r
24  * software without specific, written prior permission.\r
25  *\r
26  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
27  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
28  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
29  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
30  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
31  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
32  * SOFTWARE.\r
33  * ------------------------------------------------------------------------\r
34  *\r
35  * The following terms apply to the enhanced version of XBoard\r
36  * distributed by the Free Software Foundation:\r
37  * ------------------------------------------------------------------------\r
38  *\r
39  * GNU XBoard is free software: you can redistribute it and/or modify\r
40  * it under the terms of the GNU General Public License as published by\r
41  * the Free Software Foundation, either version 3 of the License, or (at\r
42  * your option) any later version.\r
43  *\r
44  * GNU XBoard is distributed in the hope that it will be useful, but\r
45  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
47  * General Public License for more details.\r
48  *\r
49  * You should have received a copy of the GNU General Public License\r
50  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
51  *\r
52  *------------------------------------------------------------------------\r
53  ** See the file ChangeLog for a revision history.  */\r
54 \r
55 #include "config.h"\r
56 \r
57 #include <windows.h>\r
58 #include <winuser.h>\r
59 #include <winsock.h>\r
60 #include <commctrl.h>\r
61 \r
62 #include <stdio.h>\r
63 #include <stdlib.h>\r
64 #include <time.h>\r
65 #include <malloc.h>\r
66 #include <sys/stat.h>\r
67 #include <fcntl.h>\r
68 #include <math.h>\r
69 #include <commdlg.h>\r
70 #include <dlgs.h>\r
71 #include <richedit.h>\r
72 #include <mmsystem.h>\r
73 #include <ctype.h>\r
74 \r
75 #if __GNUC__\r
76 #include <errno.h>\r
77 #include <string.h>\r
78 #endif\r
79 \r
80 #include "common.h"\r
81 #include "winboard.h"\r
82 #include "frontend.h"\r
83 #include "backend.h"\r
84 #include "moves.h"\r
85 #include "wclipbrd.h"\r
86 #include "wgamelist.h"\r
87 #include "wedittags.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 \r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 \r
102 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
103 VOID NewVariantPopup(HWND hwnd);\r
104 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
105                    /*char*/int promoChar));\r
106 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
107 void DisplayMove P((int moveNumber));\r
108 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
109 HWND WINAPI HtmlHelp( HWND hwnd, LPCSTR helpFile, UINT action, DWORD_PTR data );\r
110 \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 6\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_DELETE(X) X\r
501 #define SAYMACHINEMOVE()\r
502 #define SAY(X)\r
503 #endif\r
504 \r
505 /*---------------------------------------------------------------------------*\\r
506  *\r
507  * WinMain\r
508  *\r
509 \*---------------------------------------------------------------------------*/\r
510 \r
511 int APIENTRY\r
512 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
513         LPSTR lpCmdLine, int nCmdShow)\r
514 {\r
515   MSG msg;\r
516   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
517 //  INITCOMMONCONTROLSEX ex;\r
518 \r
519   debugFP = stderr;\r
520 \r
521   LoadLibrary("RICHED32.DLL");\r
522   consoleCF.cbSize = sizeof(CHARFORMAT);\r
523 \r
524   if (!InitApplication(hInstance)) {\r
525     return (FALSE);\r
526   }\r
527   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
528     return (FALSE);\r
529   }\r
530 \r
531   JAWS_INIT\r
532 \r
533 //  InitCommonControlsEx(&ex);\r
534   InitCommonControls();\r
535 \r
536   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
537   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
538   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
539 \r
540   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
541 \r
542   while (GetMessage(&msg, /* message structure */\r
543                     NULL, /* handle of window receiving the message */\r
544                     0,    /* lowest message to examine */\r
545                     0))   /* highest message to examine */\r
546     {\r
547 \r
548       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
549         // [HGM] navigate: switch between all windows with tab\r
550         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
551         int i, currentElement = 0;\r
552 \r
553         // first determine what element of the chain we come from (if any)\r
554         if(appData.icsActive) {\r
555             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
556             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
557         }\r
558         if(engineOutputDialog && EngineOutputIsUp()) {\r
559             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
560             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
561         }\r
562         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
563             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
564         }\r
565         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
566         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
567         if(msg.hwnd == e1)                 currentElement = 2; else\r
568         if(msg.hwnd == e2)                 currentElement = 3; else\r
569         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
570         if(msg.hwnd == mh)                currentElement = 4; else\r
571         if(msg.hwnd == evalGraphDialog)    currentElement = 7; else\r
572         if(msg.hwnd == hText)  currentElement = 5; else\r
573         if(msg.hwnd == hInput) currentElement = 6; else\r
574         for (i = 0; i < N_BUTTONS; i++) {\r
575             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
576         }\r
577 \r
578         // determine where to go to\r
579         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
580           do {\r
581             currentElement = (currentElement + direction) % 7;\r
582             switch(currentElement) {\r
583                 case 0:\r
584                   h = hwndMain; break; // passing this case always makes the loop exit\r
585                 case 1:\r
586                   h = buttonDesc[0].hwnd; break; // could be NULL\r
587                 case 2:\r
588                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
589                   h = e1; break;\r
590                 case 3:\r
591                   if(!EngineOutputIsUp()) continue;\r
592                   h = e2; break;\r
593                 case 4:\r
594                   if(!MoveHistoryIsUp()) continue;\r
595                   h = mh; break;\r
596 //              case 5: // input to eval graph does not seem to get here!\r
597 //                if(!EvalGraphIsUp()) continue;\r
598 //                h = evalGraphDialog; break;\r
599                 case 5:\r
600                   if(!appData.icsActive) continue;\r
601                   SAY("display");\r
602                   h = hText; break;\r
603                 case 6:\r
604                   if(!appData.icsActive) continue;\r
605                   SAY("input");\r
606                   h = hInput; break;\r
607             }\r
608           } while(h == 0);\r
609 \r
610           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
611           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
612           SetFocus(h);\r
613 \r
614           continue; // this message now has been processed\r
615         }\r
616       }\r
617 \r
618       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
619           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
620           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
621           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
622           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
623           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
624           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
625           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
626           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
627           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
628         TranslateMessage(&msg); /* Translates virtual key codes */\r
629         DispatchMessage(&msg);  /* Dispatches message to window */\r
630       }\r
631     }\r
632 \r
633 \r
634   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
635 }\r
636 \r
637 /*---------------------------------------------------------------------------*\\r
638  *\r
639  * Initialization functions\r
640  *\r
641 \*---------------------------------------------------------------------------*/\r
642 \r
643 void\r
644 SetUserLogo()\r
645 {   // update user logo if necessary\r
646     static char oldUserName[MSG_SIZ], *curName;\r
647 \r
648     if(appData.autoLogo) {\r
649           curName = UserName();\r
650           if(strcmp(curName, oldUserName)) {\r
651                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
652                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
653                 strcpy(oldUserName, curName);\r
654           }\r
655     }\r
656 }\r
657 \r
658 BOOL\r
659 InitApplication(HINSTANCE hInstance)\r
660 {\r
661   WNDCLASS wc;\r
662 \r
663   /* Fill in window class structure with parameters that describe the */\r
664   /* main window. */\r
665 \r
666   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
667   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
668   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
669   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
670   wc.hInstance     = hInstance;         /* Owner of this class */\r
671   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
672   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
673   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
674   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
675   wc.lpszClassName = szAppName;                 /* Name to register as */\r
676 \r
677   /* Register the window class and return success/failure code. */\r
678   if (!RegisterClass(&wc)) return FALSE;\r
679 \r
680   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
681   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
682   wc.cbClsExtra    = 0;\r
683   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
684   wc.hInstance     = hInstance;\r
685   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
686   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
687   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
688   wc.lpszMenuName  = NULL;\r
689   wc.lpszClassName = szConsoleName;\r
690 \r
691   if (!RegisterClass(&wc)) return FALSE;\r
692   return TRUE;\r
693 }\r
694 \r
695 \r
696 /* Set by InitInstance, used by EnsureOnScreen */\r
697 int screenHeight, screenWidth;\r
698 \r
699 void\r
700 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
701 {\r
702 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
703   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
704   if (*x > screenWidth - 32) *x = 0;\r
705   if (*y > screenHeight - 32) *y = 0;\r
706   if (*x < minX) *x = minX;\r
707   if (*y < minY) *y = minY;\r
708 }\r
709 \r
710 BOOL\r
711 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
712 {\r
713   HWND hwnd; /* Main window handle. */\r
714   int ibs;\r
715   WINDOWPLACEMENT wp;\r
716   char *filepart;\r
717 \r
718   hInst = hInstance;    /* Store instance handle in our global variable */\r
719 \r
720   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
721     *filepart = NULLCHAR;\r
722   } else {\r
723     GetCurrentDirectory(MSG_SIZ, installDir);\r
724   }\r
725   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
726   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
727   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
728   if (appData.debugMode) {\r
729     debugFP = fopen(appData.nameOfDebugFile, "w");\r
730     setbuf(debugFP, NULL);\r
731   }\r
732 \r
733   InitBackEnd1();\r
734 \r
735 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
736 //  InitEngineUCI( installDir, &second );\r
737 \r
738   /* Create a main window for this application instance. */\r
739   hwnd = CreateWindow(szAppName, szTitle,\r
740                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
741                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
742                       NULL, NULL, hInstance, NULL);\r
743   hwndMain = hwnd;\r
744 \r
745   /* If window could not be created, return "failure" */\r
746   if (!hwnd) {\r
747     return (FALSE);\r
748   }\r
749 \r
750   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
751   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
752       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
753 \r
754       if (first.programLogo == NULL && appData.debugMode) {\r
755           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
756       }\r
757   } else if(appData.autoLogo) {\r
758       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
759         char buf[MSG_SIZ];\r
760         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
761         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
762       }\r
763   }\r
764 \r
765   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
766       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
767 \r
768       if (second.programLogo == NULL && appData.debugMode) {\r
769           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
770       }\r
771   } else if(appData.autoLogo) {\r
772       char buf[MSG_SIZ];\r
773       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
774         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
775         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
776       } else\r
777       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
778         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
779         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
780       }\r
781   }\r
782 \r
783   SetUserLogo();\r
784 \r
785   iconWhite = LoadIcon(hInstance, "icon_white");\r
786   iconBlack = LoadIcon(hInstance, "icon_black");\r
787   iconCurrent = iconWhite;\r
788   InitDrawingColors();\r
789   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
790   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
791   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
792     /* Compute window size for each board size, and use the largest\r
793        size that fits on this screen as the default. */\r
794     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
795     if (boardSize == (BoardSize)-1 &&\r
796         winH <= screenHeight\r
797            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
798         && winW <= screenWidth) {\r
799       boardSize = (BoardSize)ibs;\r
800     }\r
801   }\r
802 \r
803   InitDrawingSizes(boardSize, 0);\r
804   InitMenuChecks();\r
805   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
806 \r
807   /* [AS] Load textures if specified */\r
808   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
809   \r
810   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
811       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
812       liteBackTextureMode = appData.liteBackTextureMode;\r
813 \r
814       if (liteBackTexture == NULL && appData.debugMode) {\r
815           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
816       }\r
817   }\r
818   \r
819   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
820       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
821       darkBackTextureMode = appData.darkBackTextureMode;\r
822 \r
823       if (darkBackTexture == NULL && appData.debugMode) {\r
824           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
825       }\r
826   }\r
827 \r
828   mysrandom( (unsigned) time(NULL) );\r
829 \r
830   /* [AS] Restore layout */\r
831   if( wpMoveHistory.visible ) {\r
832       MoveHistoryPopUp();\r
833   }\r
834 \r
835   if( wpEvalGraph.visible ) {\r
836       EvalGraphPopUp();\r
837   }\r
838 \r
839   if( wpEngineOutput.visible ) {\r
840       EngineOutputPopUp();\r
841   }\r
842 \r
843   InitBackEnd2();\r
844 \r
845   /* Make the window visible; update its client area; and return "success" */\r
846   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
847   wp.length = sizeof(WINDOWPLACEMENT);\r
848   wp.flags = 0;\r
849   wp.showCmd = nCmdShow;\r
850   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
851   wp.rcNormalPosition.left = boardX;\r
852   wp.rcNormalPosition.right = boardX + winWidth;\r
853   wp.rcNormalPosition.top = boardY;\r
854   wp.rcNormalPosition.bottom = boardY + winHeight;\r
855   SetWindowPlacement(hwndMain, &wp);\r
856 \r
857   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
858                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
859 \r
860   if (hwndConsole) {\r
861 #if AOT_CONSOLE\r
862     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
863                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
864 #endif\r
865     ShowWindow(hwndConsole, nCmdShow);\r
866   }\r
867   UpdateWindow(hwnd);\r
868 \r
869   return TRUE;\r
870 \r
871 }\r
872 \r
873 \r
874 typedef enum {\r
875   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
876   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
877   ArgSettingsFilename,\r
878   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
879 } ArgType;\r
880 \r
881 typedef struct {\r
882   char *argName;\r
883   ArgType argType;\r
884   /***\r
885   union {\r
886     String *pString;       // ArgString\r
887     int *pInt;             // ArgInt\r
888     float *pFloat;         // ArgFloat\r
889     Boolean *pBoolean;     // ArgBoolean\r
890     COLORREF *pColor;      // ArgColor\r
891     ColorClass cc;         // ArgAttribs\r
892     String *pFilename;     // ArgFilename\r
893     BoardSize *pBoardSize; // ArgBoardSize\r
894     int whichFont;         // ArgFont\r
895     DCB *pDCB;             // ArgCommSettings\r
896     String *pFilename;     // ArgSettingsFilename\r
897   } argLoc;\r
898   ***/\r
899   LPVOID argLoc;\r
900   BOOL save;\r
901 } ArgDescriptor;\r
902 \r
903 int junk;\r
904 ArgDescriptor argDescriptors[] = {\r
905   /* positional arguments */\r
906   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
907   { "", ArgNone, NULL },\r
908   /* keyword arguments */\r
909   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
910   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
911   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
912   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
913   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
914   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
915   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
916   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
917   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
918   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
919   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
920   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
921   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
922   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
923   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
924   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
925   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
926   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
927     FALSE },\r
928   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
929     FALSE },\r
930   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
931     FALSE },\r
932   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
933   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
934     FALSE },\r
935   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
936   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
937   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
938   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
939   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
940   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
941   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
942   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
943   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
944   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
945   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
946   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
947   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
948   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
949   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
950   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
951   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
952   /*!!bitmapDirectory?*/\r
953   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
954   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
955   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
956   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
957   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
958   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
959   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
960   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
961   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
962   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
963   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
964   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
965   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
966   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
967   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
968   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
969   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
970   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
971   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
972   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
973   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
974   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
975   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
976   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
977   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
978   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
979   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
980   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
981   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
982   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
983   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
984   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
985   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
986   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
987   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
988   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
989   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
990   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
991   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
992   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
993   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
994   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
995   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
996   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
997   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
998   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
999   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1000   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
1001   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1002   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1003   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1004   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1005   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1006   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1007   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1008   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1009   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1010   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1011   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1012   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1013   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1014   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1015   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1016   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1017   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1018   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1019   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1020   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1021   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1022   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1023   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1024   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1025   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1026   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1027   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1028   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1029   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1030   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1031   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1032   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1033   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1034   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1035   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1036   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1037   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1038   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1039   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1040   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1041   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1042   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1043   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1044   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1045   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1046   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1047     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1048   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1049   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1050   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1051   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1052   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1053   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1054   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1055   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1056     TRUE }, /* must come after all fonts */\r
1057   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1058   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1059     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1060   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1061   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1062   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1063   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1064   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1065   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1066   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1067   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1068   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1069   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1070   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1071   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1072   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1073   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1074   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1075   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1076   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1077   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1078   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1079   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1080   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1081   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1082   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1083   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1084   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1085   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1086   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1087   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1088 #if 0\r
1089   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1090   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1091 #endif\r
1092   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1093   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1094   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1095   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1096   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1097   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1098   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1099   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1100   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1101   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1102   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1103   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1104   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1105   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1106   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1107   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1108   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1109   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1110   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1111   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1112   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1113   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1114   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1115   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1116   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1117   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1118   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1119   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1120   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1121   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1122   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1123   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1124   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1125   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1126   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1127   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1128   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1129   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1130   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1131   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1132   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1133   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1134   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1135   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1136   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1137   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1138   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1139   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1140   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1141   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1142   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1143   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1144   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1145   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1146   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1147   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1148   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1149   { "highlightLastMove", ArgBoolean,\r
1150     (LPVOID) &appData.highlightLastMove, TRUE },\r
1151   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1152   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1153   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1154   { "highlightDragging", ArgBoolean,\r
1155     (LPVOID) &appData.highlightDragging, TRUE },\r
1156   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1157   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1158   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1159   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1160   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1161   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1162   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1163   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1164   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1165   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1166   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1167   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1168   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1169   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1170   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1171   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1172   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1173   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1174   { "soundShout", ArgFilename,\r
1175     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1176   { "soundSShout", ArgFilename,\r
1177     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1178   { "soundChannel1", ArgFilename,\r
1179     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1180   { "soundChannel", ArgFilename,\r
1181     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1182   { "soundKibitz", ArgFilename,\r
1183     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1184   { "soundTell", ArgFilename,\r
1185     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1186   { "soundChallenge", ArgFilename,\r
1187     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1188   { "soundRequest", ArgFilename,\r
1189     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1190   { "soundSeek", ArgFilename,\r
1191     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1192   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1193   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1194   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1195   { "soundIcsLoss", ArgFilename, \r
1196     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1197   { "soundIcsDraw", ArgFilename, \r
1198     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1199   { "soundIcsUnfinished", ArgFilename, \r
1200     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1201   { "soundIcsAlarm", ArgFilename, \r
1202     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1203   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1204   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1205   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1206   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1207   { "reuseChessPrograms", ArgBoolean,\r
1208     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1209   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1210   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1211   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1212   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1213   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1214   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1215   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1216   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1217   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1218   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1219   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1220   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1221   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1222   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1223   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1224     TRUE },\r
1225   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1226     TRUE },\r
1227   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1228   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1229   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1230   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1231   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1232   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1233   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1234   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1235   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1236   /* [AS] New features */\r
1237   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1238   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1239   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1240   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1241   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1242   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1243   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1244   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1245   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1246   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1247   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1248   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1249   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1250   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1251   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1252   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1253   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1254   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1255   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1256   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1257   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1258   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1259   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1260   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1261   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1262   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1263   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1264   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1265   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1266   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1267   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1268   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1269   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1270   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1271   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1272   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1273   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1274   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1275   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1276   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1277   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1278   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1279   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1280   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1281   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1282   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1283   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1284   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1285   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1286   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1287 \r
1288   /* [HGM] board-size, adjudication and misc. options */\r
1289   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1290   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1291   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1292   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1293   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1294   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1295   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1296   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1297   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1298   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1299   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1300   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1301   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1302   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1303   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1304   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1305   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1306   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1307   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1308   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1309   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1310   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1311   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1312   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1313   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1314   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1315   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1316   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1317   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1318   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1319   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1320 \r
1321 #ifdef ZIPPY\r
1322   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1323   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1324   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1325   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1326   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1327   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1328   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1329   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1330   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1331   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1332   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1333   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1334   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1335     FALSE },\r
1336   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1337   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1338   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1339   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1340   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1341   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1342   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1343     FALSE },\r
1344   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1345   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1346   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1347   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1348   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1349   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1350   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1351   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1352   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1353   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1354   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1355   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1356   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1357   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1358   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1359   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1360   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1361   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1362   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1363 #endif\r
1364   /* [HGM] options for broadcasting and time odds */\r
1365   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1366   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1367   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1368   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1369   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1370   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1371   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1372   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1373   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1374   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1375   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1376 \r
1377   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1378   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1379   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1380   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1381   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1382   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1383   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1384   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1385   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1386   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1387   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1388   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1389   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1390   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1391   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1392   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1393   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1394   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1395   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1396   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1397   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1398   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1399   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1400   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1401   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1402   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1403   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1404   /* [AS] Layout stuff */\r
1405   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1406   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1407   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1408   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1409   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1410 \r
1411   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1412   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1413   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1414   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1415   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1416 \r
1417   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1418   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1419   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1420   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1421   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1422 \r
1423   { NULL, ArgNone, NULL, FALSE }\r
1424 };\r
1425 \r
1426 \r
1427 /* Kludge for indirection files on command line */\r
1428 char* lastIndirectionFilename;\r
1429 ArgDescriptor argDescriptorIndirection =\r
1430 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1431 \r
1432 \r
1433 VOID\r
1434 ExitArgError(char *msg, char *badArg)\r
1435 {\r
1436   char buf[MSG_SIZ];\r
1437 \r
1438   sprintf(buf, "%s %s", msg, badArg);\r
1439   DisplayFatalError(buf, 0, 2);\r
1440   exit(2);\r
1441 }\r
1442 \r
1443 /* Command line font name parser.  NULL name means do nothing.\r
1444    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1445    For backward compatibility, syntax without the colon is also\r
1446    accepted, but font names with digits in them won't work in that case.\r
1447 */\r
1448 VOID\r
1449 ParseFontName(char *name, MyFontParams *mfp)\r
1450 {\r
1451   char *p, *q;\r
1452   if (name == NULL) return;\r
1453   p = name;\r
1454   q = strchr(p, ':');\r
1455   if (q) {\r
1456     if (q - p >= sizeof(mfp->faceName))\r
1457       ExitArgError("Font name too long:", name);\r
1458     memcpy(mfp->faceName, p, q - p);\r
1459     mfp->faceName[q - p] = NULLCHAR;\r
1460     p = q + 1;\r
1461   } else {\r
1462     q = mfp->faceName;\r
1463     while (*p && !isdigit(*p)) {\r
1464       *q++ = *p++;\r
1465       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1466         ExitArgError("Font name too long:", name);\r
1467     }\r
1468     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1469     *q = NULLCHAR;\r
1470   }\r
1471   if (!*p) ExitArgError("Font point size missing:", name);\r
1472   mfp->pointSize = (float) atof(p);\r
1473   mfp->bold = (strchr(p, 'b') != NULL);\r
1474   mfp->italic = (strchr(p, 'i') != NULL);\r
1475   mfp->underline = (strchr(p, 'u') != NULL);\r
1476   mfp->strikeout = (strchr(p, 's') != NULL);\r
1477 }\r
1478 \r
1479 /* Color name parser.\r
1480    X version accepts X color names, but this one\r
1481    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1482 COLORREF\r
1483 ParseColorName(char *name)\r
1484 {\r
1485   int red, green, blue, count;\r
1486   char buf[MSG_SIZ];\r
1487 \r
1488   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1489   if (count != 3) {\r
1490     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1491       &red, &green, &blue);\r
1492   }\r
1493   if (count != 3) {\r
1494     sprintf(buf, "Can't parse color name %s", name);\r
1495     DisplayError(buf, 0);\r
1496     return RGB(0, 0, 0);\r
1497   }\r
1498   return PALETTERGB(red, green, blue);\r
1499 }\r
1500 \r
1501 \r
1502 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1503 {\r
1504   char *e = argValue;\r
1505   int eff = 0;\r
1506 \r
1507   while (*e) {\r
1508     if (*e == 'b')      eff |= CFE_BOLD;\r
1509     else if (*e == 'i') eff |= CFE_ITALIC;\r
1510     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1511     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1512     else if (*e == '#' || isdigit(*e)) break;\r
1513     e++;\r
1514   }\r
1515   *effects = eff;\r
1516   *color   = ParseColorName(e);\r
1517 }\r
1518 \r
1519 \r
1520 BoardSize\r
1521 ParseBoardSize(char *name)\r
1522 {\r
1523   BoardSize bs = SizeTiny;\r
1524   while (sizeInfo[bs].name != NULL) {\r
1525     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1526     bs++;\r
1527   }\r
1528   ExitArgError("Unrecognized board size value", name);\r
1529   return bs; /* not reached */\r
1530 }\r
1531 \r
1532 \r
1533 char\r
1534 StringGet(void *getClosure)\r
1535 {\r
1536   char **p = (char **) getClosure;\r
1537   return *((*p)++);\r
1538 }\r
1539 \r
1540 char\r
1541 FileGet(void *getClosure)\r
1542 {\r
1543   int c;\r
1544   FILE* f = (FILE*) getClosure;\r
1545 \r
1546   c = getc(f);\r
1547   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1548   if (c == EOF)\r
1549     return NULLCHAR;\r
1550   else\r
1551     return (char) c;\r
1552 }\r
1553 \r
1554 /* Parse settings file named "name". If file found, return the\r
1555    full name in fullname and return TRUE; else return FALSE */\r
1556 BOOLEAN\r
1557 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1558 {\r
1559   char *dummy;\r
1560   FILE *f;\r
1561   int ok; char buf[MSG_SIZ];\r
1562 \r
1563   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1564   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1565     sprintf(buf, "%s.ini", name);\r
1566     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1567   }\r
1568   if (ok) {\r
1569     f = fopen(fullname, "r");\r
1570     if (f != NULL) {\r
1571       ParseArgs(FileGet, f);\r
1572       fclose(f);\r
1573       return TRUE;\r
1574     }\r
1575   }\r
1576   return FALSE;\r
1577 }\r
1578 \r
1579 VOID\r
1580 ParseArgs(GetFunc get, void *cl)\r
1581 {\r
1582   char argName[ARG_MAX];\r
1583   char argValue[ARG_MAX];\r
1584   ArgDescriptor *ad;\r
1585   char start;\r
1586   char *q;\r
1587   int i, octval;\r
1588   char ch;\r
1589   int posarg = 0;\r
1590 \r
1591   ch = get(cl);\r
1592   for (;;) {\r
1593     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1594     if (ch == NULLCHAR) break;\r
1595     if (ch == ';') {\r
1596       /* Comment to end of line */\r
1597       ch = get(cl);\r
1598       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1599       continue;\r
1600     } else if (ch == '/' || ch == '-') {\r
1601       /* Switch */\r
1602       q = argName;\r
1603       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1604              ch != '\n' && ch != '\t') {\r
1605         *q++ = ch;\r
1606         ch = get(cl);\r
1607       }\r
1608       *q = NULLCHAR;\r
1609 \r
1610       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1611         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1612 \r
1613       if (ad->argName == NULL)\r
1614         ExitArgError("Unrecognized argument", argName);\r
1615 \r
1616     } else if (ch == '@') {\r
1617       /* Indirection file */\r
1618       ad = &argDescriptorIndirection;\r
1619       ch = get(cl);\r
1620     } else {\r
1621       /* Positional argument */\r
1622       ad = &argDescriptors[posarg++];\r
1623       strcpy(argName, ad->argName);\r
1624     }\r
1625 \r
1626     if (ad->argType == ArgTrue) {\r
1627       *(Boolean *) ad->argLoc = TRUE;\r
1628       continue;\r
1629     }\r
1630     if (ad->argType == ArgFalse) {\r
1631       *(Boolean *) ad->argLoc = FALSE;\r
1632       continue;\r
1633     }\r
1634 \r
1635     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1636     if (ch == NULLCHAR || ch == '\n') {\r
1637       ExitArgError("No value provided for argument", argName);\r
1638     }\r
1639     q = argValue;\r
1640     if (ch == '{') {\r
1641       // Quoting with { }.  No characters have to (or can) be escaped.\r
1642       // Thus the string cannot contain a '}' character.\r
1643       start = ch;\r
1644       ch = get(cl);\r
1645       while (start) {\r
1646         switch (ch) {\r
1647         case NULLCHAR:\r
1648           start = NULLCHAR;\r
1649           break;\r
1650           \r
1651         case '}':\r
1652           ch = get(cl);\r
1653           start = NULLCHAR;\r
1654           break;\r
1655 \r
1656         default:\r
1657           *q++ = ch;\r
1658           ch = get(cl);\r
1659           break;\r
1660         }\r
1661       }   \r
1662     } else if (ch == '\'' || ch == '"') {\r
1663       // Quoting with ' ' or " ", with \ as escape character.\r
1664       // Inconvenient for long strings that may contain Windows filenames.\r
1665       start = ch;\r
1666       ch = get(cl);\r
1667       while (start) {\r
1668         switch (ch) {\r
1669         case NULLCHAR:\r
1670           start = NULLCHAR;\r
1671           break;\r
1672 \r
1673         default:\r
1674         not_special:\r
1675           *q++ = ch;\r
1676           ch = get(cl);\r
1677           break;\r
1678 \r
1679         case '\'':\r
1680         case '\"':\r
1681           if (ch == start) {\r
1682             ch = get(cl);\r
1683             start = NULLCHAR;\r
1684             break;\r
1685           } else {\r
1686             goto not_special;\r
1687           }\r
1688 \r
1689         case '\\':\r
1690           if (ad->argType == ArgFilename\r
1691               || ad->argType == ArgSettingsFilename) {\r
1692               goto not_special;\r
1693           }\r
1694           ch = get(cl);\r
1695           switch (ch) {\r
1696           case NULLCHAR:\r
1697             ExitArgError("Incomplete \\ escape in value for", argName);\r
1698             break;\r
1699           case 'n':\r
1700             *q++ = '\n';\r
1701             ch = get(cl);\r
1702             break;\r
1703           case 'r':\r
1704             *q++ = '\r';\r
1705             ch = get(cl);\r
1706             break;\r
1707           case 't':\r
1708             *q++ = '\t';\r
1709             ch = get(cl);\r
1710             break;\r
1711           case 'b':\r
1712             *q++ = '\b';\r
1713             ch = get(cl);\r
1714             break;\r
1715           case 'f':\r
1716             *q++ = '\f';\r
1717             ch = get(cl);\r
1718             break;\r
1719           default:\r
1720             octval = 0;\r
1721             for (i = 0; i < 3; i++) {\r
1722               if (ch >= '0' && ch <= '7') {\r
1723                 octval = octval*8 + (ch - '0');\r
1724                 ch = get(cl);\r
1725               } else {\r
1726                 break;\r
1727               }\r
1728             }\r
1729             if (i > 0) {\r
1730               *q++ = (char) octval;\r
1731             } else {\r
1732               *q++ = ch;\r
1733               ch = get(cl);\r
1734             }\r
1735             break;\r
1736           }\r
1737           break;\r
1738         }\r
1739       }\r
1740     } else {\r
1741       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1742         *q++ = ch;\r
1743         ch = get(cl);\r
1744       }\r
1745     }\r
1746     *q = NULLCHAR;\r
1747 \r
1748     switch (ad->argType) {\r
1749     case ArgInt:\r
1750       *(int *) ad->argLoc = atoi(argValue);\r
1751       break;\r
1752 \r
1753     case ArgX:\r
1754       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1755       break;\r
1756 \r
1757     case ArgY:\r
1758       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1759       break;\r
1760 \r
1761     case ArgZ:\r
1762       *(int *) ad->argLoc = atoi(argValue);\r
1763       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1764       break;\r
1765 \r
1766     case ArgFloat:\r
1767       *(float *) ad->argLoc = (float) atof(argValue);\r
1768       break;\r
1769 \r
1770     case ArgString:\r
1771     case ArgFilename:\r
1772       *(char **) ad->argLoc = strdup(argValue);\r
1773       break;\r
1774 \r
1775     case ArgSettingsFilename:\r
1776       {\r
1777         char fullname[MSG_SIZ];\r
1778         if (ParseSettingsFile(argValue, fullname)) {\r
1779           if (ad->argLoc != NULL) {\r
1780             *(char **) ad->argLoc = strdup(fullname);\r
1781           }\r
1782         } else {\r
1783           if (ad->argLoc != NULL) {\r
1784           } else {\r
1785             ExitArgError("Failed to open indirection file", argValue);\r
1786           }\r
1787         }\r
1788       }\r
1789       break;\r
1790 \r
1791     case ArgBoolean:\r
1792       switch (argValue[0]) {\r
1793       case 't':\r
1794       case 'T':\r
1795         *(Boolean *) ad->argLoc = TRUE;\r
1796         break;\r
1797       case 'f':\r
1798       case 'F':\r
1799         *(Boolean *) ad->argLoc = FALSE;\r
1800         break;\r
1801       default:\r
1802         ExitArgError("Unrecognized boolean argument value", argValue);\r
1803         break;\r
1804       }\r
1805       break;\r
1806 \r
1807     case ArgColor:\r
1808       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1809       break;\r
1810 \r
1811     case ArgAttribs: {\r
1812       ColorClass cc = (ColorClass)ad->argLoc;\r
1813       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1814       }\r
1815       break;\r
1816       \r
1817     case ArgBoardSize:\r
1818       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1819       break;\r
1820 \r
1821     case ArgFont:\r
1822       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1823       break;\r
1824 \r
1825     case ArgCommSettings:\r
1826       ParseCommSettings(argValue, &dcb);\r
1827       break;\r
1828 \r
1829     case ArgNone:\r
1830       ExitArgError("Unrecognized argument", argValue);\r
1831       break;\r
1832     case ArgTrue:\r
1833     case ArgFalse: ;\r
1834     }\r
1835   }\r
1836 }\r
1837 \r
1838 VOID\r
1839 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1840 {\r
1841   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1842   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1843   DeleteDC(hdc);\r
1844   lf->lfWidth = 0;\r
1845   lf->lfEscapement = 0;\r
1846   lf->lfOrientation = 0;\r
1847   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1848   lf->lfItalic = mfp->italic;\r
1849   lf->lfUnderline = mfp->underline;\r
1850   lf->lfStrikeOut = mfp->strikeout;\r
1851   lf->lfCharSet = DEFAULT_CHARSET;\r
1852   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1853   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1854   lf->lfQuality = DEFAULT_QUALITY;\r
1855   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1856   strcpy(lf->lfFaceName, mfp->faceName);\r
1857 }\r
1858 \r
1859 VOID\r
1860 CreateFontInMF(MyFont *mf)\r
1861 {\r
1862   LFfromMFP(&mf->lf, &mf->mfp);\r
1863   if (mf->hf) DeleteObject(mf->hf);\r
1864   mf->hf = CreateFontIndirect(&mf->lf);\r
1865 }\r
1866 \r
1867 VOID\r
1868 SetDefaultTextAttribs()\r
1869 {\r
1870   ColorClass cc;\r
1871   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1872     ParseAttribs(&textAttribs[cc].color, \r
1873                  &textAttribs[cc].effects, \r
1874                  defaultTextAttribs[cc]);\r
1875   }\r
1876 }\r
1877 \r
1878 VOID\r
1879 SetDefaultSounds()\r
1880 {\r
1881   ColorClass cc;\r
1882   SoundClass sc;\r
1883   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1884     textAttribs[cc].sound.name = strdup("");\r
1885     textAttribs[cc].sound.data = NULL;\r
1886   }\r
1887   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1888     sounds[sc].name = strdup("");\r
1889     sounds[sc].data = NULL;\r
1890   }\r
1891   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1892 }\r
1893 \r
1894 VOID\r
1895 LoadAllSounds()\r
1896 {\r
1897   ColorClass cc;\r
1898   SoundClass sc;\r
1899   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1900     MyLoadSound(&textAttribs[cc].sound);\r
1901   }\r
1902   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1903     MyLoadSound(&sounds[sc]);\r
1904   }\r
1905 }\r
1906 \r
1907 VOID\r
1908 InitAppData(LPSTR lpCmdLine)\r
1909 {\r
1910   int i, j;\r
1911   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1912   char *dummy, *p;\r
1913 \r
1914   programName = szAppName;\r
1915 \r
1916   /* Initialize to defaults */\r
1917   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1918   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1919   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1920   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1921   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1922   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1923   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1924   SetDefaultTextAttribs();\r
1925   SetDefaultSounds();\r
1926   appData.movesPerSession = MOVES_PER_SESSION;\r
1927   appData.initString = INIT_STRING;\r
1928   appData.secondInitString = INIT_STRING;\r
1929   appData.firstComputerString = COMPUTER_STRING;\r
1930   appData.secondComputerString = COMPUTER_STRING;\r
1931   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1932   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1933   appData.firstPlaysBlack = FALSE;\r
1934   appData.noChessProgram = FALSE;\r
1935   chessProgram = FALSE;\r
1936   appData.firstHost = FIRST_HOST;\r
1937   appData.secondHost = SECOND_HOST;\r
1938   appData.firstDirectory = FIRST_DIRECTORY;\r
1939   appData.secondDirectory = SECOND_DIRECTORY;\r
1940   appData.bitmapDirectory = "";\r
1941   appData.remoteShell = REMOTE_SHELL;\r
1942   appData.remoteUser = "";\r
1943   appData.timeDelay = TIME_DELAY;\r
1944   appData.timeControl = TIME_CONTROL;\r
1945   appData.timeIncrement = TIME_INCREMENT;\r
1946   appData.icsActive = FALSE;\r
1947   appData.icsHost = "";\r
1948   appData.icsPort = ICS_PORT;\r
1949   appData.icsCommPort = ICS_COMM_PORT;\r
1950   appData.icsLogon = ICS_LOGON;\r
1951   appData.icsHelper = "";\r
1952   appData.useTelnet = FALSE;\r
1953   appData.telnetProgram = TELNET_PROGRAM;\r
1954   appData.gateway = "";\r
1955   appData.loadGameFile = "";\r
1956   appData.loadGameIndex = 0;\r
1957   appData.saveGameFile = "";\r
1958   appData.autoSaveGames = FALSE;\r
1959   appData.loadPositionFile = "";\r
1960   appData.loadPositionIndex = 1;\r
1961   appData.savePositionFile = "";\r
1962   appData.matchMode = FALSE;\r
1963   appData.matchGames = 0;\r
1964   appData.monoMode = FALSE;\r
1965   appData.debugMode = FALSE;\r
1966   appData.clockMode = TRUE;\r
1967   boardSize = (BoardSize) -1; /* determine by screen size */\r
1968   appData.Iconic = FALSE; /*unused*/\r
1969   appData.searchTime = "";\r
1970   appData.searchDepth = 0;\r
1971   appData.showCoords = FALSE;\r
1972   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1973   appData.autoCallFlag = FALSE;\r
1974   appData.flipView = FALSE;\r
1975   appData.autoFlipView = TRUE;\r
1976   appData.cmailGameName = "";\r
1977   appData.alwaysPromoteToQueen = FALSE;\r
1978   appData.oldSaveStyle = FALSE;\r
1979   appData.quietPlay = FALSE;\r
1980   appData.showThinking = FALSE;\r
1981   appData.ponderNextMove = TRUE;\r
1982   appData.periodicUpdates = TRUE;\r
1983   appData.popupExitMessage = TRUE;\r
1984   appData.popupMoveErrors = FALSE;\r
1985   appData.autoObserve = FALSE;\r
1986   appData.autoComment = FALSE;\r
1987   appData.animate = TRUE;\r
1988   appData.animSpeed = 10;\r
1989   appData.animateDragging = TRUE;\r
1990   appData.highlightLastMove = TRUE;\r
1991   appData.getMoveList = TRUE;\r
1992   appData.testLegality = TRUE;\r
1993   appData.premove = TRUE;\r
1994   appData.premoveWhite = FALSE;\r
1995   appData.premoveWhiteText = "";\r
1996   appData.premoveBlack = FALSE;\r
1997   appData.premoveBlackText = "";\r
1998   appData.icsAlarm = TRUE;\r
1999   appData.icsAlarmTime = 5000;\r
2000   appData.autoRaiseBoard = TRUE;\r
2001   appData.localLineEditing = TRUE;\r
2002   appData.colorize = TRUE;\r
2003   appData.reuseFirst = TRUE;\r
2004   appData.reuseSecond = TRUE;\r
2005   appData.blindfold = FALSE;\r
2006   appData.icsEngineAnalyze = FALSE;\r
2007   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2008   dcb.DCBlength = sizeof(DCB);\r
2009   dcb.BaudRate = 9600;\r
2010   dcb.fBinary = TRUE;\r
2011   dcb.fParity = FALSE;\r
2012   dcb.fOutxCtsFlow = FALSE;\r
2013   dcb.fOutxDsrFlow = FALSE;\r
2014   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2015   dcb.fDsrSensitivity = FALSE;\r
2016   dcb.fTXContinueOnXoff = TRUE;\r
2017   dcb.fOutX = FALSE;\r
2018   dcb.fInX = FALSE;\r
2019   dcb.fNull = FALSE;\r
2020   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2021   dcb.fAbortOnError = FALSE;\r
2022   dcb.ByteSize = 7;\r
2023   dcb.Parity = SPACEPARITY;\r
2024   dcb.StopBits = ONESTOPBIT;\r
2025   settingsFileName = SETTINGS_FILE;\r
2026   saveSettingsOnExit = TRUE;\r
2027   boardX = CW_USEDEFAULT;\r
2028   boardY = CW_USEDEFAULT;\r
2029   analysisX = CW_USEDEFAULT; \r
2030   analysisY = CW_USEDEFAULT; \r
2031   analysisW = CW_USEDEFAULT;\r
2032   analysisH = CW_USEDEFAULT;\r
2033   commentX = CW_USEDEFAULT; \r
2034   commentY = CW_USEDEFAULT; \r
2035   commentW = CW_USEDEFAULT;\r
2036   commentH = CW_USEDEFAULT;\r
2037   editTagsX = CW_USEDEFAULT; \r
2038   editTagsY = CW_USEDEFAULT; \r
2039   editTagsW = CW_USEDEFAULT;\r
2040   editTagsH = CW_USEDEFAULT;\r
2041   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2042   icsNames = ICS_NAMES;\r
2043   firstChessProgramNames = FCP_NAMES;\r
2044   secondChessProgramNames = SCP_NAMES;\r
2045   appData.initialMode = "";\r
2046   appData.variant = "normal";\r
2047   appData.firstProtocolVersion = PROTOVER;\r
2048   appData.secondProtocolVersion = PROTOVER;\r
2049   appData.showButtonBar = TRUE;\r
2050 \r
2051    /* [AS] New properties (see comments in header file) */\r
2052   appData.firstScoreIsAbsolute = FALSE;\r
2053   appData.secondScoreIsAbsolute = FALSE;\r
2054   appData.saveExtendedInfoInPGN = FALSE;\r
2055   appData.hideThinkingFromHuman = FALSE;\r
2056   appData.liteBackTextureFile = "";\r
2057   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2058   appData.darkBackTextureFile = "";\r
2059   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2060   appData.renderPiecesWithFont = "";\r
2061   appData.fontToPieceTable = "";\r
2062   appData.fontBackColorWhite = 0;\r
2063   appData.fontForeColorWhite = 0;\r
2064   appData.fontBackColorBlack = 0;\r
2065   appData.fontForeColorBlack = 0;\r
2066   appData.fontPieceSize = 80;\r
2067   appData.overrideLineGap = 1;\r
2068   appData.adjudicateLossThreshold = 0;\r
2069   appData.delayBeforeQuit = 0;\r
2070   appData.delayAfterQuit = 0;\r
2071   appData.nameOfDebugFile = "winboard.debug";\r
2072   appData.pgnEventHeader = "Computer Chess Game";\r
2073   appData.defaultFrcPosition = -1;\r
2074   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2075   appData.saveOutOfBookInfo = TRUE;\r
2076   appData.showEvalInMoveHistory = TRUE;\r
2077   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2078   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2079   appData.highlightMoveWithArrow = FALSE;\r
2080   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2081   appData.useStickyWindows = TRUE;\r
2082   appData.adjudicateDrawMoves = 0;\r
2083   appData.autoDisplayComment = TRUE;\r
2084   appData.autoDisplayTags = TRUE;\r
2085   appData.firstIsUCI = FALSE;\r
2086   appData.secondIsUCI = FALSE;\r
2087   appData.firstHasOwnBookUCI = TRUE;\r
2088   appData.secondHasOwnBookUCI = TRUE;\r
2089   appData.polyglotDir = "";\r
2090   appData.usePolyglotBook = FALSE;\r
2091   appData.polyglotBook = "";\r
2092   appData.defaultHashSize = 64;\r
2093   appData.defaultCacheSizeEGTB = 4;\r
2094   appData.defaultPathEGTB = "c:\\egtb";\r
2095   appData.firstOptions = "";\r
2096   appData.secondOptions = "";\r
2097 \r
2098   InitWindowPlacement( &wpGameList );\r
2099   InitWindowPlacement( &wpMoveHistory );\r
2100   InitWindowPlacement( &wpEvalGraph );\r
2101   InitWindowPlacement( &wpEngineOutput );\r
2102   InitWindowPlacement( &wpConsole );\r
2103 \r
2104   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2105   appData.NrFiles      = -1;\r
2106   appData.NrRanks      = -1;\r
2107   appData.holdingsSize = -1;\r
2108   appData.testClaims   = FALSE;\r
2109   appData.checkMates   = FALSE;\r
2110   appData.materialDraws= FALSE;\r
2111   appData.trivialDraws = FALSE;\r
2112   appData.ruleMoves    = 51;\r
2113   appData.drawRepeats  = 6;\r
2114   appData.matchPause   = 10000;\r
2115   appData.alphaRank    = FALSE;\r
2116   appData.allWhite     = FALSE;\r
2117   appData.upsideDown   = FALSE;\r
2118   appData.serverPause  = 15;\r
2119   appData.serverMovesName   = NULL;\r
2120   appData.suppressLoadMoves = FALSE;\r
2121   appData.firstTimeOdds  = 1;\r
2122   appData.secondTimeOdds = 1;\r
2123   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2124   appData.secondAccumulateTC = 1;\r
2125   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2126   appData.secondNPS = -1;\r
2127   appData.engineComments = 1;\r
2128   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2129   appData.egtFormats = "";\r
2130 \r
2131 #ifdef ZIPPY\r
2132   appData.zippyTalk = ZIPPY_TALK;\r
2133   appData.zippyPlay = ZIPPY_PLAY;\r
2134   appData.zippyLines = ZIPPY_LINES;\r
2135   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2136   appData.zippyPassword = ZIPPY_PASSWORD;\r
2137   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2138   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2139   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2140   appData.zippyUseI = ZIPPY_USE_I;\r
2141   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2142   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2143   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2144   appData.zippyGameStart = ZIPPY_GAME_START;\r
2145   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2146   appData.zippyAbort = ZIPPY_ABORT;\r
2147   appData.zippyVariants = ZIPPY_VARIANTS;\r
2148   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2149   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2150 #endif\r
2151 \r
2152   /* Point font array elements to structures and\r
2153      parse default font names */\r
2154   for (i=0; i<NUM_FONTS; i++) {\r
2155     for (j=0; j<NUM_SIZES; j++) {\r
2156       font[j][i] = &fontRec[j][i];\r
2157       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2158     }\r
2159   }\r
2160   \r
2161   /* Parse default settings file if any */\r
2162   if (ParseSettingsFile(settingsFileName, buf)) {\r
2163     settingsFileName = strdup(buf);\r
2164   }\r
2165 \r
2166   /* Parse command line */\r
2167   ParseArgs(StringGet, &lpCmdLine);\r
2168 \r
2169   /* [HGM] make sure board size is acceptable */\r
2170   if(appData.NrFiles > BOARD_SIZE ||\r
2171      appData.NrRanks > BOARD_SIZE   )\r
2172       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2173 \r
2174   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2175    * with options from the command line, we now make an even higher priority\r
2176    * overrule by WB options attached to the engine command line. This so that\r
2177    * tournament managers can use WB options (such as /timeOdds) that follow\r
2178    * the engines.\r
2179    */\r
2180   if(appData.firstChessProgram != NULL) {\r
2181       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2182       static char *f = "first";\r
2183       char buf[MSG_SIZ], *q = buf;\r
2184       if(p != NULL) { // engine command line contains WinBoard options\r
2185           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2186           ParseArgs(StringGet, &q);\r
2187           p[-1] = 0; // cut them offengine command line\r
2188       }\r
2189   }\r
2190   // now do same for second chess program\r
2191   if(appData.secondChessProgram != NULL) {\r
2192       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2193       static char *s = "second";\r
2194       char buf[MSG_SIZ], *q = buf;\r
2195       if(p != NULL) { // engine command line contains WinBoard options\r
2196           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2197           ParseArgs(StringGet, &q);\r
2198           p[-1] = 0; // cut them offengine command line\r
2199       }\r
2200   }\r
2201 \r
2202 \r
2203   /* Propagate options that affect others */\r
2204   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2205   if (appData.icsActive || appData.noChessProgram) {\r
2206      chessProgram = FALSE;  /* not local chess program mode */\r
2207   }\r
2208 \r
2209   /* Open startup dialog if needed */\r
2210   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2211       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2212       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2213                         *appData.secondChessProgram == NULLCHAR))) {\r
2214     FARPROC lpProc;\r
2215     \r
2216     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2217     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2218     FreeProcInstance(lpProc);\r
2219   }\r
2220 \r
2221   /* Make sure save files land in the right (?) directory */\r
2222   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2223     appData.saveGameFile = strdup(buf);\r
2224   }\r
2225   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2226     appData.savePositionFile = strdup(buf);\r
2227   }\r
2228 \r
2229   /* Finish initialization for fonts and sounds */\r
2230   for (i=0; i<NUM_FONTS; i++) {\r
2231     for (j=0; j<NUM_SIZES; j++) {\r
2232       CreateFontInMF(font[j][i]);\r
2233     }\r
2234   }\r
2235   /* xboard, and older WinBoards, controlled the move sound with the\r
2236      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2237      always turn the option on (so that the backend will call us),\r
2238      then let the user turn the sound off by setting it to silence if\r
2239      desired.  To accommodate old winboard.ini files saved by old\r
2240      versions of WinBoard, we also turn off the sound if the option\r
2241      was initially set to false. */\r
2242   if (!appData.ringBellAfterMoves) {\r
2243     sounds[(int)SoundMove].name = strdup("");\r
2244     appData.ringBellAfterMoves = TRUE;\r
2245   }\r
2246   GetCurrentDirectory(MSG_SIZ, currDir);\r
2247   SetCurrentDirectory(installDir);\r
2248   LoadAllSounds();\r
2249   SetCurrentDirectory(currDir);\r
2250 \r
2251   p = icsTextMenuString;\r
2252   if (p[0] == '@') {\r
2253     FILE* f = fopen(p + 1, "r");\r
2254     if (f == NULL) {\r
2255       DisplayFatalError(p + 1, errno, 2);\r
2256       return;\r
2257     }\r
2258     i = fread(buf, 1, sizeof(buf)-1, f);\r
2259     fclose(f);\r
2260     buf[i] = NULLCHAR;\r
2261     p = buf;\r
2262   }\r
2263   ParseIcsTextMenu(strdup(p));\r
2264 }\r
2265 \r
2266 \r
2267 VOID\r
2268 InitMenuChecks()\r
2269 {\r
2270   HMENU hmenu = GetMenu(hwndMain);\r
2271 \r
2272   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2273                         MF_BYCOMMAND|((appData.icsActive &&\r
2274                                        *appData.icsCommPort != NULLCHAR) ?\r
2275                                       MF_ENABLED : MF_GRAYED));\r
2276   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2277                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2278                                      MF_CHECKED : MF_UNCHECKED));\r
2279 }\r
2280 \r
2281 \r
2282 VOID\r
2283 SaveSettings(char* name)\r
2284 {\r
2285   FILE *f;\r
2286   ArgDescriptor *ad;\r
2287   WINDOWPLACEMENT wp;\r
2288   char dir[MSG_SIZ];\r
2289 \r
2290   if (!hwndMain) return;\r
2291 \r
2292   GetCurrentDirectory(MSG_SIZ, dir);\r
2293   SetCurrentDirectory(installDir);\r
2294   f = fopen(name, "w");\r
2295   SetCurrentDirectory(dir);\r
2296   if (f == NULL) {\r
2297     DisplayError(name, errno);\r
2298     return;\r
2299   }\r
2300   fprintf(f, ";\n");\r
2301   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2302   fprintf(f, ";\n");\r
2303   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2304   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2305   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2306   fprintf(f, ";\n");\r
2307 \r
2308   wp.length = sizeof(WINDOWPLACEMENT);\r
2309   GetWindowPlacement(hwndMain, &wp);\r
2310   boardX = wp.rcNormalPosition.left;\r
2311   boardY = wp.rcNormalPosition.top;\r
2312 \r
2313   if (hwndConsole) {\r
2314     GetWindowPlacement(hwndConsole, &wp);\r
2315     wpConsole.x = wp.rcNormalPosition.left;\r
2316     wpConsole.y = wp.rcNormalPosition.top;\r
2317     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2318     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2319   }\r
2320 \r
2321   if (analysisDialog) {\r
2322     GetWindowPlacement(analysisDialog, &wp);\r
2323     analysisX = wp.rcNormalPosition.left;\r
2324     analysisY = wp.rcNormalPosition.top;\r
2325     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2326     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2327   }\r
2328 \r
2329   if (commentDialog) {\r
2330     GetWindowPlacement(commentDialog, &wp);\r
2331     commentX = wp.rcNormalPosition.left;\r
2332     commentY = wp.rcNormalPosition.top;\r
2333     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2334     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2335   }\r
2336 \r
2337   if (editTagsDialog) {\r
2338     GetWindowPlacement(editTagsDialog, &wp);\r
2339     editTagsX = wp.rcNormalPosition.left;\r
2340     editTagsY = wp.rcNormalPosition.top;\r
2341     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2342     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2343   }\r
2344 \r
2345   if (gameListDialog) {\r
2346     GetWindowPlacement(gameListDialog, &wp);\r
2347     wpGameList.x = wp.rcNormalPosition.left;\r
2348     wpGameList.y = wp.rcNormalPosition.top;\r
2349     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2350     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2351   }\r
2352 \r
2353   /* [AS] Move history */\r
2354   wpMoveHistory.visible = MoveHistoryIsUp();\r
2355   \r
2356   if( moveHistoryDialog ) {\r
2357     GetWindowPlacement(moveHistoryDialog, &wp);\r
2358     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2359     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2360     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2361     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2362   }\r
2363 \r
2364   /* [AS] Eval graph */\r
2365   wpEvalGraph.visible = EvalGraphIsUp();\r
2366 \r
2367   if( evalGraphDialog ) {\r
2368     GetWindowPlacement(evalGraphDialog, &wp);\r
2369     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2370     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2371     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2372     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2373   }\r
2374 \r
2375   /* [AS] Engine output */\r
2376   wpEngineOutput.visible = EngineOutputIsUp();\r
2377 \r
2378   if( engineOutputDialog ) {\r
2379     GetWindowPlacement(engineOutputDialog, &wp);\r
2380     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2381     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2382     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2383     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2384   }\r
2385 \r
2386   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2387     if (!ad->save) continue;\r
2388     switch (ad->argType) {\r
2389     case ArgString:\r
2390       {\r
2391         char *p = *(char **)ad->argLoc;\r
2392         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2393           /* Quote multiline values or \-containing values\r
2394              with { } if possible */\r
2395           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2396         } else {\r
2397           /* Else quote with " " */\r
2398           fprintf(f, "/%s=\"", ad->argName);\r
2399           while (*p) {\r
2400             if (*p == '\n') fprintf(f, "\n");\r
2401             else if (*p == '\r') fprintf(f, "\\r");\r
2402             else if (*p == '\t') fprintf(f, "\\t");\r
2403             else if (*p == '\b') fprintf(f, "\\b");\r
2404             else if (*p == '\f') fprintf(f, "\\f");\r
2405             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2406             else if (*p == '\"') fprintf(f, "\\\"");\r
2407             else if (*p == '\\') fprintf(f, "\\\\");\r
2408             else putc(*p, f);\r
2409             p++;\r
2410           }\r
2411           fprintf(f, "\"\n");\r
2412         }\r
2413       }\r
2414       break;\r
2415     case ArgInt:\r
2416     case ArgZ:\r
2417       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2418       break;\r
2419     case ArgX:\r
2420       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2421       break;\r
2422     case ArgY:\r
2423       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2424       break;\r
2425     case ArgFloat:\r
2426       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2427       break;\r
2428     case ArgBoolean:\r
2429       fprintf(f, "/%s=%s\n", ad->argName, \r
2430         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2431       break;\r
2432     case ArgTrue:\r
2433       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2434       break;\r
2435     case ArgFalse:\r
2436       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2437       break;\r
2438     case ArgColor:\r
2439       {\r
2440         COLORREF color = *(COLORREF *)ad->argLoc;\r
2441         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2442           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2443       }\r
2444       break;\r
2445     case ArgAttribs:\r
2446       {\r
2447         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2448         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2449           (ta->effects & CFE_BOLD) ? "b" : "",\r
2450           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2451           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2452           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2453           (ta->effects) ? " " : "",\r
2454           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2455       }\r
2456       break;\r
2457     case ArgFilename:\r
2458       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2459         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2460       } else {\r
2461         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2462       }\r
2463       break;\r
2464     case ArgBoardSize:\r
2465       fprintf(f, "/%s=%s\n", ad->argName,\r
2466               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2467       break;\r
2468     case ArgFont:\r
2469       {\r
2470         int bs;\r
2471         for (bs=0; bs<NUM_SIZES; bs++) {\r
2472           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2473           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2474           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2475             ad->argName, mfp->faceName, mfp->pointSize,\r
2476             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2477             mfp->bold ? "b" : "",\r
2478             mfp->italic ? "i" : "",\r
2479             mfp->underline ? "u" : "",\r
2480             mfp->strikeout ? "s" : "");\r
2481         }\r
2482       }\r
2483       break;\r
2484     case ArgCommSettings:\r
2485       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2486     case ArgNone:\r
2487     case ArgSettingsFilename: ;\r
2488     }\r
2489   }\r
2490   fclose(f);\r
2491 }\r
2492 \r
2493 \r
2494 \r
2495 /*---------------------------------------------------------------------------*\\r
2496  *\r
2497  * GDI board drawing routines\r
2498  *\r
2499 \*---------------------------------------------------------------------------*/\r
2500 \r
2501 /* [AS] Draw square using background texture */\r
2502 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2503 {\r
2504     XFORM   x;\r
2505 \r
2506     if( mode == 0 ) {\r
2507         return; /* Should never happen! */\r
2508     }\r
2509 \r
2510     SetGraphicsMode( dst, GM_ADVANCED );\r
2511 \r
2512     switch( mode ) {\r
2513     case 1:\r
2514         /* Identity */\r
2515         break;\r
2516     case 2:\r
2517         /* X reflection */\r
2518         x.eM11 = -1.0;\r
2519         x.eM12 = 0;\r
2520         x.eM21 = 0;\r
2521         x.eM22 = 1.0;\r
2522         x.eDx = (FLOAT) dw + dx - 1;\r
2523         x.eDy = 0;\r
2524         dx = 0;\r
2525         SetWorldTransform( dst, &x );\r
2526         break;\r
2527     case 3:\r
2528         /* Y reflection */\r
2529         x.eM11 = 1.0;\r
2530         x.eM12 = 0;\r
2531         x.eM21 = 0;\r
2532         x.eM22 = -1.0;\r
2533         x.eDx = 0;\r
2534         x.eDy = (FLOAT) dh + dy - 1;\r
2535         dy = 0;\r
2536         SetWorldTransform( dst, &x );\r
2537         break;\r
2538     case 4:\r
2539         /* X/Y flip */\r
2540         x.eM11 = 0;\r
2541         x.eM12 = 1.0;\r
2542         x.eM21 = 1.0;\r
2543         x.eM22 = 0;\r
2544         x.eDx = (FLOAT) dx;\r
2545         x.eDy = (FLOAT) dy;\r
2546         dx = 0;\r
2547         dy = 0;\r
2548         SetWorldTransform( dst, &x );\r
2549         break;\r
2550     }\r
2551 \r
2552     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2553 \r
2554     x.eM11 = 1.0;\r
2555     x.eM12 = 0;\r
2556     x.eM21 = 0;\r
2557     x.eM22 = 1.0;\r
2558     x.eDx = 0;\r
2559     x.eDy = 0;\r
2560     SetWorldTransform( dst, &x );\r
2561 \r
2562     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2563 }\r
2564 \r
2565 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2566 enum {\r
2567     PM_WP = (int) WhitePawn, \r
2568     PM_WN = (int) WhiteKnight, \r
2569     PM_WB = (int) WhiteBishop, \r
2570     PM_WR = (int) WhiteRook, \r
2571     PM_WQ = (int) WhiteQueen, \r
2572     PM_WF = (int) WhiteFerz, \r
2573     PM_WW = (int) WhiteWazir, \r
2574     PM_WE = (int) WhiteAlfil, \r
2575     PM_WM = (int) WhiteMan, \r
2576     PM_WO = (int) WhiteCannon, \r
2577     PM_WU = (int) WhiteUnicorn, \r
2578     PM_WH = (int) WhiteNightrider, \r
2579     PM_WA = (int) WhiteAngel, \r
2580     PM_WC = (int) WhiteMarshall, \r
2581     PM_WAB = (int) WhiteCardinal, \r
2582     PM_WD = (int) WhiteDragon, \r
2583     PM_WL = (int) WhiteLance, \r
2584     PM_WS = (int) WhiteCobra, \r
2585     PM_WV = (int) WhiteFalcon, \r
2586     PM_WSG = (int) WhiteSilver, \r
2587     PM_WG = (int) WhiteGrasshopper, \r
2588     PM_WK = (int) WhiteKing,\r
2589     PM_BP = (int) BlackPawn, \r
2590     PM_BN = (int) BlackKnight, \r
2591     PM_BB = (int) BlackBishop, \r
2592     PM_BR = (int) BlackRook, \r
2593     PM_BQ = (int) BlackQueen, \r
2594     PM_BF = (int) BlackFerz, \r
2595     PM_BW = (int) BlackWazir, \r
2596     PM_BE = (int) BlackAlfil, \r
2597     PM_BM = (int) BlackMan,\r
2598     PM_BO = (int) BlackCannon, \r
2599     PM_BU = (int) BlackUnicorn, \r
2600     PM_BH = (int) BlackNightrider, \r
2601     PM_BA = (int) BlackAngel, \r
2602     PM_BC = (int) BlackMarshall, \r
2603     PM_BG = (int) BlackGrasshopper, \r
2604     PM_BAB = (int) BlackCardinal,\r
2605     PM_BD = (int) BlackDragon,\r
2606     PM_BL = (int) BlackLance,\r
2607     PM_BS = (int) BlackCobra,\r
2608     PM_BV = (int) BlackFalcon,\r
2609     PM_BSG = (int) BlackSilver,\r
2610     PM_BK = (int) BlackKing\r
2611 };\r
2612 \r
2613 static HFONT hPieceFont = NULL;\r
2614 static HBITMAP hPieceMask[(int) EmptySquare];\r
2615 static HBITMAP hPieceFace[(int) EmptySquare];\r
2616 static int fontBitmapSquareSize = 0;\r
2617 static char pieceToFontChar[(int) EmptySquare] =\r
2618                               { 'p', 'n', 'b', 'r', 'q', \r
2619                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2620                       'k', 'o', 'm', 'v', 't', 'w', \r
2621                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2622                                                               'l' };\r
2623 \r
2624 extern BOOL SetCharTable( char *table, const char * map );\r
2625 /* [HGM] moved to backend.c */\r
2626 \r
2627 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2628 {\r
2629     HBRUSH hbrush;\r
2630     BYTE r1 = GetRValue( color );\r
2631     BYTE g1 = GetGValue( color );\r
2632     BYTE b1 = GetBValue( color );\r
2633     BYTE r2 = r1 / 2;\r
2634     BYTE g2 = g1 / 2;\r
2635     BYTE b2 = b1 / 2;\r
2636     RECT rc;\r
2637 \r
2638     /* Create a uniform background first */\r
2639     hbrush = CreateSolidBrush( color );\r
2640     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2641     FillRect( hdc, &rc, hbrush );\r
2642     DeleteObject( hbrush );\r
2643     \r
2644     if( mode == 1 ) {\r
2645         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2646         int steps = squareSize / 2;\r
2647         int i;\r
2648 \r
2649         for( i=0; i<steps; i++ ) {\r
2650             BYTE r = r1 - (r1-r2) * i / steps;\r
2651             BYTE g = g1 - (g1-g2) * i / steps;\r
2652             BYTE b = b1 - (b1-b2) * i / steps;\r
2653 \r
2654             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2655             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2656             FillRect( hdc, &rc, hbrush );\r
2657             DeleteObject(hbrush);\r
2658         }\r
2659     }\r
2660     else if( mode == 2 ) {\r
2661         /* Diagonal gradient, good more or less for every piece */\r
2662         POINT triangle[3];\r
2663         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2664         HBRUSH hbrush_old;\r
2665         int steps = squareSize;\r
2666         int i;\r
2667 \r
2668         triangle[0].x = squareSize - steps;\r
2669         triangle[0].y = squareSize;\r
2670         triangle[1].x = squareSize;\r
2671         triangle[1].y = squareSize;\r
2672         triangle[2].x = squareSize;\r
2673         triangle[2].y = squareSize - steps;\r
2674 \r
2675         for( i=0; i<steps; i++ ) {\r
2676             BYTE r = r1 - (r1-r2) * i / steps;\r
2677             BYTE g = g1 - (g1-g2) * i / steps;\r
2678             BYTE b = b1 - (b1-b2) * i / steps;\r
2679 \r
2680             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2681             hbrush_old = SelectObject( hdc, hbrush );\r
2682             Polygon( hdc, triangle, 3 );\r
2683             SelectObject( hdc, hbrush_old );\r
2684             DeleteObject(hbrush);\r
2685             triangle[0].x++;\r
2686             triangle[2].y++;\r
2687         }\r
2688 \r
2689         SelectObject( hdc, hpen );\r
2690     }\r
2691 }\r
2692 \r
2693 /*\r
2694     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2695     seems to work ok. The main problem here is to find the "inside" of a chess\r
2696     piece: follow the steps as explained below.\r
2697 */\r
2698 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2699 {\r
2700     HBITMAP hbm;\r
2701     HBITMAP hbm_old;\r
2702     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2703     RECT rc;\r
2704     SIZE sz;\r
2705     POINT pt;\r
2706     int backColor = whitePieceColor; \r
2707     int foreColor = blackPieceColor;\r
2708     \r
2709     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2710         backColor = appData.fontBackColorWhite;\r
2711         foreColor = appData.fontForeColorWhite;\r
2712     }\r
2713     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2714         backColor = appData.fontBackColorBlack;\r
2715         foreColor = appData.fontForeColorBlack;\r
2716     }\r
2717 \r
2718     /* Mask */\r
2719     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2720 \r
2721     hbm_old = SelectObject( hdc, hbm );\r
2722 \r
2723     rc.left = 0;\r
2724     rc.top = 0;\r
2725     rc.right = squareSize;\r
2726     rc.bottom = squareSize;\r
2727 \r
2728     /* Step 1: background is now black */\r
2729     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2730 \r
2731     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2732 \r
2733     pt.x = (squareSize - sz.cx) / 2;\r
2734     pt.y = (squareSize - sz.cy) / 2;\r
2735 \r
2736     SetBkMode( hdc, TRANSPARENT );\r
2737     SetTextColor( hdc, chroma );\r
2738     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2739     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2740 \r
2741     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2742     /* Step 3: the area outside the piece is filled with white */\r
2743 //    FloodFill( hdc, 0, 0, chroma );\r
2744     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2745     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2746     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2747     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2748     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2749     /* \r
2750         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2751         but if the start point is not inside the piece we're lost!\r
2752         There should be a better way to do this... if we could create a region or path\r
2753         from the fill operation we would be fine for example.\r
2754     */\r
2755 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2756     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2757 \r
2758     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2759         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2760         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2761 \r
2762         SelectObject( dc2, bm2 );\r
2763         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2764         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2765         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2766         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2767         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2768 \r
2769         DeleteDC( dc2 );\r
2770         DeleteObject( bm2 );\r
2771     }\r
2772 \r
2773     SetTextColor( hdc, 0 );\r
2774     /* \r
2775         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2776         draw the piece again in black for safety.\r
2777     */\r
2778     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2779 \r
2780     SelectObject( hdc, hbm_old );\r
2781 \r
2782     if( hPieceMask[index] != NULL ) {\r
2783         DeleteObject( hPieceMask[index] );\r
2784     }\r
2785 \r
2786     hPieceMask[index] = hbm;\r
2787 \r
2788     /* Face */\r
2789     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2790 \r
2791     SelectObject( hdc, hbm );\r
2792 \r
2793     {\r
2794         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2795         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2796         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2797 \r
2798         SelectObject( dc1, hPieceMask[index] );\r
2799         SelectObject( dc2, bm2 );\r
2800         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2801         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2802         \r
2803         /* \r
2804             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2805             the piece background and deletes (makes transparent) the rest.\r
2806             Thanks to that mask, we are free to paint the background with the greates\r
2807             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2808             We use this, to make gradients and give the pieces a "roundish" look.\r
2809         */\r
2810         SetPieceBackground( hdc, backColor, 2 );\r
2811         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2812 \r
2813         DeleteDC( dc2 );\r
2814         DeleteDC( dc1 );\r
2815         DeleteObject( bm2 );\r
2816     }\r
2817 \r
2818     SetTextColor( hdc, foreColor );\r
2819     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2820 \r
2821     SelectObject( hdc, hbm_old );\r
2822 \r
2823     if( hPieceFace[index] != NULL ) {\r
2824         DeleteObject( hPieceFace[index] );\r
2825     }\r
2826 \r
2827     hPieceFace[index] = hbm;\r
2828 }\r
2829 \r
2830 static int TranslatePieceToFontPiece( int piece )\r
2831 {\r
2832     switch( piece ) {\r
2833     case BlackPawn:\r
2834         return PM_BP;\r
2835     case BlackKnight:\r
2836         return PM_BN;\r
2837     case BlackBishop:\r
2838         return PM_BB;\r
2839     case BlackRook:\r
2840         return PM_BR;\r
2841     case BlackQueen:\r
2842         return PM_BQ;\r
2843     case BlackKing:\r
2844         return PM_BK;\r
2845     case WhitePawn:\r
2846         return PM_WP;\r
2847     case WhiteKnight:\r
2848         return PM_WN;\r
2849     case WhiteBishop:\r
2850         return PM_WB;\r
2851     case WhiteRook:\r
2852         return PM_WR;\r
2853     case WhiteQueen:\r
2854         return PM_WQ;\r
2855     case WhiteKing:\r
2856         return PM_WK;\r
2857 \r
2858     case BlackAngel:\r
2859         return PM_BA;\r
2860     case BlackMarshall:\r
2861         return PM_BC;\r
2862     case BlackFerz:\r
2863         return PM_BF;\r
2864     case BlackNightrider:\r
2865         return PM_BH;\r
2866     case BlackAlfil:\r
2867         return PM_BE;\r
2868     case BlackWazir:\r
2869         return PM_BW;\r
2870     case BlackUnicorn:\r
2871         return PM_BU;\r
2872     case BlackCannon:\r
2873         return PM_BO;\r
2874     case BlackGrasshopper:\r
2875         return PM_BG;\r
2876     case BlackMan:\r
2877         return PM_BM;\r
2878     case BlackSilver:\r
2879         return PM_BSG;\r
2880     case BlackLance:\r
2881         return PM_BL;\r
2882     case BlackFalcon:\r
2883         return PM_BV;\r
2884     case BlackCobra:\r
2885         return PM_BS;\r
2886     case BlackCardinal:\r
2887         return PM_BAB;\r
2888     case BlackDragon:\r
2889         return PM_BD;\r
2890 \r
2891     case WhiteAngel:\r
2892         return PM_WA;\r
2893     case WhiteMarshall:\r
2894         return PM_WC;\r
2895     case WhiteFerz:\r
2896         return PM_WF;\r
2897     case WhiteNightrider:\r
2898         return PM_WH;\r
2899     case WhiteAlfil:\r
2900         return PM_WE;\r
2901     case WhiteWazir:\r
2902         return PM_WW;\r
2903     case WhiteUnicorn:\r
2904         return PM_WU;\r
2905     case WhiteCannon:\r
2906         return PM_WO;\r
2907     case WhiteGrasshopper:\r
2908         return PM_WG;\r
2909     case WhiteMan:\r
2910         return PM_WM;\r
2911     case WhiteSilver:\r
2912         return PM_WSG;\r
2913     case WhiteLance:\r
2914         return PM_WL;\r
2915     case WhiteFalcon:\r
2916         return PM_WV;\r
2917     case WhiteCobra:\r
2918         return PM_WS;\r
2919     case WhiteCardinal:\r
2920         return PM_WAB;\r
2921     case WhiteDragon:\r
2922         return PM_WD;\r
2923     }\r
2924 \r
2925     return 0;\r
2926 }\r
2927 \r
2928 void CreatePiecesFromFont()\r
2929 {\r
2930     LOGFONT lf;\r
2931     HDC hdc_window = NULL;\r
2932     HDC hdc = NULL;\r
2933     HFONT hfont_old;\r
2934     int fontHeight;\r
2935     int i;\r
2936 \r
2937     if( fontBitmapSquareSize < 0 ) {\r
2938         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2939         return;\r
2940     }\r
2941 \r
2942     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2943         fontBitmapSquareSize = -1;\r
2944         return;\r
2945     }\r
2946 \r
2947     if( fontBitmapSquareSize != squareSize ) {\r
2948         hdc_window = GetDC( hwndMain );\r
2949         hdc = CreateCompatibleDC( hdc_window );\r
2950 \r
2951         if( hPieceFont != NULL ) {\r
2952             DeleteObject( hPieceFont );\r
2953         }\r
2954         else {\r
2955             for( i=0; i<=(int)BlackKing; i++ ) {\r
2956                 hPieceMask[i] = NULL;\r
2957                 hPieceFace[i] = NULL;\r
2958             }\r
2959         }\r
2960 \r
2961         fontHeight = 75;\r
2962 \r
2963         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2964             fontHeight = appData.fontPieceSize;\r
2965         }\r
2966 \r
2967         fontHeight = (fontHeight * squareSize) / 100;\r
2968 \r
2969         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2970         lf.lfWidth = 0;\r
2971         lf.lfEscapement = 0;\r
2972         lf.lfOrientation = 0;\r
2973         lf.lfWeight = FW_NORMAL;\r
2974         lf.lfItalic = 0;\r
2975         lf.lfUnderline = 0;\r
2976         lf.lfStrikeOut = 0;\r
2977         lf.lfCharSet = DEFAULT_CHARSET;\r
2978         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2979         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2980         lf.lfQuality = PROOF_QUALITY;\r
2981         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2982         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2983         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2984 \r
2985         hPieceFont = CreateFontIndirect( &lf );\r
2986 \r
2987         if( hPieceFont == NULL ) {\r
2988             fontBitmapSquareSize = -2;\r
2989         }\r
2990         else {\r
2991             /* Setup font-to-piece character table */\r
2992             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2993                 /* No (or wrong) global settings, try to detect the font */\r
2994                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2995                     /* Alpha */\r
2996                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2997                 }\r
2998                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2999                     /* DiagramTT* family */\r
3000                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3001                 }\r
3002                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3003                     /* Fairy symbols */\r
3004                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3005                 }\r
3006                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3007                     /* Good Companion (Some characters get warped as literal :-( */\r
3008                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3009                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3010                     SetCharTable(pieceToFontChar, s);\r
3011                 }\r
3012                 else {\r
3013                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3014                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3015                 }\r
3016             }\r
3017 \r
3018             /* Create bitmaps */\r
3019             hfont_old = SelectObject( hdc, hPieceFont );\r
3020 #if 0\r
3021             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
3022             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
3023             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
3024             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
3025             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
3026             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
3027             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
3028             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
3029             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
3030             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
3031             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
3032             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
3033 \r
3034             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
3035             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
3036             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
3037             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
3038             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
3039             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
3040             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
3041             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
3042             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
3043             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
3044             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
3045             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
3046             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
3047             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
3048             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
3049             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
3050             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
3051             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
3052             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
3053             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
3054             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
3055             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
3056             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
3057             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
3058             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
3059             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
3060             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
3061             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
3062             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
3063             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
3064             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
3065             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
3066 #else\r
3067             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3068                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3069                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3070 #endif\r
3071             SelectObject( hdc, hfont_old );\r
3072 \r
3073             fontBitmapSquareSize = squareSize;\r
3074         }\r
3075     }\r
3076 \r
3077     if( hdc != NULL ) {\r
3078         DeleteDC( hdc );\r
3079     }\r
3080 \r
3081     if( hdc_window != NULL ) {\r
3082         ReleaseDC( hwndMain, hdc_window );\r
3083     }\r
3084 }\r
3085 \r
3086 HBITMAP\r
3087 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3088 {\r
3089   char name[128];\r
3090 \r
3091   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3092   if (gameInfo.event &&\r
3093       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3094       strcmp(name, "k80s") == 0) {\r
3095     strcpy(name, "tim");\r
3096   }\r
3097   return LoadBitmap(hinst, name);\r
3098 }\r
3099 \r
3100 \r
3101 /* Insert a color into the program's logical palette\r
3102    structure.  This code assumes the given color is\r
3103    the result of the RGB or PALETTERGB macro, and it\r
3104    knows how those macros work (which is documented).\r
3105 */\r
3106 VOID\r
3107 InsertInPalette(COLORREF color)\r
3108 {\r
3109   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3110 \r
3111   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3112     DisplayFatalError("Too many colors", 0, 1);\r
3113     pLogPal->palNumEntries--;\r
3114     return;\r
3115   }\r
3116 \r
3117   pe->peFlags = (char) 0;\r
3118   pe->peRed = (char) (0xFF & color);\r
3119   pe->peGreen = (char) (0xFF & (color >> 8));\r
3120   pe->peBlue = (char) (0xFF & (color >> 16));\r
3121   return;\r
3122 }\r
3123 \r
3124 \r
3125 VOID\r
3126 InitDrawingColors()\r
3127 {\r
3128   if (pLogPal == NULL) {\r
3129     /* Allocate enough memory for a logical palette with\r
3130      * PALETTESIZE entries and set the size and version fields\r
3131      * of the logical palette structure.\r
3132      */\r
3133     pLogPal = (NPLOGPALETTE)\r
3134       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3135                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3136     pLogPal->palVersion    = 0x300;\r
3137   }\r
3138   pLogPal->palNumEntries = 0;\r
3139 \r
3140   InsertInPalette(lightSquareColor);\r
3141   InsertInPalette(darkSquareColor);\r
3142   InsertInPalette(whitePieceColor);\r
3143   InsertInPalette(blackPieceColor);\r
3144   InsertInPalette(highlightSquareColor);\r
3145   InsertInPalette(premoveHighlightColor);\r
3146 \r
3147   /*  create a logical color palette according the information\r
3148    *  in the LOGPALETTE structure.\r
3149    */\r
3150   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3151 \r
3152   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3153   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3154   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3155   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3156   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3157   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3158   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3159   /* [AS] Force rendering of the font-based pieces */\r
3160   if( fontBitmapSquareSize > 0 ) {\r
3161     fontBitmapSquareSize = 0;\r
3162   }\r
3163 }\r
3164 \r
3165 \r
3166 int\r
3167 BoardWidth(int boardSize, int n)\r
3168 { /* [HGM] argument n added to allow different width and height */\r
3169   int lineGap = sizeInfo[boardSize].lineGap;\r
3170 \r
3171   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3172       lineGap = appData.overrideLineGap;\r
3173   }\r
3174 \r
3175   return (n + 1) * lineGap +\r
3176           n * sizeInfo[boardSize].squareSize;\r
3177 }\r
3178 \r
3179 /* Respond to board resize by dragging edge */\r
3180 VOID\r
3181 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3182 {\r
3183   BoardSize newSize = NUM_SIZES - 1;\r
3184   static int recurse = 0;\r
3185   if (IsIconic(hwndMain)) return;\r
3186   if (recurse > 0) return;\r
3187   recurse++;\r
3188   while (newSize > 0) {\r
3189         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3190         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3191            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3192     newSize--;\r
3193   } \r
3194   boardSize = newSize;\r
3195   InitDrawingSizes(boardSize, flags);\r
3196   recurse--;\r
3197 }\r
3198 \r
3199 \r
3200 \r
3201 VOID\r
3202 InitDrawingSizes(BoardSize boardSize, int flags)\r
3203 {\r
3204   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3205   ChessSquare piece;\r
3206   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3207   HDC hdc;\r
3208   SIZE clockSize, messageSize;\r
3209   HFONT oldFont;\r
3210   char buf[MSG_SIZ];\r
3211   char *str;\r
3212   HMENU hmenu = GetMenu(hwndMain);\r
3213   RECT crect, wrect, oldRect;\r
3214   int offby;\r
3215   LOGBRUSH logbrush;\r
3216 \r
3217   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3218   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3219 \r
3220   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3221   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3222 \r
3223   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3224   oldRect.top = boardY;\r
3225   oldRect.right = boardX + winWidth;\r
3226   oldRect.bottom = boardY + winHeight;\r
3227 \r
3228   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3229   smallLayout = sizeInfo[boardSize].smallLayout;\r
3230   squareSize = sizeInfo[boardSize].squareSize;\r
3231   lineGap = sizeInfo[boardSize].lineGap;\r
3232   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3233 \r
3234   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3235       lineGap = appData.overrideLineGap;\r
3236   }\r
3237 \r
3238   if (tinyLayout != oldTinyLayout) {\r
3239     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3240     if (tinyLayout) {\r
3241       style &= ~WS_SYSMENU;\r
3242       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3243                  "&Minimize\tCtrl+F4");\r
3244     } else {\r
3245       style |= WS_SYSMENU;\r
3246       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3247     }\r
3248     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3249 \r
3250     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3251       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3252         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3253     }\r
3254     DrawMenuBar(hwndMain);\r
3255   }\r
3256 \r
3257   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3258   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3259 \r
3260   /* Get text area sizes */\r
3261   hdc = GetDC(hwndMain);\r
3262   if (appData.clockMode) {\r
3263     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3264   } else {\r
3265     sprintf(buf, "White");\r
3266   }\r
3267   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3268   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3269   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3270   str = "We only care about the height here";\r
3271   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3272   SelectObject(hdc, oldFont);\r
3273   ReleaseDC(hwndMain, hdc);\r
3274 \r
3275   /* Compute where everything goes */\r
3276   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3277         /* [HGM] logo: if either logo is on, reserve space for it */\r
3278         logoHeight =  2*clockSize.cy;\r
3279         leftLogoRect.left   = OUTER_MARGIN;\r
3280         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3281         leftLogoRect.top    = OUTER_MARGIN;\r
3282         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3283 \r
3284         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3285         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3286         rightLogoRect.top    = OUTER_MARGIN;\r
3287         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3288 \r
3289 \r
3290     whiteRect.left = leftLogoRect.right;\r
3291     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3292     whiteRect.top = OUTER_MARGIN;\r
3293     whiteRect.bottom = whiteRect.top + logoHeight;\r
3294 \r
3295     blackRect.right = rightLogoRect.left;\r
3296     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3297     blackRect.top = whiteRect.top;\r
3298     blackRect.bottom = whiteRect.bottom;\r
3299   } else {\r
3300     whiteRect.left = OUTER_MARGIN;\r
3301     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3302     whiteRect.top = OUTER_MARGIN;\r
3303     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3304 \r
3305     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3306     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3307     blackRect.top = whiteRect.top;\r
3308     blackRect.bottom = whiteRect.bottom;\r
3309   }\r
3310 \r
3311   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3312   if (appData.showButtonBar) {\r
3313     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3314       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3315   } else {\r
3316     messageRect.right = OUTER_MARGIN + boardWidth;\r
3317   }\r
3318   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3319   messageRect.bottom = messageRect.top + messageSize.cy;\r
3320 \r
3321   boardRect.left = OUTER_MARGIN;\r
3322   boardRect.right = boardRect.left + boardWidth;\r
3323   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3324   boardRect.bottom = boardRect.top + boardHeight;\r
3325 \r
3326   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3327   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3328   oldBoardSize = boardSize;\r
3329   oldTinyLayout = tinyLayout;\r
3330   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3331   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3332     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3333   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3334   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3335   winHeight = winH; //       without disturbing window attachments\r
3336   GetWindowRect(hwndMain, &wrect);\r
3337   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3338                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3339 \r
3340   // [HGM] placement: let attached windows follow size change.\r
3341   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3342   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3343   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3344   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3345   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3346 \r
3347   /* compensate if menu bar wrapped */\r
3348   GetClientRect(hwndMain, &crect);\r
3349   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3350   winHeight += offby;\r
3351   switch (flags) {\r
3352   case WMSZ_TOPLEFT:\r
3353     SetWindowPos(hwndMain, NULL, \r
3354                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3355                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3356     break;\r
3357 \r
3358   case WMSZ_TOPRIGHT:\r
3359   case WMSZ_TOP:\r
3360     SetWindowPos(hwndMain, NULL, \r
3361                  wrect.left, wrect.bottom - winHeight, \r
3362                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3363     break;\r
3364 \r
3365   case WMSZ_BOTTOMLEFT:\r
3366   case WMSZ_LEFT:\r
3367     SetWindowPos(hwndMain, NULL, \r
3368                  wrect.right - winWidth, wrect.top, \r
3369                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3370     break;\r
3371 \r
3372   case WMSZ_BOTTOMRIGHT:\r
3373   case WMSZ_BOTTOM:\r
3374   case WMSZ_RIGHT:\r
3375   default:\r
3376     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3377                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3378     break;\r
3379   }\r
3380 \r
3381   hwndPause = NULL;\r
3382   for (i = 0; i < N_BUTTONS; i++) {\r
3383     if (buttonDesc[i].hwnd != NULL) {\r
3384       DestroyWindow(buttonDesc[i].hwnd);\r
3385       buttonDesc[i].hwnd = NULL;\r
3386     }\r
3387     if (appData.showButtonBar) {\r
3388       buttonDesc[i].hwnd =\r
3389         CreateWindow("BUTTON", buttonDesc[i].label,\r
3390                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3391                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3392                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3393                      (HMENU) buttonDesc[i].id,\r
3394                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3395       if (tinyLayout) {\r
3396         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3397                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3398                     MAKELPARAM(FALSE, 0));\r
3399       }\r
3400       if (buttonDesc[i].id == IDM_Pause)\r
3401         hwndPause = buttonDesc[i].hwnd;\r
3402       buttonDesc[i].wndproc = (WNDPROC)\r
3403         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3404     }\r
3405   }\r
3406   if (gridPen != NULL) DeleteObject(gridPen);\r
3407   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3408   if (premovePen != NULL) DeleteObject(premovePen);\r
3409   if (lineGap != 0) {\r
3410     logbrush.lbStyle = BS_SOLID;\r
3411     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3412     gridPen =\r
3413       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3414                    lineGap, &logbrush, 0, NULL);\r
3415     logbrush.lbColor = highlightSquareColor;\r
3416     highlightPen =\r
3417       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3418                    lineGap, &logbrush, 0, NULL);\r
3419 \r
3420     logbrush.lbColor = premoveHighlightColor; \r
3421     premovePen =\r
3422       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3423                    lineGap, &logbrush, 0, NULL);\r
3424 \r
3425     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3426     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3427       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3428       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3429         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3430       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3431         BOARD_WIDTH * (squareSize + lineGap);\r
3432       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3433     }\r
3434     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3435       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3436       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3437         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3438         lineGap / 2 + (i * (squareSize + lineGap));\r
3439       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3440         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3441       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3442     }\r
3443   }\r
3444 \r
3445   /* [HGM] Licensing requirement */\r
3446 #ifdef GOTHIC\r
3447   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3448 #endif\r
3449 #ifdef FALCON\r
3450   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3451 #endif\r
3452   GothicPopUp( "", VariantNormal);\r
3453 \r
3454 \r
3455 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3456 \r
3457   /* Load piece bitmaps for this board size */\r
3458   for (i=0; i<=2; i++) {\r
3459     for (piece = WhitePawn;\r
3460          (int) piece < (int) BlackPawn;\r
3461          piece = (ChessSquare) ((int) piece + 1)) {\r
3462       if (pieceBitmap[i][piece] != NULL)\r
3463         DeleteObject(pieceBitmap[i][piece]);\r
3464     }\r
3465   }\r
3466 \r
3467   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3468   // Orthodox Chess pieces\r
3469   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3470   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3471   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3472   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3473   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3474   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3475   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3476   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3477   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3478   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3479   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3480   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3481   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3482   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3483   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3484   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3485     // in Shogi, Hijack the unused Queen for Lance\r
3486     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3487     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3488     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3489   } else {\r
3490     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3491     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3492     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3493   }\r
3494 \r
3495   if(squareSize <= 72 && squareSize >= 33) { \r
3496     /* A & C are available in most sizes now */\r
3497     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3498       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3499       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3500       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3501       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3502       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3503       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3504       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3505       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3506       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3507       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3508       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3509       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3510     } else { // Smirf-like\r
3511       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3512       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3513       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3514     }\r
3515     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3516       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3517       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3518       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3519     } else { // WinBoard standard\r
3520       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3521       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3522       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3523     }\r
3524   }\r
3525 \r
3526 \r
3527   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3528     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3529     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3530     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3531     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3532     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3533     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3534     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3535     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3536     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3537     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3538     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3539     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3540     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3541     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3542     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3543     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3544     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3545     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3546     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3547     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3548     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3549     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3550     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3551     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3552     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3553     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3554     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3555     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3556     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3557     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3558 \r
3559     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3560       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3561       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3562       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3563       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3564       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3565       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3566       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3567       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3568       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3569       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3570       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3571       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3572     } else {\r
3573       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3574       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3575       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3576       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3577       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3578       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3579       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3580       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3581       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3582       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3583       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3584       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3585     }\r
3586 \r
3587   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3588     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3589     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3590     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3591     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3592     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3593     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3594     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3595     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3596     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3597     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3598     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3599     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3600     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3601     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3602   }\r
3603 \r
3604 \r
3605   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3606   /* special Shogi support in this size */\r
3607   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3608       for (piece = WhitePawn;\r
3609            (int) piece < (int) BlackPawn;\r
3610            piece = (ChessSquare) ((int) piece + 1)) {\r
3611         if (pieceBitmap[i][piece] != NULL)\r
3612           DeleteObject(pieceBitmap[i][piece]);\r
3613       }\r
3614     }\r
3615   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3616   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3617   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3618   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3619   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3620   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3621   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3622   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3623   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3624   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3625   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3626   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3627   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3628   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3629   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3630   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3631   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3632   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3633   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3634   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3635   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3636   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3637   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3638   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3639   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3640   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3641   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3642   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3643   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3644   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3645   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3646   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3647   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3648   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3649   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3650   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3651   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3652   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3653   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3654   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3655   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3656   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3657   minorSize = 0;\r
3658   }\r
3659 }\r
3660 \r
3661 HBITMAP\r
3662 PieceBitmap(ChessSquare p, int kind)\r
3663 {\r
3664   if ((int) p >= (int) BlackPawn)\r
3665     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3666 \r
3667   return pieceBitmap[kind][(int) p];\r
3668 }\r
3669 \r
3670 /***************************************************************/\r
3671 \r
3672 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3673 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3674 /*\r
3675 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3676 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3677 */\r
3678 \r
3679 VOID\r
3680 SquareToPos(int row, int column, int * x, int * y)\r
3681 {\r
3682   if (flipView) {\r
3683     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3684     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3685   } else {\r
3686     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3687     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3688   }\r
3689 }\r
3690 \r
3691 VOID\r
3692 DrawCoordsOnDC(HDC hdc)\r
3693 {\r
3694   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
3695   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
3696   char str[2] = { NULLCHAR, NULLCHAR };\r
3697   int oldMode, oldAlign, x, y, start, i;\r
3698   HFONT oldFont;\r
3699   HBRUSH oldBrush;\r
3700 \r
3701   if (!appData.showCoords)\r
3702     return;\r
3703 \r
3704   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3705 \r
3706   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3707   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3708   oldAlign = GetTextAlign(hdc);\r
3709   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3710 \r
3711   y = boardRect.top + lineGap;\r
3712   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3713 \r
3714   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3715   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3716     str[0] = files[start + i];\r
3717     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3718     y += squareSize + lineGap;\r
3719   }\r
3720 \r
3721   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3722 \r
3723   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3724   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3725     str[0] = ranks[start + i];\r
3726     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3727     x += squareSize + lineGap;\r
3728   }    \r
3729 \r
3730   SelectObject(hdc, oldBrush);\r
3731   SetBkMode(hdc, oldMode);\r
3732   SetTextAlign(hdc, oldAlign);\r
3733   SelectObject(hdc, oldFont);\r
3734 }\r
3735 \r
3736 VOID\r
3737 DrawGridOnDC(HDC hdc)\r
3738 {\r
3739   HPEN oldPen;\r
3740  \r
3741   if (lineGap != 0) {\r
3742     oldPen = SelectObject(hdc, gridPen);\r
3743     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3744     SelectObject(hdc, oldPen);\r
3745   }\r
3746 }\r
3747 \r
3748 #define HIGHLIGHT_PEN 0\r
3749 #define PREMOVE_PEN   1\r
3750 \r
3751 VOID\r
3752 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3753 {\r
3754   int x1, y1;\r
3755   HPEN oldPen, hPen;\r
3756   if (lineGap == 0) return;\r
3757   if (flipView) {\r
3758     x1 = boardRect.left +\r
3759       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3760     y1 = boardRect.top +\r
3761       lineGap/2 + y * (squareSize + lineGap);\r
3762   } else {\r
3763     x1 = boardRect.left +\r
3764       lineGap/2 + x * (squareSize + lineGap);\r
3765     y1 = boardRect.top +\r
3766       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3767   }\r
3768   hPen = pen ? premovePen : highlightPen;\r
3769   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3770   MoveToEx(hdc, x1, y1, NULL);\r
3771   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3772   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3773   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3774   LineTo(hdc, x1, y1);\r
3775   SelectObject(hdc, oldPen);\r
3776 }\r
3777 \r
3778 VOID\r
3779 DrawHighlightsOnDC(HDC hdc)\r
3780 {\r
3781   int i;\r
3782   for (i=0; i<2; i++) {\r
3783     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3784       DrawHighlightOnDC(hdc, TRUE,\r
3785                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3786                         HIGHLIGHT_PEN);\r
3787   }\r
3788   for (i=0; i<2; i++) {\r
3789     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3790         premoveHighlightInfo.sq[i].y >= 0) {\r
3791         DrawHighlightOnDC(hdc, TRUE,\r
3792                           premoveHighlightInfo.sq[i].x, \r
3793                           premoveHighlightInfo.sq[i].y,\r
3794                           PREMOVE_PEN);\r
3795     }\r
3796   }\r
3797 }\r
3798 \r
3799 /* Note: sqcolor is used only in monoMode */\r
3800 /* Note that this code is largely duplicated in woptions.c,\r
3801    function DrawSampleSquare, so that needs to be updated too */\r
3802 VOID\r
3803 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3804 {\r
3805   HBITMAP oldBitmap;\r
3806   HBRUSH oldBrush;\r
3807   int tmpSize;\r
3808 \r
3809   if (appData.blindfold) return;\r
3810 \r
3811   /* [AS] Use font-based pieces if needed */\r
3812   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3813     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3814     CreatePiecesFromFont();\r
3815 \r
3816     if( fontBitmapSquareSize == squareSize ) {\r
3817         int index = TranslatePieceToFontPiece(piece);\r
3818 \r
3819         SelectObject( tmphdc, hPieceMask[ index ] );\r
3820 \r
3821         BitBlt( hdc,\r
3822             x, y,\r
3823             squareSize, squareSize,\r
3824             tmphdc,\r
3825             0, 0,\r
3826             SRCAND );\r
3827 \r
3828         SelectObject( tmphdc, hPieceFace[ index ] );\r
3829 \r
3830         BitBlt( hdc,\r
3831             x, y,\r
3832             squareSize, squareSize,\r
3833             tmphdc,\r
3834             0, 0,\r
3835             SRCPAINT );\r
3836 \r
3837         return;\r
3838     }\r
3839   }\r
3840 \r
3841   if (appData.monoMode) {\r
3842     SelectObject(tmphdc, PieceBitmap(piece, \r
3843       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3844     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3845            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3846   } else {\r
3847     tmpSize = squareSize;\r
3848     if(minorSize &&\r
3849         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3850          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3851       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3852       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3853       x += (squareSize - minorSize)>>1;\r
3854       y += squareSize - minorSize - 2;\r
3855       tmpSize = minorSize;\r
3856     }\r
3857     if (color || appData.allWhite ) {\r
3858       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3859       if( color )\r
3860               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3861       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3862       if(appData.upsideDown && color==flipView)\r
3863         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3864       else\r
3865         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3866 #if 0\r
3867       /* Use black piece color for outline of white pieces */\r
3868       /* Not sure this looks really good (though xboard does it).\r
3869          Maybe better to have another selectable color, default black */\r
3870       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3871       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3872       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3873 #else\r
3874       /* Use black for outline of white pieces */\r
3875       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3876       if(appData.upsideDown && color==flipView)\r
3877         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3878       else\r
3879         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3880 #endif\r
3881     } else {\r
3882 #if 0\r
3883       /* Use white piece color for details of black pieces */\r
3884       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3885          WHITE_PIECE ones aren't always the right shape. */\r
3886       /* Not sure this looks really good (though xboard does it).\r
3887          Maybe better to have another selectable color, default medium gray? */\r
3888       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3889       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3890       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3891       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3892       SelectObject(hdc, blackPieceBrush);\r
3893       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3894 #else\r
3895       /* Use square color for details of black pieces */\r
3896       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3897       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3898       if(appData.upsideDown && !flipView)\r
3899         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3900       else\r
3901         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3902 #endif\r
3903     }\r
3904     SelectObject(hdc, oldBrush);\r
3905     SelectObject(tmphdc, oldBitmap);\r
3906   }\r
3907 }\r
3908 \r
3909 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3910 int GetBackTextureMode( int algo )\r
3911 {\r
3912     int result = BACK_TEXTURE_MODE_DISABLED;\r
3913 \r
3914     switch( algo ) \r
3915     {\r
3916         case BACK_TEXTURE_MODE_PLAIN:\r
3917             result = 1; /* Always use identity map */\r
3918             break;\r
3919         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3920             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3921             break;\r
3922     }\r
3923 \r
3924     return result;\r
3925 }\r
3926 \r
3927 /* \r
3928     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3929     to handle redraws cleanly (as random numbers would always be different).\r
3930 */\r
3931 VOID RebuildTextureSquareInfo()\r
3932 {\r
3933     BITMAP bi;\r
3934     int lite_w = 0;\r
3935     int lite_h = 0;\r
3936     int dark_w = 0;\r
3937     int dark_h = 0;\r
3938     int row;\r
3939     int col;\r
3940 \r
3941     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3942 \r
3943     if( liteBackTexture != NULL ) {\r
3944         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3945             lite_w = bi.bmWidth;\r
3946             lite_h = bi.bmHeight;\r
3947         }\r
3948     }\r
3949 \r
3950     if( darkBackTexture != NULL ) {\r
3951         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3952             dark_w = bi.bmWidth;\r
3953             dark_h = bi.bmHeight;\r
3954         }\r
3955     }\r
3956 \r
3957     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3958         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3959             if( (col + row) & 1 ) {\r
3960                 /* Lite square */\r
3961                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3962                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3963                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3964                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3965                 }\r
3966             }\r
3967             else {\r
3968                 /* Dark square */\r
3969                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3970                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3971                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3972                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3973                 }\r
3974             }\r
3975         }\r
3976     }\r
3977 }\r
3978 \r
3979 /* [AS] Arrow highlighting support */\r
3980 \r
3981 static int A_WIDTH = 5; /* Width of arrow body */\r
3982 \r
3983 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3984 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3985 \r
3986 static double Sqr( double x )\r
3987 {\r
3988     return x*x;\r
3989 }\r
3990 \r
3991 static int Round( double x )\r
3992 {\r
3993     return (int) (x + 0.5);\r
3994 }\r
3995 \r
3996 /* Draw an arrow between two points using current settings */\r
3997 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3998 {\r
3999     POINT arrow[7];\r
4000     double dx, dy, j, k, x, y;\r
4001 \r
4002     if( d_x == s_x ) {\r
4003         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4004 \r
4005         arrow[0].x = s_x + A_WIDTH;\r
4006         arrow[0].y = s_y;\r
4007 \r
4008         arrow[1].x = s_x + A_WIDTH;\r
4009         arrow[1].y = d_y - h;\r
4010 \r
4011         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
4012         arrow[2].y = d_y - h;\r
4013 \r
4014         arrow[3].x = d_x;\r
4015         arrow[3].y = d_y;\r
4016 \r
4017         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
4018         arrow[4].y = d_y - h;\r
4019 \r
4020         arrow[5].x = s_x - A_WIDTH;\r
4021         arrow[5].y = d_y - h;\r
4022 \r
4023         arrow[6].x = s_x - A_WIDTH;\r
4024         arrow[6].y = s_y;\r
4025     }\r
4026     else if( d_y == s_y ) {\r
4027         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4028 \r
4029         arrow[0].x = s_x;\r
4030         arrow[0].y = s_y + A_WIDTH;\r
4031 \r
4032         arrow[1].x = d_x - w;\r
4033         arrow[1].y = s_y + A_WIDTH;\r
4034 \r
4035         arrow[2].x = d_x - w;\r
4036         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
4037 \r
4038         arrow[3].x = d_x;\r
4039         arrow[3].y = d_y;\r
4040 \r
4041         arrow[4].x = d_x - w;\r
4042         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
4043 \r
4044         arrow[5].x = d_x - w;\r
4045         arrow[5].y = s_y - A_WIDTH;\r
4046 \r
4047         arrow[6].x = s_x;\r
4048         arrow[6].y = s_y - A_WIDTH;\r
4049     }\r
4050     else {\r
4051         /* [AS] Needed a lot of paper for this! :-) */\r
4052         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4053         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4054   \r
4055         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4056 \r
4057         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4058 \r
4059         x = s_x;\r
4060         y = s_y;\r
4061 \r
4062         arrow[0].x = Round(x - j);\r
4063         arrow[0].y = Round(y + j*dx);\r
4064 \r
4065         arrow[1].x = Round(x + j);\r
4066         arrow[1].y = Round(y - j*dx);\r
4067 \r
4068         if( d_x > s_x ) {\r
4069             x = (double) d_x - k;\r
4070             y = (double) d_y - k*dy;\r
4071         }\r
4072         else {\r
4073             x = (double) d_x + k;\r
4074             y = (double) d_y + k*dy;\r
4075         }\r
4076 \r
4077         arrow[2].x = Round(x + j);\r
4078         arrow[2].y = Round(y - j*dx);\r
4079 \r
4080         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4081         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4082 \r
4083         arrow[4].x = d_x;\r
4084         arrow[4].y = d_y;\r
4085 \r
4086         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4087         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4088 \r
4089         arrow[6].x = Round(x - j);\r
4090         arrow[6].y = Round(y + j*dx);\r
4091     }\r
4092 \r
4093     Polygon( hdc, arrow, 7 );\r
4094 }\r
4095 \r
4096 /* [AS] Draw an arrow between two squares */\r
4097 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4098 {\r
4099     int s_x, s_y, d_x, d_y;\r
4100     HPEN hpen;\r
4101     HPEN holdpen;\r
4102     HBRUSH hbrush;\r
4103     HBRUSH holdbrush;\r
4104     LOGBRUSH stLB;\r
4105 \r
4106     if( s_col == d_col && s_row == d_row ) {\r
4107         return;\r
4108     }\r
4109 \r
4110     /* Get source and destination points */\r
4111     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4112     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4113 \r
4114     if( d_y > s_y ) {\r
4115         d_y += squareSize / 4;\r
4116     }\r
4117     else if( d_y < s_y ) {\r
4118         d_y += 3 * squareSize / 4;\r
4119     }\r
4120     else {\r
4121         d_y += squareSize / 2;\r
4122     }\r
4123 \r
4124     if( d_x > s_x ) {\r
4125         d_x += squareSize / 4;\r
4126     }\r
4127     else if( d_x < s_x ) {\r
4128         d_x += 3 * squareSize / 4;\r
4129     }\r
4130     else {\r
4131         d_x += squareSize / 2;\r
4132     }\r
4133 \r
4134     s_x += squareSize / 2;\r
4135     s_y += squareSize / 2;\r
4136 \r
4137     /* Adjust width */\r
4138     A_WIDTH = squareSize / 14;\r
4139 \r
4140     /* Draw */\r
4141     stLB.lbStyle = BS_SOLID;\r
4142     stLB.lbColor = appData.highlightArrowColor;\r
4143     stLB.lbHatch = 0;\r
4144 \r
4145     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4146     holdpen = SelectObject( hdc, hpen );\r
4147     hbrush = CreateBrushIndirect( &stLB );\r
4148     holdbrush = SelectObject( hdc, hbrush );\r
4149 \r
4150     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4151 \r
4152     SelectObject( hdc, holdpen );\r
4153     SelectObject( hdc, holdbrush );\r
4154     DeleteObject( hpen );\r
4155     DeleteObject( hbrush );\r
4156 }\r
4157 \r
4158 BOOL HasHighlightInfo()\r
4159 {\r
4160     BOOL result = FALSE;\r
4161 \r
4162     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4163         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4164     {\r
4165         result = TRUE;\r
4166     }\r
4167 \r
4168     return result;\r
4169 }\r
4170 \r
4171 BOOL IsDrawArrowEnabled()\r
4172 {\r
4173     BOOL result = FALSE;\r
4174 \r
4175     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4176         result = TRUE;\r
4177     }\r
4178 \r
4179     return result;\r
4180 }\r
4181 \r
4182 VOID DrawArrowHighlight( HDC hdc )\r
4183 {\r
4184     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4185         DrawArrowBetweenSquares( hdc,\r
4186             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4187             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4188     }\r
4189 }\r
4190 \r
4191 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4192 {\r
4193     HRGN result = NULL;\r
4194 \r
4195     if( HasHighlightInfo() ) {\r
4196         int x1, y1, x2, y2;\r
4197         int sx, sy, dx, dy;\r
4198 \r
4199         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4200         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4201 \r
4202         sx = MIN( x1, x2 );\r
4203         sy = MIN( y1, y2 );\r
4204         dx = MAX( x1, x2 ) + squareSize;\r
4205         dy = MAX( y1, y2 ) + squareSize;\r
4206 \r
4207         result = CreateRectRgn( sx, sy, dx, dy );\r
4208     }\r
4209 \r
4210     return result;\r
4211 }\r
4212 \r
4213 /*\r
4214     Warning: this function modifies the behavior of several other functions. \r
4215     \r
4216     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4217     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4218     repaint is scattered all over the place, which is not good for features such as\r
4219     "arrow highlighting" that require a full repaint of the board.\r
4220 \r
4221     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4222     user interaction, when speed is not so important) but especially to avoid errors\r
4223     in the displayed graphics.\r
4224 \r
4225     In such patched places, I always try refer to this function so there is a single\r
4226     place to maintain knowledge.\r
4227     \r
4228     To restore the original behavior, just return FALSE unconditionally.\r
4229 */\r
4230 BOOL IsFullRepaintPreferrable()\r
4231 {\r
4232     BOOL result = FALSE;\r
4233 \r
4234     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4235         /* Arrow may appear on the board */\r
4236         result = TRUE;\r
4237     }\r
4238 \r
4239     return result;\r
4240 }\r
4241 \r
4242 /* \r
4243     This function is called by DrawPosition to know whether a full repaint must\r
4244     be forced or not.\r
4245 \r
4246     Only DrawPosition may directly call this function, which makes use of \r
4247     some state information. Other function should call DrawPosition specifying \r
4248     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4249 */\r
4250 BOOL DrawPositionNeedsFullRepaint()\r
4251 {\r
4252     BOOL result = FALSE;\r
4253 \r
4254     /* \r
4255         Probably a slightly better policy would be to trigger a full repaint\r
4256         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4257         but animation is fast enough that it's difficult to notice.\r
4258     */\r
4259     if( animInfo.piece == EmptySquare ) {\r
4260         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4261             result = TRUE;\r
4262         }\r
4263     }\r
4264 \r
4265     return result;\r
4266 }\r
4267 \r
4268 VOID\r
4269 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4270 {\r
4271   int row, column, x, y, square_color, piece_color;\r
4272   ChessSquare piece;\r
4273   HBRUSH oldBrush;\r
4274   HDC texture_hdc = NULL;\r
4275 \r
4276   /* [AS] Initialize background textures if needed */\r
4277   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4278       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4279       if( backTextureSquareSize != squareSize \r
4280        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4281           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4282           backTextureSquareSize = squareSize;\r
4283           RebuildTextureSquareInfo();\r
4284       }\r
4285 \r
4286       texture_hdc = CreateCompatibleDC( hdc );\r
4287   }\r
4288 \r
4289   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4290     for (column = 0; column < BOARD_WIDTH; column++) {\r
4291   \r
4292       SquareToPos(row, column, &x, &y);\r
4293 \r
4294       piece = board[row][column];\r
4295 \r
4296       square_color = ((column + row) % 2) == 1;\r
4297       if( gameInfo.variant == VariantXiangqi ) {\r
4298           square_color = !InPalace(row, column);\r
4299           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4300           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4301       }\r
4302       piece_color = (int) piece < (int) BlackPawn;\r
4303 \r
4304 \r
4305       /* [HGM] holdings file: light square or black */\r
4306       if(column == BOARD_LEFT-2) {\r
4307             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4308                 square_color = 1;\r
4309             else {\r
4310                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4311                 continue;\r
4312             }\r
4313       } else\r
4314       if(column == BOARD_RGHT + 1 ) {\r
4315             if( row < gameInfo.holdingsSize )\r
4316                 square_color = 1;\r
4317             else {\r
4318                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4319                 continue;\r
4320             }\r
4321       }\r
4322       if(column == BOARD_LEFT-1 ) /* left align */\r
4323             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4324       else if( column == BOARD_RGHT) /* right align */\r
4325             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4326       else\r
4327       if (appData.monoMode) {\r
4328         if (piece == EmptySquare) {\r
4329           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4330                  square_color ? WHITENESS : BLACKNESS);\r
4331         } else {\r
4332           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4333         }\r
4334       } \r
4335       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4336           /* [AS] Draw the square using a texture bitmap */\r
4337           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4338           int r = row, c = column; // [HGM] do not flip board in flipView\r
4339           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4340 \r
4341           DrawTile( x, y, \r
4342               squareSize, squareSize, \r
4343               hdc, \r
4344               texture_hdc,\r
4345               backTextureSquareInfo[r][c].mode,\r
4346               backTextureSquareInfo[r][c].x,\r
4347               backTextureSquareInfo[r][c].y );\r
4348 \r
4349           SelectObject( texture_hdc, hbm );\r
4350 \r
4351           if (piece != EmptySquare) {\r
4352               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4353           }\r
4354       }\r
4355       else {\r
4356         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4357 \r
4358         oldBrush = SelectObject(hdc, brush );\r
4359         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4360         SelectObject(hdc, oldBrush);\r
4361         if (piece != EmptySquare)\r
4362           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4363       }\r
4364     }\r
4365   }\r
4366 \r
4367   if( texture_hdc != NULL ) {\r
4368     DeleteDC( texture_hdc );\r
4369   }\r
4370 }\r
4371 \r
4372 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4373 void fputDW(FILE *f, int x)\r
4374 {\r
4375         fputc(x     & 255, f);\r
4376         fputc(x>>8  & 255, f);\r
4377         fputc(x>>16 & 255, f);\r
4378         fputc(x>>24 & 255, f);\r
4379 }\r
4380 \r
4381 #define MAX_CLIPS 200   /* more than enough */\r
4382 \r
4383 VOID\r
4384 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4385 {\r
4386 //  HBITMAP bufferBitmap;\r
4387   BITMAP bi;\r
4388 //  RECT Rect;\r
4389   HDC tmphdc;\r
4390   HBITMAP hbm;\r
4391   int w = 100, h = 50;\r
4392 \r
4393   if(logo == NULL) return;\r
4394 //  GetClientRect(hwndMain, &Rect);\r
4395 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4396 //                                      Rect.bottom-Rect.top+1);\r
4397   tmphdc = CreateCompatibleDC(hdc);\r
4398   hbm = SelectObject(tmphdc, logo);\r
4399   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4400             w = bi.bmWidth;\r
4401             h = bi.bmHeight;\r
4402   }\r
4403   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4404                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4405   SelectObject(tmphdc, hbm);\r
4406   DeleteDC(tmphdc);\r
4407 }\r
4408 \r
4409 VOID\r
4410 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4411 {\r
4412   static Board lastReq, lastDrawn;\r
4413   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4414   static int lastDrawnFlipView = 0;\r
4415   static int lastReqValid = 0, lastDrawnValid = 0;\r
4416   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4417   HDC tmphdc;\r
4418   HDC hdcmem;\r
4419   HBITMAP bufferBitmap;\r
4420   HBITMAP oldBitmap;\r
4421   RECT Rect;\r
4422   HRGN clips[MAX_CLIPS];\r
4423   ChessSquare dragged_piece = EmptySquare;\r
4424 \r
4425   /* I'm undecided on this - this function figures out whether a full\r
4426    * repaint is necessary on its own, so there's no real reason to have the\r
4427    * caller tell it that.  I think this can safely be set to FALSE - but\r
4428    * if we trust the callers not to request full repaints unnessesarily, then\r
4429    * we could skip some clipping work.  In other words, only request a full\r
4430    * redraw when the majority of pieces have changed positions (ie. flip, \r
4431    * gamestart and similar)  --Hawk\r
4432    */\r
4433   Boolean fullrepaint = repaint;\r
4434 \r
4435   if( DrawPositionNeedsFullRepaint() ) {\r
4436       fullrepaint = TRUE;\r
4437   }\r
4438 \r
4439 #if 0\r
4440   if( fullrepaint ) {\r
4441       static int repaint_count = 0;\r
4442       char buf[128];\r
4443 \r
4444       repaint_count++;\r
4445       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4446       OutputDebugString( buf );\r
4447   }\r
4448 #endif\r
4449 \r
4450   if (board == NULL) {\r
4451     if (!lastReqValid) {\r
4452       return;\r
4453     }\r
4454     board = lastReq;\r
4455   } else {\r
4456     CopyBoard(lastReq, board);\r
4457     lastReqValid = 1;\r
4458   }\r
4459 \r
4460   if (doingSizing) {\r
4461     return;\r
4462   }\r
4463 \r
4464   if (IsIconic(hwndMain)) {\r
4465     return;\r
4466   }\r
4467 \r
4468   if (hdc == NULL) {\r
4469     hdc = GetDC(hwndMain);\r
4470     if (!appData.monoMode) {\r
4471       SelectPalette(hdc, hPal, FALSE);\r
4472       RealizePalette(hdc);\r
4473     }\r
4474     releaseDC = TRUE;\r
4475   } else {\r
4476     releaseDC = FALSE;\r
4477   }\r
4478 \r
4479 #if 0\r
4480   fprintf(debugFP, "*******************************\n"\r
4481                    "repaint = %s\n"\r
4482                    "dragInfo.from (%d,%d)\n"\r
4483                    "dragInfo.start (%d,%d)\n"\r
4484                    "dragInfo.pos (%d,%d)\n"\r
4485                    "dragInfo.lastpos (%d,%d)\n", \r
4486                     repaint ? "TRUE" : "FALSE",\r
4487                     dragInfo.from.x, dragInfo.from.y, \r
4488                     dragInfo.start.x, dragInfo.start.y,\r
4489                     dragInfo.pos.x, dragInfo.pos.y,\r
4490                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4491   fprintf(debugFP, "prev:  ");\r
4492   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4493     for (column = 0; column < BOARD_WIDTH; column++) {\r
4494       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4495     }\r
4496   }\r
4497   fprintf(debugFP, "\n");\r
4498   fprintf(debugFP, "board: ");\r
4499   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4500     for (column = 0; column < BOARD_WIDTH; column++) {\r
4501       fprintf(debugFP, "%d ", board[row][column]);\r
4502     }\r
4503   }\r
4504   fprintf(debugFP, "\n");\r
4505   fflush(debugFP);\r
4506 #endif\r
4507 \r
4508   /* Create some work-DCs */\r
4509   hdcmem = CreateCompatibleDC(hdc);\r
4510   tmphdc = CreateCompatibleDC(hdc);\r
4511 \r
4512   /* If dragging is in progress, we temporarely remove the piece */\r
4513   /* [HGM] or temporarily decrease count if stacked              */\r
4514   /*       !! Moved to before board compare !!                   */\r
4515   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4516     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4517     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4518             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4519         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4520     } else \r
4521     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4522             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4523         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4524     } else \r
4525         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4526   }\r
4527 \r
4528   /* Figure out which squares need updating by comparing the \r
4529    * newest board with the last drawn board and checking if\r
4530    * flipping has changed.\r
4531    */\r
4532   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4533     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4534       for (column = 0; column < BOARD_WIDTH; column++) {\r
4535         if (lastDrawn[row][column] != board[row][column]) {\r
4536           SquareToPos(row, column, &x, &y);\r
4537           clips[num_clips++] =\r
4538             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4539         }\r
4540       }\r
4541     }\r
4542     for (i=0; i<2; i++) {\r
4543       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4544           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4545         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4546             lastDrawnHighlight.sq[i].y >= 0) {\r
4547           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4548                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4549           clips[num_clips++] =\r
4550             CreateRectRgn(x - lineGap, y - lineGap, \r
4551                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4552         }\r
4553         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4554           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4555           clips[num_clips++] =\r
4556             CreateRectRgn(x - lineGap, y - lineGap, \r
4557                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4558         }\r
4559       }\r
4560     }\r
4561     for (i=0; i<2; i++) {\r
4562       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4563           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4564         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4565             lastDrawnPremove.sq[i].y >= 0) {\r
4566           SquareToPos(lastDrawnPremove.sq[i].y,\r
4567                       lastDrawnPremove.sq[i].x, &x, &y);\r
4568           clips[num_clips++] =\r
4569             CreateRectRgn(x - lineGap, y - lineGap, \r
4570                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4571         }\r
4572         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4573             premoveHighlightInfo.sq[i].y >= 0) {\r
4574           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4575                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4576           clips[num_clips++] =\r
4577             CreateRectRgn(x - lineGap, y - lineGap, \r
4578                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4579         }\r
4580       }\r
4581     }\r
4582   } else {\r
4583     fullrepaint = TRUE;\r
4584   }\r
4585 \r
4586   /* Create a buffer bitmap - this is the actual bitmap\r
4587    * being written to.  When all the work is done, we can\r
4588    * copy it to the real DC (the screen).  This avoids\r
4589    * the problems with flickering.\r
4590    */\r
4591   GetClientRect(hwndMain, &Rect);\r
4592   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4593                                         Rect.bottom-Rect.top+1);\r
4594   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4595   if (!appData.monoMode) {\r
4596     SelectPalette(hdcmem, hPal, FALSE);\r
4597   }\r
4598 \r
4599   /* Create clips for dragging */\r
4600   if (!fullrepaint) {\r
4601     if (dragInfo.from.x >= 0) {\r
4602       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4603       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4604     }\r
4605     if (dragInfo.start.x >= 0) {\r
4606       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4607       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4608     }\r
4609     if (dragInfo.pos.x >= 0) {\r
4610       x = dragInfo.pos.x - squareSize / 2;\r
4611       y = dragInfo.pos.y - squareSize / 2;\r
4612       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4613     }\r
4614     if (dragInfo.lastpos.x >= 0) {\r
4615       x = dragInfo.lastpos.x - squareSize / 2;\r
4616       y = dragInfo.lastpos.y - squareSize / 2;\r
4617       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4618     }\r
4619   }\r
4620 \r
4621   /* Are we animating a move?  \r
4622    * If so, \r
4623    *   - remove the piece from the board (temporarely)\r
4624    *   - calculate the clipping region\r
4625    */\r
4626   if (!fullrepaint) {\r
4627     if (animInfo.piece != EmptySquare) {\r
4628       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4629       x = boardRect.left + animInfo.lastpos.x;\r
4630       y = boardRect.top + animInfo.lastpos.y;\r
4631       x2 = boardRect.left + animInfo.pos.x;\r
4632       y2 = boardRect.top + animInfo.pos.y;\r
4633       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4634       /* Slight kludge.  The real problem is that after AnimateMove is\r
4635          done, the position on the screen does not match lastDrawn.\r
4636          This currently causes trouble only on e.p. captures in\r
4637          atomic, where the piece moves to an empty square and then\r
4638          explodes.  The old and new positions both had an empty square\r
4639          at the destination, but animation has drawn a piece there and\r
4640          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4641       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4642     }\r
4643   }\r
4644 \r
4645   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4646   if (num_clips == 0)\r
4647     fullrepaint = TRUE;\r
4648 \r
4649   /* Set clipping on the memory DC */\r
4650   if (!fullrepaint) {\r
4651     SelectClipRgn(hdcmem, clips[0]);\r
4652     for (x = 1; x < num_clips; x++) {\r
4653       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4654         abort();  // this should never ever happen!\r
4655     }\r
4656   }\r
4657 \r
4658   /* Do all the drawing to the memory DC */\r
4659   if(explodeInfo.radius) { // [HGM] atomic\r
4660         HBRUSH oldBrush;\r
4661         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4662         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4663         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4664         x += squareSize/2;\r
4665         y += squareSize/2;\r
4666         if(!fullrepaint) {\r
4667           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4668           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4669         }\r
4670         DrawGridOnDC(hdcmem);\r
4671         DrawHighlightsOnDC(hdcmem);\r
4672         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4673         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4674         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4675         SelectObject(hdcmem, oldBrush);\r
4676   } else {\r
4677     DrawGridOnDC(hdcmem);\r
4678     DrawHighlightsOnDC(hdcmem);\r
4679     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4680   }\r
4681   if(logoHeight) {\r
4682         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4683         if(appData.autoLogo) {\r
4684           \r
4685           switch(gameMode) { // pick logos based on game mode\r
4686             case IcsObserving:\r
4687                 whiteLogo = second.programLogo; // ICS logo\r
4688                 blackLogo = second.programLogo;\r
4689             default:\r
4690                 break;\r
4691             case IcsPlayingWhite:\r
4692                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4693                 blackLogo = second.programLogo; // ICS logo\r
4694                 break;\r
4695             case IcsPlayingBlack:\r
4696                 whiteLogo = second.programLogo; // ICS logo\r
4697                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4698                 break;\r
4699             case TwoMachinesPlay:\r
4700                 if(first.twoMachinesColor[0] == 'b') {\r
4701                     whiteLogo = second.programLogo;\r
4702                     blackLogo = first.programLogo;\r
4703                 }\r
4704                 break;\r
4705             case MachinePlaysWhite:\r
4706                 blackLogo = userLogo;\r
4707                 break;\r
4708             case MachinePlaysBlack:\r
4709                 whiteLogo = userLogo;\r
4710                 blackLogo = first.programLogo;\r
4711           }\r
4712         }\r
4713         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4714         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4715   }\r
4716 \r
4717   if( appData.highlightMoveWithArrow ) {\r
4718     DrawArrowHighlight(hdcmem);\r
4719   }\r
4720 \r
4721   DrawCoordsOnDC(hdcmem);\r
4722 \r
4723   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4724                  /* to make sure lastDrawn contains what is actually drawn */\r
4725 \r
4726   /* Put the dragged piece back into place and draw it (out of place!) */\r
4727     if (dragged_piece != EmptySquare) {\r
4728     /* [HGM] or restack */\r
4729     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4730                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4731     else\r
4732     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4733                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4734     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4735     x = dragInfo.pos.x - squareSize / 2;\r
4736     y = dragInfo.pos.y - squareSize / 2;\r
4737     DrawPieceOnDC(hdcmem, dragged_piece,\r
4738                   ((int) dragged_piece < (int) BlackPawn), \r
4739                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4740   }   \r
4741   \r
4742   /* Put the animated piece back into place and draw it */\r
4743   if (animInfo.piece != EmptySquare) {\r
4744     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4745     x = boardRect.left + animInfo.pos.x;\r
4746     y = boardRect.top + animInfo.pos.y;\r
4747     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4748                   ((int) animInfo.piece < (int) BlackPawn),\r
4749                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4750   }\r
4751 \r
4752   /* Release the bufferBitmap by selecting in the old bitmap \r
4753    * and delete the memory DC\r
4754    */\r
4755   SelectObject(hdcmem, oldBitmap);\r
4756   DeleteDC(hdcmem);\r
4757 \r
4758   /* Set clipping on the target DC */\r
4759   if (!fullrepaint) {\r
4760     SelectClipRgn(hdc, clips[0]);\r
4761     for (x = 1; x < num_clips; x++) {\r
4762       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4763         abort();   // this should never ever happen!\r
4764     } \r
4765   }\r
4766 \r
4767   /* Copy the new bitmap onto the screen in one go.\r
4768    * This way we avoid any flickering\r
4769    */\r
4770   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4771   BitBlt(hdc, boardRect.left, boardRect.top,\r
4772          boardRect.right - boardRect.left,\r
4773          boardRect.bottom - boardRect.top,\r
4774          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4775   if(saveDiagFlag) { \r
4776     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4777     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4778 \r
4779     GetObject(bufferBitmap, sizeof(b), &b);\r
4780     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4781         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4782         bih.biWidth = b.bmWidth;\r
4783         bih.biHeight = b.bmHeight;\r
4784         bih.biPlanes = 1;\r
4785         bih.biBitCount = b.bmBitsPixel;\r
4786         bih.biCompression = 0;\r
4787         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4788         bih.biXPelsPerMeter = 0;\r
4789         bih.biYPelsPerMeter = 0;\r
4790         bih.biClrUsed = 0;\r
4791         bih.biClrImportant = 0;\r
4792 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4793 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4794         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4795 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4796 \r
4797 #if 1\r
4798         wb = b.bmWidthBytes;\r
4799         // count colors\r
4800         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4801                 int k = ((int*) pData)[i];\r
4802                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4803                 if(j >= 16) break;\r
4804                 color[j] = k;\r
4805                 if(j >= nrColors) nrColors = j+1;\r
4806         }\r
4807         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4808                 INT p = 0;\r
4809                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4810                     for(w=0; w<(wb>>2); w+=2) {\r
4811                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4812                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4813                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4814                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4815                         pData[p++] = m | j<<4;\r
4816                     }\r
4817                     while(p&3) pData[p++] = 0;\r
4818                 }\r
4819                 fac = 3;\r
4820                 wb = ((wb+31)>>5)<<2;\r
4821         }\r
4822         // write BITMAPFILEHEADER\r
4823         fprintf(diagFile, "BM");\r
4824         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4825         fputDW(diagFile, 0);\r
4826         fputDW(diagFile, 0x36 + (fac?64:0));\r
4827         // write BITMAPINFOHEADER\r
4828         fputDW(diagFile, 40);\r
4829         fputDW(diagFile, b.bmWidth);\r
4830         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4831         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4832         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4833         fputDW(diagFile, 0);\r
4834         fputDW(diagFile, 0);\r
4835         fputDW(diagFile, 0);\r
4836         fputDW(diagFile, 0);\r
4837         fputDW(diagFile, 0);\r
4838         fputDW(diagFile, 0);\r
4839         // write color table\r
4840         if(fac)\r
4841         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4842         // write bitmap data\r
4843         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4844                 fputc(pData[i], diagFile);\r
4845 #endif\r
4846      }\r
4847   }\r
4848 \r
4849   SelectObject(tmphdc, oldBitmap);\r
4850 \r
4851   /* Massive cleanup */\r
4852   for (x = 0; x < num_clips; x++)\r
4853     DeleteObject(clips[x]);\r
4854 \r
4855   DeleteDC(tmphdc);\r
4856   DeleteObject(bufferBitmap);\r
4857 \r
4858   if (releaseDC) \r
4859     ReleaseDC(hwndMain, hdc);\r
4860   \r
4861   if (lastDrawnFlipView != flipView) {\r
4862     if (flipView)\r
4863       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4864     else\r
4865       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4866   }\r
4867 \r
4868 /*  CopyBoard(lastDrawn, board);*/\r
4869   lastDrawnHighlight = highlightInfo;\r
4870   lastDrawnPremove   = premoveHighlightInfo;\r
4871   lastDrawnFlipView = flipView;\r
4872   lastDrawnValid = 1;\r
4873 }\r
4874 \r
4875 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4876 int\r
4877 SaveDiagram(f)\r
4878      FILE *f;\r
4879 {\r
4880     saveDiagFlag = 1; diagFile = f;\r
4881     HDCDrawPosition(NULL, TRUE, NULL);\r
4882 \r
4883     saveDiagFlag = 0;\r
4884 \r
4885 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4886     \r
4887     fclose(f);\r
4888     return TRUE;\r
4889 }\r
4890 \r
4891 \r
4892 /*---------------------------------------------------------------------------*\\r
4893 | CLIENT PAINT PROCEDURE\r
4894 |   This is the main event-handler for the WM_PAINT message.\r
4895 |\r
4896 \*---------------------------------------------------------------------------*/\r
4897 VOID\r
4898 PaintProc(HWND hwnd)\r
4899 {\r
4900   HDC         hdc;\r
4901   PAINTSTRUCT ps;\r
4902   HFONT       oldFont;\r
4903 \r
4904   if((hdc = BeginPaint(hwnd, &ps))) {\r
4905     if (IsIconic(hwnd)) {\r
4906       DrawIcon(hdc, 2, 2, iconCurrent);\r
4907     } else {\r
4908       if (!appData.monoMode) {\r
4909         SelectPalette(hdc, hPal, FALSE);\r
4910         RealizePalette(hdc);\r
4911       }\r
4912       HDCDrawPosition(hdc, 1, NULL);\r
4913       oldFont =\r
4914         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4915       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4916                  ETO_CLIPPED|ETO_OPAQUE,\r
4917                  &messageRect, messageText, strlen(messageText), NULL);\r
4918       SelectObject(hdc, oldFont);\r
4919       DisplayBothClocks();\r
4920     }\r
4921     EndPaint(hwnd,&ps);\r
4922   }\r
4923 \r
4924   return;\r
4925 }\r
4926 \r
4927 \r
4928 /*\r
4929  * If the user selects on a border boundary, return -1; if off the board,\r
4930  *   return -2.  Otherwise map the event coordinate to the square.\r
4931  * The offset boardRect.left or boardRect.top must already have been\r
4932  *   subtracted from x.\r
4933  */\r
4934 int\r
4935 EventToSquare(int x)\r
4936 {\r
4937   if (x <= 0)\r
4938     return -2;\r
4939   if (x < lineGap)\r
4940     return -1;\r
4941   x -= lineGap;\r
4942   if ((x % (squareSize + lineGap)) >= squareSize)\r
4943     return -1;\r
4944   x /= (squareSize + lineGap);\r
4945   if (x >= BOARD_SIZE)\r
4946     return -2;\r
4947   return x;\r
4948 }\r
4949 \r
4950 typedef struct {\r
4951   char piece;\r
4952   int command;\r
4953   char* name;\r
4954 } DropEnable;\r
4955 \r
4956 DropEnable dropEnables[] = {\r
4957   { 'P', DP_Pawn, "Pawn" },\r
4958   { 'N', DP_Knight, "Knight" },\r
4959   { 'B', DP_Bishop, "Bishop" },\r
4960   { 'R', DP_Rook, "Rook" },\r
4961   { 'Q', DP_Queen, "Queen" },\r
4962 };\r
4963 \r
4964 VOID\r
4965 SetupDropMenu(HMENU hmenu)\r
4966 {\r
4967   int i, count, enable;\r
4968   char *p;\r
4969   extern char white_holding[], black_holding[];\r
4970   char item[MSG_SIZ];\r
4971 \r
4972   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4973     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4974                dropEnables[i].piece);\r
4975     count = 0;\r
4976     while (p && *p++ == dropEnables[i].piece) count++;\r
4977     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4978     enable = count > 0 || !appData.testLegality\r
4979       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4980                       && !appData.icsActive);\r
4981     ModifyMenu(hmenu, dropEnables[i].command,\r
4982                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4983                dropEnables[i].command, item);\r
4984   }\r
4985 }\r
4986 \r
4987 /* Event handler for mouse messages */\r
4988 VOID\r
4989 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4990 {\r
4991   int x, y;\r
4992   POINT pt;\r
4993   static int recursive = 0;\r
4994   HMENU hmenu;\r
4995 //  BOOLEAN needsRedraw = FALSE;\r
4996   BOOLEAN saveAnimate;\r
4997   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4998   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4999   ChessMove moveType;\r
5000 \r
5001   if (recursive) {\r
5002     if (message == WM_MBUTTONUP) {\r
5003       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
5004          to the middle button: we simulate pressing the left button too!\r
5005          */\r
5006       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
5007       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
5008     }\r
5009     return;\r
5010   }\r
5011   recursive++;\r
5012   \r
5013   pt.x = LOWORD(lParam);\r
5014   pt.y = HIWORD(lParam);\r
5015   x = EventToSquare(pt.x - boardRect.left);\r
5016   y = EventToSquare(pt.y - boardRect.top);\r
5017   if (!flipView && y >= 0) {\r
5018     y = BOARD_HEIGHT - 1 - y;\r
5019   }\r
5020   if (flipView && x >= 0) {\r
5021     x = BOARD_WIDTH - 1 - x;\r
5022   }\r
5023 \r
5024   switch (message) {\r
5025   case WM_LBUTTONDOWN:\r
5026     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
5027         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
5028         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
5029         if(gameInfo.holdingsWidth && \r
5030                 (WhiteOnMove(currentMove) \r
5031                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
5032                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
5033             // click in right holdings, for determining promotion piece\r
5034             ChessSquare p = boards[currentMove][y][x];\r
5035             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
5036             if(p != EmptySquare) {\r
5037                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
5038                 fromX = fromY = -1;\r
5039                 break;\r
5040             }\r
5041         }\r
5042         DrawPosition(FALSE, boards[currentMove]);\r
5043         break;\r
5044     }\r
5045     ErrorPopDown();\r
5046     sameAgain = FALSE;\r
5047     if (y == -2) {\r
5048       /* Downclick vertically off board; check if on clock */\r
5049       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5050         if (gameMode == EditPosition) {\r
5051           SetWhiteToPlayEvent();\r
5052         } else if (gameMode == IcsPlayingBlack ||\r
5053                    gameMode == MachinePlaysWhite) {\r
5054           CallFlagEvent();\r
5055         } else if (gameMode == EditGame) {\r
5056           AdjustClock(flipClock, -1);\r
5057         }\r
5058       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5059         if (gameMode == EditPosition) {\r
5060           SetBlackToPlayEvent();\r
5061         } else if (gameMode == IcsPlayingWhite ||\r
5062                    gameMode == MachinePlaysBlack) {\r
5063           CallFlagEvent();\r
5064         } else if (gameMode == EditGame) {\r
5065           AdjustClock(!flipClock, -1);\r
5066         }\r
5067       }\r
5068       if (!appData.highlightLastMove) {\r
5069         ClearHighlights();\r
5070         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
5071       }\r
5072       fromX = fromY = -1;\r
5073       dragInfo.start.x = dragInfo.start.y = -1;\r
5074       dragInfo.from = dragInfo.start;\r
5075       break;\r
5076     } else if (x < 0 || y < 0\r
5077       /* [HGM] block clicks between board and holdings */\r
5078               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
5079               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
5080               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
5081         /* EditPosition, empty square, or different color piece;\r
5082            click-click move is possible */\r
5083                                ) {\r
5084       break;\r
5085     } else if (fromX == x && fromY == y) {\r
5086       /* Downclick on same square again */\r
5087       ClearHighlights();\r
5088       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5089       sameAgain = TRUE;  \r
5090     } else if (fromX != -1 &&\r
5091                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5092                                                                         ) {\r
5093       /* Downclick on different square. */\r
5094       /* [HGM] if on holdings file, should count as new first click ! */\r
5095       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5096         toX = x;\r
5097         toY = y;\r
5098         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5099            to make sure move is legal before showing promotion popup */\r
5100         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5101         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5102                 fromX = fromY = -1; \r
5103                 ClearHighlights();\r
5104                 DrawPosition(FALSE, boards[currentMove]);\r
5105                 break; \r
5106         } else \r
5107         if(moveType != ImpossibleMove) {\r
5108           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5109           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5110             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5111               appData.alwaysPromoteToQueen)) {\r
5112                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5113                   if (!appData.highlightLastMove) {\r
5114                       ClearHighlights();\r
5115                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5116                   }\r
5117           } else\r
5118           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5119                   SetHighlights(fromX, fromY, toX, toY);\r
5120                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5121                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5122                      If promotion to Q is legal, all are legal! */\r
5123                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5124                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5125                     // kludge to temporarily execute move on display, wthout promotng yet\r
5126                     promotionChoice = TRUE;\r
5127                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5128                     boards[currentMove][toY][toX] = p;\r
5129                     DrawPosition(FALSE, boards[currentMove]);\r
5130                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5131                     boards[currentMove][toY][toX] = q;\r
5132                   } else\r
5133                   PromotionPopup(hwnd);\r
5134           } else {       /* not a promotion */\r
5135              if (appData.animate || appData.highlightLastMove) {\r
5136                  SetHighlights(fromX, fromY, toX, toY);\r
5137              } else {\r
5138                  ClearHighlights();\r
5139              }\r
5140              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5141              fromX = fromY = -1;\r
5142              if (appData.animate && !appData.highlightLastMove) {\r
5143                   ClearHighlights();\r
5144                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5145              }\r
5146           }\r
5147           break;\r
5148         }\r
5149         if (gotPremove) {\r
5150             /* [HGM] it seemed that braces were missing here */\r
5151             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5152             fromX = fromY = -1;\r
5153             break;\r
5154         }\r
5155       }\r
5156       ClearHighlights();\r
5157       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5158     }\r
5159     /* First downclick, or restart on a square with same color piece */\r
5160     if (!frozen && OKToStartUserMove(x, y)) {\r
5161       fromX = x;\r
5162       fromY = y;\r
5163       dragInfo.lastpos = pt;\r
5164       dragInfo.from.x = fromX;\r
5165       dragInfo.from.y = fromY;\r
5166       dragInfo.start = dragInfo.from;\r
5167       SetCapture(hwndMain);\r
5168     } else {\r
5169       fromX = fromY = -1;\r
5170       dragInfo.start.x = dragInfo.start.y = -1;\r
5171       dragInfo.from = dragInfo.start;\r
5172       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5173     }\r
5174     break;\r
5175 \r
5176   case WM_LBUTTONUP:\r
5177     ReleaseCapture();\r
5178     if (fromX == -1) break;\r
5179     if (x == fromX && y == fromY) {\r
5180       dragInfo.from.x = dragInfo.from.y = -1;\r
5181       /* Upclick on same square */\r
5182       if (sameAgain) {\r
5183         /* Clicked same square twice: abort click-click move */\r
5184         fromX = fromY = -1;\r
5185         gotPremove = 0;\r
5186         ClearPremoveHighlights();\r
5187       } else {\r
5188         /* First square clicked: start click-click move */\r
5189         SetHighlights(fromX, fromY, -1, -1);\r
5190       }\r
5191       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5192     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5193       /* Errant click; ignore */\r
5194       break;\r
5195     } else {\r
5196       /* Finish drag move. */\r
5197     if (appData.debugMode) {\r
5198         fprintf(debugFP, "release\n");\r
5199     }\r
5200       dragInfo.from.x = dragInfo.from.y = -1;\r
5201       toX = x;\r
5202       toY = y;\r
5203       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5204       appData.animate = appData.animate && !appData.animateDragging;\r
5205       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5206       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5207                 fromX = fromY = -1; \r
5208                 ClearHighlights();\r
5209                 DrawPosition(FALSE, boards[currentMove]);\r
5210                 appData.animate = saveAnimate;\r
5211                 break; \r
5212       } else \r
5213       if(moveType != ImpossibleMove) {\r
5214           /* [HGM] use move type to determine if move is promotion.\r
5215              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5216           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5217             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5218               appData.alwaysPromoteToQueen)) \r
5219                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5220           else \r
5221           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5222                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5223                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5224                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5225                     // kludge to temporarily execute move on display, wthout promotng yet\r
5226                     promotionChoice = TRUE;\r
5227                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5228                     boards[currentMove][toY][toX] = p;\r
5229                     DrawPosition(FALSE, boards[currentMove]);\r
5230                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5231                     boards[currentMove][toY][toX] = q;\r
5232                     appData.animate = saveAnimate;\r
5233                     break;\r
5234                   } else\r
5235                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5236           } else {\r
5237             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5238                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5239                                         moveType == WhiteCapturesEnPassant || \r
5240                                         moveType == BlackCapturesEnPassant   ) )\r
5241                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5242             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5243           }\r
5244       }\r
5245       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5246       appData.animate = saveAnimate;\r
5247       fromX = fromY = -1;\r
5248       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5249         ClearHighlights();\r
5250       }\r
5251       if (appData.animate || appData.animateDragging ||\r
5252           appData.highlightDragging || gotPremove) {\r
5253         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5254       }\r
5255     }\r
5256     dragInfo.start.x = dragInfo.start.y = -1; \r
5257     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5258     break;\r
5259 \r
5260   case WM_MOUSEMOVE:\r
5261     if ((appData.animateDragging || appData.highlightDragging)\r
5262         && (wParam & MK_LBUTTON)\r
5263         && dragInfo.from.x >= 0) \r
5264     {\r
5265       BOOL full_repaint = FALSE;\r
5266 \r
5267       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5268       if (appData.animateDragging) {\r
5269         dragInfo.pos = pt;\r
5270       }\r
5271       if (appData.highlightDragging) {\r
5272         SetHighlights(fromX, fromY, x, y);\r
5273         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5274             full_repaint = TRUE;\r
5275         }\r
5276       }\r
5277       \r
5278       DrawPosition( full_repaint, NULL);\r
5279       \r
5280       dragInfo.lastpos = dragInfo.pos;\r
5281     }\r
5282     break;\r
5283 \r
5284   case WM_MOUSEWHEEL: // [DM]\r
5285     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5286        /* Mouse Wheel is being rolled forward\r
5287         * Play moves forward\r
5288         */\r
5289        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5290                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5291        /* Mouse Wheel is being rolled backward\r
5292         * Play moves backward\r
5293         */\r
5294        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5295                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5296     }\r
5297     break;\r
5298 \r
5299   case WM_MBUTTONDOWN:\r
5300   case WM_RBUTTONDOWN:\r
5301     ErrorPopDown();\r
5302     ReleaseCapture();\r
5303     fromX = fromY = -1;\r
5304     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5305     dragInfo.start.x = dragInfo.start.y = -1;\r
5306     dragInfo.from = dragInfo.start;\r
5307     dragInfo.lastpos = dragInfo.pos;\r
5308     if (appData.highlightDragging) {\r
5309       ClearHighlights();\r
5310     }\r
5311     if(y == -2) {\r
5312       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5313       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5314           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5315       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5316           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5317       }\r
5318     }\r
5319     DrawPosition(TRUE, NULL);\r
5320 \r
5321     switch (gameMode) {\r
5322     case EditPosition:\r
5323     case IcsExamining:\r
5324       if (x < 0 || y < 0) break;\r
5325       fromX = x;\r
5326       fromY = y;\r
5327       if (message == WM_MBUTTONDOWN) {\r
5328         buttonCount = 3;  /* even if system didn't think so */\r
5329         if (wParam & MK_SHIFT) \r
5330           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5331         else\r
5332           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5333       } else { /* message == WM_RBUTTONDOWN */\r
5334 #if 0\r
5335         if (buttonCount == 3) {\r
5336           if (wParam & MK_SHIFT) \r
5337             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5338           else\r
5339             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5340         } else {\r
5341           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5342         }\r
5343 #else\r
5344         /* Just have one menu, on the right button.  Windows users don't\r
5345            think to try the middle one, and sometimes other software steals\r
5346            it, or it doesn't really exist. */\r
5347         if(gameInfo.variant != VariantShogi)\r
5348             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5349         else\r
5350             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5351 #endif\r
5352       }\r
5353       break;\r
5354     case IcsPlayingWhite:\r
5355     case IcsPlayingBlack:\r
5356     case EditGame:\r
5357     case MachinePlaysWhite:\r
5358     case MachinePlaysBlack:\r
5359       if (appData.testLegality &&\r
5360           gameInfo.variant != VariantBughouse &&\r
5361           gameInfo.variant != VariantCrazyhouse) break;\r
5362       if (x < 0 || y < 0) break;\r
5363       fromX = x;\r
5364       fromY = y;\r
5365       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5366       SetupDropMenu(hmenu);\r
5367       MenuPopup(hwnd, pt, hmenu, -1);\r
5368       break;\r
5369     default:\r
5370       break;\r
5371     }\r
5372     break;\r
5373   }\r
5374 \r
5375   recursive--;\r
5376 }\r
5377 \r
5378 /* Preprocess messages for buttons in main window */\r
5379 LRESULT CALLBACK\r
5380 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5381 {\r
5382   int id = GetWindowLong(hwnd, GWL_ID);\r
5383   int i, dir;\r
5384 \r
5385   for (i=0; i<N_BUTTONS; i++) {\r
5386     if (buttonDesc[i].id == id) break;\r
5387   }\r
5388   if (i == N_BUTTONS) return 0;\r
5389   switch (message) {\r
5390   case WM_KEYDOWN:\r
5391     switch (wParam) {\r
5392     case VK_LEFT:\r
5393     case VK_RIGHT:\r
5394       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5395       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5396       return TRUE;\r
5397     }\r
5398     break;\r
5399   case WM_CHAR:\r
5400     switch (wParam) {\r
5401     case '\r':\r
5402       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5403       return TRUE;\r
5404     default:\r
5405       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5406         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5407         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5408         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5409         SetFocus(h);\r
5410         SendMessage(h, WM_CHAR, wParam, lParam);\r
5411         return TRUE;\r
5412       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5413         PopUpMoveDialog((char)wParam);\r
5414       }\r
5415       break;\r
5416     }\r
5417     break;\r
5418   }\r
5419   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5420 }\r
5421 \r
5422 /* Process messages for Promotion dialog box */\r
5423 LRESULT CALLBACK\r
5424 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5425 {\r
5426   char promoChar;\r
5427 \r
5428   switch (message) {\r
5429   case WM_INITDIALOG: /* message: initialize dialog box */\r
5430     /* Center the dialog over the application window */\r
5431     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5432     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5433       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5434        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5435                SW_SHOW : SW_HIDE);\r
5436     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5437     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5438        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5439          PieceToChar(WhiteAngel) != '~') ||\r
5440         (PieceToChar(BlackAngel) >= 'A' &&\r
5441          PieceToChar(BlackAngel) != '~')   ) ?\r
5442                SW_SHOW : SW_HIDE);\r
5443     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5444        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5445          PieceToChar(WhiteMarshall) != '~') ||\r
5446         (PieceToChar(BlackMarshall) >= 'A' &&\r
5447          PieceToChar(BlackMarshall) != '~')   ) ?\r
5448                SW_SHOW : SW_HIDE);\r
5449     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5450     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5451        gameInfo.variant != VariantShogi ?\r
5452                SW_SHOW : SW_HIDE);\r
5453     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5454        gameInfo.variant != VariantShogi ?\r
5455                SW_SHOW : SW_HIDE);\r
5456     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5457        gameInfo.variant == VariantShogi ?\r
5458                SW_SHOW : SW_HIDE);\r
5459     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5460        gameInfo.variant == VariantShogi ?\r
5461                SW_SHOW : SW_HIDE);\r
5462     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5463        gameInfo.variant == VariantSuper ?\r
5464                SW_SHOW : SW_HIDE);\r
5465     return TRUE;\r
5466 \r
5467   case WM_COMMAND: /* message: received a command */\r
5468     switch (LOWORD(wParam)) {\r
5469     case IDCANCEL:\r
5470       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5471       ClearHighlights();\r
5472       DrawPosition(FALSE, NULL);\r
5473       return TRUE;\r
5474     case PB_King:\r
5475       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5476       break;\r
5477     case PB_Queen:\r
5478       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5479       break;\r
5480     case PB_Rook:\r
5481       promoChar = PieceToChar(BlackRook);\r
5482       break;\r
5483     case PB_Bishop:\r
5484       promoChar = PieceToChar(BlackBishop);\r
5485       break;\r
5486     case PB_Chancellor:\r
5487       promoChar = PieceToChar(BlackMarshall);\r
5488       break;\r
5489     case PB_Archbishop:\r
5490       promoChar = PieceToChar(BlackAngel);\r
5491       break;\r
5492     case PB_Knight:\r
5493       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5494       break;\r
5495     default:\r
5496       return FALSE;\r
5497     }\r
5498     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5499     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5500        only show the popup when we are already sure the move is valid or\r
5501        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5502        will figure out it is a promotion from the promoChar. */\r
5503     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5504     if (!appData.highlightLastMove) {\r
5505       ClearHighlights();\r
5506       DrawPosition(FALSE, NULL);\r
5507     }\r
5508     return TRUE;\r
5509   }\r
5510   return FALSE;\r
5511 }\r
5512 \r
5513 /* Pop up promotion dialog */\r
5514 VOID\r
5515 PromotionPopup(HWND hwnd)\r
5516 {\r
5517   FARPROC lpProc;\r
5518 \r
5519   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5520   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5521     hwnd, (DLGPROC)lpProc);\r
5522   FreeProcInstance(lpProc);\r
5523 }\r
5524 \r
5525 /* Toggle ShowThinking */\r
5526 VOID\r
5527 ToggleShowThinking()\r
5528 {\r
5529   appData.showThinking = !appData.showThinking;\r
5530   ShowThinkingEvent();\r
5531 }\r
5532 \r
5533 VOID\r
5534 LoadGameDialog(HWND hwnd, char* title)\r
5535 {\r
5536   UINT number = 0;\r
5537   FILE *f;\r
5538   char fileTitle[MSG_SIZ];\r
5539   f = OpenFileDialog(hwnd, "rb", "",\r
5540                      appData.oldSaveStyle ? "gam" : "pgn",\r
5541                      GAME_FILT,\r
5542                      title, &number, fileTitle, NULL);\r
5543   if (f != NULL) {\r
5544     cmailMsgLoaded = FALSE;\r
5545     if (number == 0) {\r
5546       int error = GameListBuild(f);\r
5547       if (error) {\r
5548         DisplayError("Cannot build game list", error);\r
5549       } else if (!ListEmpty(&gameList) &&\r
5550                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5551         GameListPopUp(f, fileTitle);\r
5552         return;\r
5553       }\r
5554       GameListDestroy();\r
5555       number = 1;\r
5556     }\r
5557     LoadGame(f, number, fileTitle, FALSE);\r
5558   }\r
5559 }\r
5560 \r
5561 VOID\r
5562 ChangedConsoleFont()\r
5563 {\r
5564   CHARFORMAT cfmt;\r
5565   CHARRANGE tmpsel, sel;\r
5566   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5567   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5568   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5569   PARAFORMAT paraf;\r
5570 \r
5571   cfmt.cbSize = sizeof(CHARFORMAT);\r
5572   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5573   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5574   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5575    * size.  This was undocumented in the version of MSVC++ that I had\r
5576    * when I wrote the code, but is apparently documented now.\r
5577    */\r
5578   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5579   cfmt.bCharSet = f->lf.lfCharSet;\r
5580   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5581   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5582   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5583   /* Why are the following seemingly needed too? */\r
5584   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5585   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5586   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5587   tmpsel.cpMin = 0;\r
5588   tmpsel.cpMax = -1; /*999999?*/\r
5589   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5590   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5591   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5592    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5593    */\r
5594   paraf.cbSize = sizeof(paraf);\r
5595   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5596   paraf.dxStartIndent = 0;\r
5597   paraf.dxOffset = WRAP_INDENT;\r
5598   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5599   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5600 }\r
5601 \r
5602 /*---------------------------------------------------------------------------*\\r
5603  *\r
5604  * Window Proc for main window\r
5605  *\r
5606 \*---------------------------------------------------------------------------*/\r
5607 \r
5608 /* Process messages for main window, etc. */\r
5609 LRESULT CALLBACK\r
5610 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5611 {\r
5612   FARPROC lpProc;\r
5613   int wmId, wmEvent;\r
5614   char *defName;\r
5615   FILE *f;\r
5616   UINT number;\r
5617   char fileTitle[MSG_SIZ];\r
5618   char buf[MSG_SIZ];\r
5619   static SnapData sd;\r
5620 \r
5621   switch (message) {\r
5622 \r
5623   case WM_PAINT: /* message: repaint portion of window */\r
5624     PaintProc(hwnd);\r
5625     break;\r
5626 \r
5627   case WM_ERASEBKGND:\r
5628     if (IsIconic(hwnd)) {\r
5629       /* Cheat; change the message */\r
5630       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5631     } else {\r
5632       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5633     }\r
5634     break;\r
5635 \r
5636   case WM_LBUTTONDOWN:\r
5637   case WM_MBUTTONDOWN:\r
5638   case WM_RBUTTONDOWN:\r
5639   case WM_LBUTTONUP:\r
5640   case WM_MBUTTONUP:\r
5641   case WM_RBUTTONUP:\r
5642   case WM_MOUSEMOVE:\r
5643   case WM_MOUSEWHEEL:\r
5644     MouseEvent(hwnd, message, wParam, lParam);\r
5645     break;\r
5646 \r
5647   JAWS_KB_NAVIGATION\r
5648 \r
5649   case WM_CHAR:\r
5650     \r
5651     JAWS_ALT_INTERCEPT\r
5652 \r
5653     if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) { \r
5654         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5655         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5656         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5657         SetFocus(h);\r
5658         SendMessage(h, message, wParam, lParam);\r
5659     } else if(lParam != KF_REPEAT) {\r
5660         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5661                 PopUpMoveDialog((char)wParam);\r
5662         }\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_OptionsUCI:\r
6128       UciOptionsPopup(hwnd);\r
6129       break;\r
6130 \r
6131     case IDM_IcsOptions:\r
6132       IcsOptionsPopup(hwnd);\r
6133       break;\r
6134 \r
6135     case IDM_Fonts:\r
6136       FontsOptionsPopup(hwnd);\r
6137       break;\r
6138 \r
6139     case IDM_Sounds:\r
6140       SoundOptionsPopup(hwnd);\r
6141       break;\r
6142 \r
6143     case IDM_CommPort:\r
6144       CommPortOptionsPopup(hwnd);\r
6145       break;\r
6146 \r
6147     case IDM_LoadOptions:\r
6148       LoadOptionsPopup(hwnd);\r
6149       break;\r
6150 \r
6151     case IDM_SaveOptions:\r
6152       SaveOptionsPopup(hwnd);\r
6153       break;\r
6154 \r
6155     case IDM_TimeControl:\r
6156       TimeControlOptionsPopup(hwnd);\r
6157       break;\r
6158 \r
6159     case IDM_SaveSettings:\r
6160       SaveSettings(settingsFileName);\r
6161       break;\r
6162 \r
6163     case IDM_SaveSettingsOnExit:\r
6164       saveSettingsOnExit = !saveSettingsOnExit;\r
6165       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6166                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6167                                          MF_CHECKED : MF_UNCHECKED));\r
6168       break;\r
6169 \r
6170     case IDM_Hint:\r
6171       HintEvent();\r
6172       break;\r
6173 \r
6174     case IDM_Book:\r
6175       BookEvent();\r
6176       break;\r
6177 \r
6178     case IDM_AboutGame:\r
6179       AboutGameEvent();\r
6180       break;\r
6181 \r
6182     case IDM_Debug:\r
6183       appData.debugMode = !appData.debugMode;\r
6184       if (appData.debugMode) {\r
6185         char dir[MSG_SIZ];\r
6186         GetCurrentDirectory(MSG_SIZ, dir);\r
6187         SetCurrentDirectory(installDir);\r
6188         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6189         SetCurrentDirectory(dir);\r
6190         setbuf(debugFP, NULL);\r
6191       } else {\r
6192         fclose(debugFP);\r
6193         debugFP = NULL;\r
6194       }\r
6195       break;\r
6196 \r
6197     case IDM_HELPCONTENTS:\r
6198       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6199           !HtmlHelp(hwnd, "winboard.chm", 0, NULL)      ) {\r
6200           MessageBox (GetFocus(),\r
6201                     "Unable to activate help",\r
6202                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6203       }\r
6204       break;\r
6205 \r
6206     case IDM_HELPSEARCH:\r
6207         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6208             !HtmlHelp(hwnd, "winboard.chm", 0, NULL)    ) {\r
6209         MessageBox (GetFocus(),\r
6210                     "Unable to activate help",\r
6211                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6212       }\r
6213       break;\r
6214 \r
6215     case IDM_HELPHELP:\r
6216       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 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_ABOUT:\r
6224       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6225       DialogBox(hInst, \r
6226         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6227         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6228       FreeProcInstance(lpProc);\r
6229       break;\r
6230 \r
6231     case IDM_DirectCommand1:\r
6232       AskQuestionEvent("Direct Command",\r
6233                        "Send to chess program:", "", "1");\r
6234       break;\r
6235     case IDM_DirectCommand2:\r
6236       AskQuestionEvent("Direct Command",\r
6237                        "Send to second chess program:", "", "2");\r
6238       break;\r
6239 \r
6240     case EP_WhitePawn:\r
6241       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6242       fromX = fromY = -1;\r
6243       break;\r
6244 \r
6245     case EP_WhiteKnight:\r
6246       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6247       fromX = fromY = -1;\r
6248       break;\r
6249 \r
6250     case EP_WhiteBishop:\r
6251       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6252       fromX = fromY = -1;\r
6253       break;\r
6254 \r
6255     case EP_WhiteRook:\r
6256       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6257       fromX = fromY = -1;\r
6258       break;\r
6259 \r
6260     case EP_WhiteQueen:\r
6261       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6262       fromX = fromY = -1;\r
6263       break;\r
6264 \r
6265     case EP_WhiteFerz:\r
6266       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6267       fromX = fromY = -1;\r
6268       break;\r
6269 \r
6270     case EP_WhiteWazir:\r
6271       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6272       fromX = fromY = -1;\r
6273       break;\r
6274 \r
6275     case EP_WhiteAlfil:\r
6276       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6277       fromX = fromY = -1;\r
6278       break;\r
6279 \r
6280     case EP_WhiteCannon:\r
6281       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6282       fromX = fromY = -1;\r
6283       break;\r
6284 \r
6285     case EP_WhiteCardinal:\r
6286       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6287       fromX = fromY = -1;\r
6288       break;\r
6289 \r
6290     case EP_WhiteMarshall:\r
6291       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6292       fromX = fromY = -1;\r
6293       break;\r
6294 \r
6295     case EP_WhiteKing:\r
6296       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6297       fromX = fromY = -1;\r
6298       break;\r
6299 \r
6300     case EP_BlackPawn:\r
6301       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6302       fromX = fromY = -1;\r
6303       break;\r
6304 \r
6305     case EP_BlackKnight:\r
6306       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6307       fromX = fromY = -1;\r
6308       break;\r
6309 \r
6310     case EP_BlackBishop:\r
6311       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6312       fromX = fromY = -1;\r
6313       break;\r
6314 \r
6315     case EP_BlackRook:\r
6316       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6317       fromX = fromY = -1;\r
6318       break;\r
6319 \r
6320     case EP_BlackQueen:\r
6321       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6322       fromX = fromY = -1;\r
6323       break;\r
6324 \r
6325     case EP_BlackFerz:\r
6326       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6327       fromX = fromY = -1;\r
6328       break;\r
6329 \r
6330     case EP_BlackWazir:\r
6331       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6332       fromX = fromY = -1;\r
6333       break;\r
6334 \r
6335     case EP_BlackAlfil:\r
6336       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6337       fromX = fromY = -1;\r
6338       break;\r
6339 \r
6340     case EP_BlackCannon:\r
6341       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6342       fromX = fromY = -1;\r
6343       break;\r
6344 \r
6345     case EP_BlackCardinal:\r
6346       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6347       fromX = fromY = -1;\r
6348       break;\r
6349 \r
6350     case EP_BlackMarshall:\r
6351       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6352       fromX = fromY = -1;\r
6353       break;\r
6354 \r
6355     case EP_BlackKing:\r
6356       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6357       fromX = fromY = -1;\r
6358       break;\r
6359 \r
6360     case EP_EmptySquare:\r
6361       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6362       fromX = fromY = -1;\r
6363       break;\r
6364 \r
6365     case EP_ClearBoard:\r
6366       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6367       fromX = fromY = -1;\r
6368       break;\r
6369 \r
6370     case EP_White:\r
6371       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6372       fromX = fromY = -1;\r
6373       break;\r
6374 \r
6375     case EP_Black:\r
6376       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6377       fromX = fromY = -1;\r
6378       break;\r
6379 \r
6380     case EP_Promote:\r
6381       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6382       fromX = fromY = -1;\r
6383       break;\r
6384 \r
6385     case EP_Demote:\r
6386       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6387       fromX = fromY = -1;\r
6388       break;\r
6389 \r
6390     case DP_Pawn:\r
6391       DropMenuEvent(WhitePawn, fromX, fromY);\r
6392       fromX = fromY = -1;\r
6393       break;\r
6394 \r
6395     case DP_Knight:\r
6396       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6397       fromX = fromY = -1;\r
6398       break;\r
6399 \r
6400     case DP_Bishop:\r
6401       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6402       fromX = fromY = -1;\r
6403       break;\r
6404 \r
6405     case DP_Rook:\r
6406       DropMenuEvent(WhiteRook, fromX, fromY);\r
6407       fromX = fromY = -1;\r
6408       break;\r
6409 \r
6410     case DP_Queen:\r
6411       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6412       fromX = fromY = -1;\r
6413       break;\r
6414 \r
6415     default:\r
6416       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6417     }\r
6418     break;\r
6419 \r
6420   case WM_TIMER:\r
6421     switch (wParam) {\r
6422     case CLOCK_TIMER_ID:\r
6423       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6424       clockTimerEvent = 0;\r
6425       DecrementClocks(); /* call into back end */\r
6426       break;\r
6427     case LOAD_GAME_TIMER_ID:\r
6428       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6429       loadGameTimerEvent = 0;\r
6430       AutoPlayGameLoop(); /* call into back end */\r
6431       break;\r
6432     case ANALYSIS_TIMER_ID:\r
6433       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6434                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6435         AnalysisPeriodicEvent(0);\r
6436       } else {\r
6437         KillTimer(hwnd, analysisTimerEvent);\r
6438         analysisTimerEvent = 0;\r
6439       }\r
6440       break;\r
6441     case DELAYED_TIMER_ID:\r
6442       KillTimer(hwnd, delayedTimerEvent);\r
6443       delayedTimerEvent = 0;\r
6444       delayedTimerCallback();\r
6445       break;\r
6446     }\r
6447     break;\r
6448 \r
6449   case WM_USER_Input:\r
6450     InputEvent(hwnd, message, wParam, lParam);\r
6451     break;\r
6452 \r
6453   /* [AS] Also move "attached" child windows */\r
6454   case WM_WINDOWPOSCHANGING:\r
6455 \r
6456     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6457         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6458 \r
6459         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6460             /* Window is moving */\r
6461             RECT rcMain;\r
6462 \r
6463 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6464             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6465             rcMain.right  = boardX + winWidth;\r
6466             rcMain.top    = boardY;\r
6467             rcMain.bottom = boardY + winHeight;\r
6468             \r
6469             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6470             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6471             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6472             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6473             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6474             boardX = lpwp->x;\r
6475             boardY = lpwp->y;\r
6476         }\r
6477     }\r
6478     break;\r
6479 \r
6480   /* [AS] Snapping */\r
6481   case WM_ENTERSIZEMOVE:\r
6482     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6483     if (hwnd == hwndMain) {\r
6484       doingSizing = TRUE;\r
6485       lastSizing = 0;\r
6486     }\r
6487     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6488     break;\r
6489 \r
6490   case WM_SIZING:\r
6491     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6492     if (hwnd == hwndMain) {\r
6493       lastSizing = wParam;\r
6494     }\r
6495     break;\r
6496 \r
6497   case WM_MOVING:\r
6498     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6499       return OnMoving( &sd, hwnd, wParam, lParam );\r
6500 \r
6501   case WM_EXITSIZEMOVE:\r
6502     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6503     if (hwnd == hwndMain) {\r
6504       RECT client;\r
6505       doingSizing = FALSE;\r
6506       InvalidateRect(hwnd, &boardRect, FALSE);\r
6507       GetClientRect(hwnd, &client);\r
6508       ResizeBoard(client.right, client.bottom, lastSizing);\r
6509       lastSizing = 0;\r
6510       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6511     }\r
6512     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6513     break;\r
6514 \r
6515   case WM_DESTROY: /* message: window being destroyed */\r
6516     PostQuitMessage(0);\r
6517     break;\r
6518 \r
6519   case WM_CLOSE:\r
6520     if (hwnd == hwndMain) {\r
6521       ExitEvent(0);\r
6522     }\r
6523     break;\r
6524 \r
6525   default:      /* Passes it on if unprocessed */\r
6526     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6527   }\r
6528   return 0;\r
6529 }\r
6530 \r
6531 /*---------------------------------------------------------------------------*\\r
6532  *\r
6533  * Misc utility routines\r
6534  *\r
6535 \*---------------------------------------------------------------------------*/\r
6536 \r
6537 /*\r
6538  * Decent random number generator, at least not as bad as Windows\r
6539  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6540  */\r
6541 unsigned int randstate;\r
6542 \r
6543 int\r
6544 myrandom(void)\r
6545 {\r
6546   randstate = randstate * 1664525 + 1013904223;\r
6547   return (int) randstate & 0x7fffffff;\r
6548 }\r
6549 \r
6550 void\r
6551 mysrandom(unsigned int seed)\r
6552 {\r
6553   randstate = seed;\r
6554 }\r
6555 \r
6556 \r
6557 /* \r
6558  * returns TRUE if user selects a different color, FALSE otherwise \r
6559  */\r
6560 \r
6561 BOOL\r
6562 ChangeColor(HWND hwnd, COLORREF *which)\r
6563 {\r
6564   static BOOL firstTime = TRUE;\r
6565   static DWORD customColors[16];\r
6566   CHOOSECOLOR cc;\r
6567   COLORREF newcolor;\r
6568   int i;\r
6569   ColorClass ccl;\r
6570 \r
6571   if (firstTime) {\r
6572     /* Make initial colors in use available as custom colors */\r
6573     /* Should we put the compiled-in defaults here instead? */\r
6574     i = 0;\r
6575     customColors[i++] = lightSquareColor & 0xffffff;\r
6576     customColors[i++] = darkSquareColor & 0xffffff;\r
6577     customColors[i++] = whitePieceColor & 0xffffff;\r
6578     customColors[i++] = blackPieceColor & 0xffffff;\r
6579     customColors[i++] = highlightSquareColor & 0xffffff;\r
6580     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6581 \r
6582     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6583       customColors[i++] = textAttribs[ccl].color;\r
6584     }\r
6585     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6586     firstTime = FALSE;\r
6587   }\r
6588 \r
6589   cc.lStructSize = sizeof(cc);\r
6590   cc.hwndOwner = hwnd;\r
6591   cc.hInstance = NULL;\r
6592   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6593   cc.lpCustColors = (LPDWORD) customColors;\r
6594   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6595 \r
6596   if (!ChooseColor(&cc)) return FALSE;\r
6597 \r
6598   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6599   if (newcolor == *which) return FALSE;\r
6600   *which = newcolor;\r
6601   return TRUE;\r
6602 \r
6603   /*\r
6604   InitDrawingColors();\r
6605   InvalidateRect(hwnd, &boardRect, FALSE);\r
6606   */\r
6607 }\r
6608 \r
6609 BOOLEAN\r
6610 MyLoadSound(MySound *ms)\r
6611 {\r
6612   BOOL ok = FALSE;\r
6613   struct stat st;\r
6614   FILE *f;\r
6615 \r
6616   if (ms->data) free(ms->data);\r
6617   ms->data = NULL;\r
6618 \r
6619   switch (ms->name[0]) {\r
6620   case NULLCHAR:\r
6621     /* Silence */\r
6622     ok = TRUE;\r
6623     break;\r
6624   case '$':\r
6625     /* System sound from Control Panel.  Don't preload here. */\r
6626     ok = TRUE;\r
6627     break;\r
6628   case '!':\r
6629     if (ms->name[1] == NULLCHAR) {\r
6630       /* "!" alone = silence */\r
6631       ok = TRUE;\r
6632     } else {\r
6633       /* Builtin wave resource.  Error if not found. */\r
6634       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6635       if (h == NULL) break;\r
6636       ms->data = (void *)LoadResource(hInst, h);\r
6637       if (h == NULL) break;\r
6638       ok = TRUE;\r
6639     }\r
6640     break;\r
6641   default:\r
6642     /* .wav file.  Error if not found. */\r
6643     f = fopen(ms->name, "rb");\r
6644     if (f == NULL) break;\r
6645     if (fstat(fileno(f), &st) < 0) break;\r
6646     ms->data = malloc(st.st_size);\r
6647     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6648     fclose(f);\r
6649     ok = TRUE;\r
6650     break;\r
6651   }\r
6652   if (!ok) {\r
6653     char buf[MSG_SIZ];\r
6654     sprintf(buf, "Error loading sound %s", ms->name);\r
6655     DisplayError(buf, GetLastError());\r
6656   }\r
6657   return ok;\r
6658 }\r
6659 \r
6660 BOOLEAN\r
6661 MyPlaySound(MySound *ms)\r
6662 {\r
6663   BOOLEAN ok = FALSE;\r
6664         if(appData.debugMode) fprintf(debugFP, "make sound %s %x %d\n", ms->name, ms, ms->name[0]);\r
6665   switch (ms->name[0]) {\r
6666   case NULLCHAR:\r
6667         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6668     /* Silence */\r
6669     ok = TRUE;\r
6670     break;\r
6671   case '$':\r
6672     /* System sound from Control Panel (deprecated feature).\r
6673        "$" alone or an unset sound name gets default beep (still in use). */\r
6674     if (ms->name[1]) {\r
6675       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6676     }\r
6677     if (!ok) ok = MessageBeep(MB_OK);\r
6678     break; \r
6679   case '!':\r
6680     /* Builtin wave resource, or "!" alone for silence */\r
6681     if (ms->name[1]) {\r
6682       if (ms->data == NULL) return FALSE;\r
6683       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6684     } else {\r
6685       ok = TRUE;\r
6686     }\r
6687     break;\r
6688   default:\r
6689     /* .wav file.  Error if not found. */\r
6690     if (ms->data == NULL) return FALSE;\r
6691     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6692     break;\r
6693   }\r
6694   /* Don't print an error: this can happen innocently if the sound driver\r
6695      is busy; for instance, if another instance of WinBoard is playing\r
6696      a sound at about the same time. */\r
6697 #if 0\r
6698   if (!ok) {\r
6699     char buf[MSG_SIZ];\r
6700     sprintf(buf, "Error playing sound %s", ms->name);\r
6701     DisplayError(buf, GetLastError());\r
6702   }\r
6703 #endif\r
6704   return ok;\r
6705 }\r
6706 \r
6707 \r
6708 LRESULT CALLBACK\r
6709 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6710 {\r
6711   BOOL ok;\r
6712   OPENFILENAME *ofn;\r
6713   static UINT *number; /* gross that this is static */\r
6714 \r
6715   switch (message) {\r
6716   case WM_INITDIALOG: /* message: initialize dialog box */\r
6717     /* Center the dialog over the application window */\r
6718     ofn = (OPENFILENAME *) lParam;\r
6719     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6720       number = (UINT *) ofn->lCustData;\r
6721       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6722     } else {\r
6723       number = NULL;\r
6724     }\r
6725     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6726     return FALSE;  /* Allow for further processing */\r
6727 \r
6728   case WM_COMMAND:\r
6729     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6730       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6731     }\r
6732     return FALSE;  /* Allow for further processing */\r
6733   }\r
6734   return FALSE;\r
6735 }\r
6736 \r
6737 UINT APIENTRY\r
6738 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6739 {\r
6740   static UINT *number;\r
6741   OPENFILENAME *ofname;\r
6742   OFNOTIFY *ofnot;\r
6743   switch (uiMsg) {\r
6744   case WM_INITDIALOG:\r
6745     ofname = (OPENFILENAME *)lParam;\r
6746     number = (UINT *)(ofname->lCustData);\r
6747     break;\r
6748   case WM_NOTIFY:\r
6749     ofnot = (OFNOTIFY *)lParam;\r
6750     if (ofnot->hdr.code == CDN_FILEOK) {\r
6751       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6752     }\r
6753     break;\r
6754   }\r
6755   return 0;\r
6756 }\r
6757 \r
6758 \r
6759 FILE *\r
6760 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6761                char *nameFilt, char *dlgTitle, UINT *number,\r
6762                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6763 {\r
6764   OPENFILENAME openFileName;\r
6765   char buf1[MSG_SIZ];\r
6766   FILE *f;\r
6767 \r
6768   if (fileName == NULL) fileName = buf1;\r
6769   if (defName == NULL) {\r
6770     strcpy(fileName, "*.");\r
6771     strcat(fileName, defExt);\r
6772   } else {\r
6773     strcpy(fileName, defName);\r
6774   }\r
6775   if (fileTitle) strcpy(fileTitle, "");\r
6776   if (number) *number = 0;\r
6777 \r
6778   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6779   openFileName.hwndOwner         = hwnd;\r
6780   openFileName.hInstance         = (HANDLE) hInst;\r
6781   openFileName.lpstrFilter       = nameFilt;\r
6782   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6783   openFileName.nMaxCustFilter    = 0L;\r
6784   openFileName.nFilterIndex      = 1L;\r
6785   openFileName.lpstrFile         = fileName;\r
6786   openFileName.nMaxFile          = MSG_SIZ;\r
6787   openFileName.lpstrFileTitle    = fileTitle;\r
6788   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6789   openFileName.lpstrInitialDir   = NULL;\r
6790   openFileName.lpstrTitle        = dlgTitle;\r
6791   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6792     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6793     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6794     | (oldDialog ? 0 : OFN_EXPLORER);\r
6795   openFileName.nFileOffset       = 0;\r
6796   openFileName.nFileExtension    = 0;\r
6797   openFileName.lpstrDefExt       = defExt;\r
6798   openFileName.lCustData         = (LONG) number;\r
6799   openFileName.lpfnHook          = oldDialog ?\r
6800     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6801   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6802 \r
6803   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6804                         GetOpenFileName(&openFileName)) {\r
6805     /* open the file */\r
6806     f = fopen(openFileName.lpstrFile, write);\r
6807     if (f == NULL) {\r
6808       MessageBox(hwnd, "File open failed", NULL,\r
6809                  MB_OK|MB_ICONEXCLAMATION);\r
6810       return NULL;\r
6811     }\r
6812   } else {\r
6813     int err = CommDlgExtendedError();\r
6814     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6815     return FALSE;\r
6816   }\r
6817   return f;\r
6818 }\r
6819 \r
6820 \r
6821 \r
6822 VOID APIENTRY\r
6823 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6824 {\r
6825   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6826 \r
6827   /*\r
6828    * Get the first pop-up menu in the menu template. This is the\r
6829    * menu that TrackPopupMenu displays.\r
6830    */\r
6831   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6832 \r
6833   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6834 \r
6835   /*\r
6836    * TrackPopup uses screen coordinates, so convert the\r
6837    * coordinates of the mouse click to screen coordinates.\r
6838    */\r
6839   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6840 \r
6841   /* Draw and track the floating pop-up menu. */\r
6842   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6843                  pt.x, pt.y, 0, hwnd, NULL);\r
6844 \r
6845   /* Destroy the menu.*/\r
6846   DestroyMenu(hmenu);\r
6847 }\r
6848    \r
6849 typedef struct {\r
6850   HWND hDlg, hText;\r
6851   int sizeX, sizeY, newSizeX, newSizeY;\r
6852   HDWP hdwp;\r
6853 } ResizeEditPlusButtonsClosure;\r
6854 \r
6855 BOOL CALLBACK\r
6856 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6857 {\r
6858   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6859   RECT rect;\r
6860   POINT pt;\r
6861 \r
6862   if (hChild == cl->hText) return TRUE;\r
6863   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6864   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6865   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6866   ScreenToClient(cl->hDlg, &pt);\r
6867   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6868     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6869   return TRUE;\r
6870 }\r
6871 \r
6872 /* Resize a dialog that has a (rich) edit field filling most of\r
6873    the top, with a row of buttons below */\r
6874 VOID\r
6875 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6876 {\r
6877   RECT rectText;\r
6878   int newTextHeight, newTextWidth;\r
6879   ResizeEditPlusButtonsClosure cl;\r
6880   \r
6881   /*if (IsIconic(hDlg)) return;*/\r
6882   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6883   \r
6884   cl.hdwp = BeginDeferWindowPos(8);\r
6885 \r
6886   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6887   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6888   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6889   if (newTextHeight < 0) {\r
6890     newSizeY += -newTextHeight;\r
6891     newTextHeight = 0;\r
6892   }\r
6893   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6894     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6895 \r
6896   cl.hDlg = hDlg;\r
6897   cl.hText = hText;\r
6898   cl.sizeX = sizeX;\r
6899   cl.sizeY = sizeY;\r
6900   cl.newSizeX = newSizeX;\r
6901   cl.newSizeY = newSizeY;\r
6902   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6903 \r
6904   EndDeferWindowPos(cl.hdwp);\r
6905 }\r
6906 \r
6907 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6908 {\r
6909     RECT    rChild, rParent;\r
6910     int     wChild, hChild, wParent, hParent;\r
6911     int     wScreen, hScreen, xNew, yNew;\r
6912     HDC     hdc;\r
6913 \r
6914     /* Get the Height and Width of the child window */\r
6915     GetWindowRect (hwndChild, &rChild);\r
6916     wChild = rChild.right - rChild.left;\r
6917     hChild = rChild.bottom - rChild.top;\r
6918 \r
6919     /* Get the Height and Width of the parent window */\r
6920     GetWindowRect (hwndParent, &rParent);\r
6921     wParent = rParent.right - rParent.left;\r
6922     hParent = rParent.bottom - rParent.top;\r
6923 \r
6924     /* Get the display limits */\r
6925     hdc = GetDC (hwndChild);\r
6926     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6927     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6928     ReleaseDC(hwndChild, hdc);\r
6929 \r
6930     /* Calculate new X position, then adjust for screen */\r
6931     xNew = rParent.left + ((wParent - wChild) /2);\r
6932     if (xNew < 0) {\r
6933         xNew = 0;\r
6934     } else if ((xNew+wChild) > wScreen) {\r
6935         xNew = wScreen - wChild;\r
6936     }\r
6937 \r
6938     /* Calculate new Y position, then adjust for screen */\r
6939     if( mode == 0 ) {\r
6940         yNew = rParent.top  + ((hParent - hChild) /2);\r
6941     }\r
6942     else {\r
6943         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6944     }\r
6945 \r
6946     if (yNew < 0) {\r
6947         yNew = 0;\r
6948     } else if ((yNew+hChild) > hScreen) {\r
6949         yNew = hScreen - hChild;\r
6950     }\r
6951 \r
6952     /* Set it, and return */\r
6953     return SetWindowPos (hwndChild, NULL,\r
6954                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6955 }\r
6956 \r
6957 /* Center one window over another */\r
6958 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6959 {\r
6960     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6961 }\r
6962 \r
6963 /*---------------------------------------------------------------------------*\\r
6964  *\r
6965  * Startup Dialog functions\r
6966  *\r
6967 \*---------------------------------------------------------------------------*/\r
6968 void\r
6969 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6970 {\r
6971   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6972 \r
6973   while (*cd != NULL) {\r
6974     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6975     cd++;\r
6976   }\r
6977 }\r
6978 \r
6979 void\r
6980 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6981 {\r
6982   char buf1[ARG_MAX];\r
6983   int len;\r
6984 \r
6985   if (str[0] == '@') {\r
6986     FILE* f = fopen(str + 1, "r");\r
6987     if (f == NULL) {\r
6988       DisplayFatalError(str + 1, errno, 2);\r
6989       return;\r
6990     }\r
6991     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6992     fclose(f);\r
6993     buf1[len] = NULLCHAR;\r
6994     str = buf1;\r
6995   }\r
6996 \r
6997   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6998 \r
6999   for (;;) {\r
7000     char buf[MSG_SIZ];\r
7001     char *end = strchr(str, '\n');\r
7002     if (end == NULL) return;\r
7003     memcpy(buf, str, end - str);\r
7004     buf[end - str] = NULLCHAR;\r
7005     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
7006     str = end + 1;\r
7007   }\r
7008 }\r
7009 \r
7010 void\r
7011 SetStartupDialogEnables(HWND hDlg)\r
7012 {\r
7013   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
7014     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7015     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
7016   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7017     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
7018   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
7019     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
7020   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
7021     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
7022   EnableWindow(GetDlgItem(hDlg, IDOK),\r
7023     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7024     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
7025     IsDlgButtonChecked(hDlg, OPT_View));\r
7026 }\r
7027 \r
7028 char *\r
7029 QuoteForFilename(char *filename)\r
7030 {\r
7031   int dquote, space;\r
7032   dquote = strchr(filename, '"') != NULL;\r
7033   space = strchr(filename, ' ') != NULL;\r
7034   if (dquote || space) {\r
7035     if (dquote) {\r
7036       return "'";\r
7037     } else {\r
7038       return "\"";\r
7039     }\r
7040   } else {\r
7041     return "";\r
7042   }\r
7043 }\r
7044 \r
7045 VOID\r
7046 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
7047 {\r
7048   char buf[MSG_SIZ];\r
7049   char *q;\r
7050 \r
7051   InitComboStringsFromOption(hwndCombo, nthnames);\r
7052   q = QuoteForFilename(nthcp);\r
7053   sprintf(buf, "%s%s%s", q, nthcp, q);\r
7054   if (*nthdir != NULLCHAR) {\r
7055     q = QuoteForFilename(nthdir);\r
7056     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
7057   }\r
7058   if (*nthcp == NULLCHAR) {\r
7059     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7060   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7061     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7062     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7063   }\r
7064 }\r
7065 \r
7066 LRESULT CALLBACK\r
7067 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7068 {\r
7069   char buf[MSG_SIZ];\r
7070   HANDLE hwndCombo;\r
7071   char *p;\r
7072 \r
7073   switch (message) {\r
7074   case WM_INITDIALOG:\r
7075     /* Center the dialog */\r
7076     CenterWindow (hDlg, GetDesktopWindow());\r
7077     /* Initialize the dialog items */\r
7078     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7079                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7080                   firstChessProgramNames);\r
7081     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7082                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7083                   secondChessProgramNames);\r
7084     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7085     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7086     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7087     if (*appData.icsHelper != NULLCHAR) {\r
7088       char *q = QuoteForFilename(appData.icsHelper);\r
7089       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7090     }\r
7091     if (*appData.icsHost == NULLCHAR) {\r
7092       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7093       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7094     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7095       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7096       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7097     }\r
7098 \r
7099     if (appData.icsActive) {\r
7100       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7101     }\r
7102     else if (appData.noChessProgram) {\r
7103       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7104     }\r
7105     else {\r
7106       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7107     }\r
7108 \r
7109     SetStartupDialogEnables(hDlg);\r
7110     return TRUE;\r
7111 \r
7112   case WM_COMMAND:\r
7113     switch (LOWORD(wParam)) {\r
7114     case IDOK:\r
7115       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7116         strcpy(buf, "/fcp=");\r
7117         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7118         p = buf;\r
7119         ParseArgs(StringGet, &p);\r
7120         strcpy(buf, "/scp=");\r
7121         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7122         p = buf;\r
7123         ParseArgs(StringGet, &p);\r
7124         appData.noChessProgram = FALSE;\r
7125         appData.icsActive = FALSE;\r
7126       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7127         strcpy(buf, "/ics /icshost=");\r
7128         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7129         p = buf;\r
7130         ParseArgs(StringGet, &p);\r
7131         if (appData.zippyPlay) {\r
7132           strcpy(buf, "/fcp=");\r
7133           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7134           p = buf;\r
7135           ParseArgs(StringGet, &p);\r
7136         }\r
7137       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7138         appData.noChessProgram = TRUE;\r
7139         appData.icsActive = FALSE;\r
7140       } else {\r
7141         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7142                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7143         return TRUE;\r
7144       }\r
7145       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7146         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7147         p = buf;\r
7148         ParseArgs(StringGet, &p);\r
7149       }\r
7150       EndDialog(hDlg, TRUE);\r
7151       return TRUE;\r
7152 \r
7153     case IDCANCEL:\r
7154       ExitEvent(0);\r
7155       return TRUE;\r
7156 \r
7157     case IDM_HELPCONTENTS:\r
7158       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7159         MessageBox (GetFocus(),\r
7160                     "Unable to activate help",\r
7161                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7162       }\r
7163       break;\r
7164 \r
7165     default:\r
7166       SetStartupDialogEnables(hDlg);\r
7167       break;\r
7168     }\r
7169     break;\r
7170   }\r
7171   return FALSE;\r
7172 }\r
7173 \r
7174 /*---------------------------------------------------------------------------*\\r
7175  *\r
7176  * About box dialog functions\r
7177  *\r
7178 \*---------------------------------------------------------------------------*/\r
7179 \r
7180 /* Process messages for "About" dialog box */\r
7181 LRESULT CALLBACK\r
7182 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7183 {\r
7184   switch (message) {\r
7185   case WM_INITDIALOG: /* message: initialize dialog box */\r
7186     /* Center the dialog over the application window */\r
7187     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7188     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7189     return (TRUE);\r
7190 \r
7191   case WM_COMMAND: /* message: received a command */\r
7192     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7193         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7194       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7195       return (TRUE);\r
7196     }\r
7197     break;\r
7198   }\r
7199   return (FALSE);\r
7200 }\r
7201 \r
7202 /*---------------------------------------------------------------------------*\\r
7203  *\r
7204  * Comment Dialog functions\r
7205  *\r
7206 \*---------------------------------------------------------------------------*/\r
7207 \r
7208 LRESULT CALLBACK\r
7209 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7210 {\r
7211   static HANDLE hwndText = NULL;\r
7212   int len, newSizeX, newSizeY, flags;\r
7213   static int sizeX, sizeY;\r
7214   char *str;\r
7215   RECT rect;\r
7216   MINMAXINFO *mmi;\r
7217 \r
7218   switch (message) {\r
7219   case WM_INITDIALOG: /* message: initialize dialog box */\r
7220     /* Initialize the dialog items */\r
7221     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7222     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7223     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7224     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7225     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7226     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7227     SetWindowText(hDlg, commentTitle);\r
7228     if (editComment) {\r
7229       SetFocus(hwndText);\r
7230     } else {\r
7231       SetFocus(GetDlgItem(hDlg, IDOK));\r
7232     }\r
7233     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7234                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7235                 MAKELPARAM(FALSE, 0));\r
7236     /* Size and position the dialog */\r
7237     if (!commentDialog) {\r
7238       commentDialog = hDlg;\r
7239       flags = SWP_NOZORDER;\r
7240       GetClientRect(hDlg, &rect);\r
7241       sizeX = rect.right;\r
7242       sizeY = rect.bottom;\r
7243       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7244           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7245         WINDOWPLACEMENT wp;\r
7246         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7247         wp.length = sizeof(WINDOWPLACEMENT);\r
7248         wp.flags = 0;\r
7249         wp.showCmd = SW_SHOW;\r
7250         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7251         wp.rcNormalPosition.left = commentX;\r
7252         wp.rcNormalPosition.right = commentX + commentW;\r
7253         wp.rcNormalPosition.top = commentY;\r
7254         wp.rcNormalPosition.bottom = commentY + commentH;\r
7255         SetWindowPlacement(hDlg, &wp);\r
7256 \r
7257         GetClientRect(hDlg, &rect);\r
7258         newSizeX = rect.right;\r
7259         newSizeY = rect.bottom;\r
7260         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7261                               newSizeX, newSizeY);\r
7262         sizeX = newSizeX;\r
7263         sizeY = newSizeY;\r
7264       }\r
7265     }\r
7266     return FALSE;\r
7267 \r
7268   case WM_COMMAND: /* message: received a command */\r
7269     switch (LOWORD(wParam)) {\r
7270     case IDOK:\r
7271       if (editComment) {\r
7272         char *p, *q;\r
7273         /* Read changed options from the dialog box */\r
7274         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7275         len = GetWindowTextLength(hwndText);\r
7276         str = (char *) malloc(len + 1);\r
7277         GetWindowText(hwndText, str, len + 1);\r
7278         p = q = str;\r
7279         while (*q) {\r
7280           if (*q == '\r')\r
7281             q++;\r
7282           else\r
7283             *p++ = *q++;\r
7284         }\r
7285         *p = NULLCHAR;\r
7286         ReplaceComment(commentIndex, str);\r
7287         free(str);\r
7288       }\r
7289       CommentPopDown();\r
7290       return TRUE;\r
7291 \r
7292     case IDCANCEL:\r
7293     case OPT_CancelComment:\r
7294       CommentPopDown();\r
7295       return TRUE;\r
7296 \r
7297     case OPT_ClearComment:\r
7298       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7299       break;\r
7300 \r
7301     case OPT_EditComment:\r
7302       EditCommentEvent();\r
7303       return TRUE;\r
7304 \r
7305     default:\r
7306       break;\r
7307     }\r
7308     break;\r
7309 \r
7310   case WM_SIZE:\r
7311     newSizeX = LOWORD(lParam);\r
7312     newSizeY = HIWORD(lParam);\r
7313     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7314     sizeX = newSizeX;\r
7315     sizeY = newSizeY;\r
7316     break;\r
7317 \r
7318   case WM_GETMINMAXINFO:\r
7319     /* Prevent resizing window too small */\r
7320     mmi = (MINMAXINFO *) lParam;\r
7321     mmi->ptMinTrackSize.x = 100;\r
7322     mmi->ptMinTrackSize.y = 100;\r
7323     break;\r
7324   }\r
7325   return FALSE;\r
7326 }\r
7327 \r
7328 VOID\r
7329 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7330 {\r
7331   FARPROC lpProc;\r
7332   char *p, *q;\r
7333 \r
7334   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7335 \r
7336   if (str == NULL) str = "";\r
7337   p = (char *) malloc(2 * strlen(str) + 2);\r
7338   q = p;\r
7339   while (*str) {\r
7340     if (*str == '\n') *q++ = '\r';\r
7341     *q++ = *str++;\r
7342   }\r
7343   *q = NULLCHAR;\r
7344   if (commentText != NULL) free(commentText);\r
7345 \r
7346   commentIndex = index;\r
7347   commentTitle = title;\r
7348   commentText = p;\r
7349   editComment = edit;\r
7350 \r
7351   if (commentDialog) {\r
7352     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7353     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7354   } else {\r
7355     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7356     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7357                  hwndMain, (DLGPROC)lpProc);\r
7358     FreeProcInstance(lpProc);\r
7359   }\r
7360   commentDialogUp = TRUE;\r
7361 }\r
7362 \r
7363 \r
7364 /*---------------------------------------------------------------------------*\\r
7365  *\r
7366  * Type-in move dialog functions\r
7367  * \r
7368 \*---------------------------------------------------------------------------*/\r
7369 \r
7370 LRESULT CALLBACK\r
7371 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7372 {\r
7373   char move[MSG_SIZ];\r
7374   HWND hInput;\r
7375   ChessMove moveType;\r
7376   int fromX, fromY, toX, toY;\r
7377   char promoChar;\r
7378 \r
7379   switch (message) {\r
7380   case WM_INITDIALOG:\r
7381     move[0] = (char) lParam;\r
7382     move[1] = NULLCHAR;\r
7383     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7384     hInput = GetDlgItem(hDlg, OPT_Move);\r
7385     SetWindowText(hInput, move);\r
7386     SetFocus(hInput);\r
7387     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7388     return FALSE;\r
7389 \r
7390   case WM_COMMAND:\r
7391     switch (LOWORD(wParam)) {\r
7392     case IDOK:\r
7393       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7394       { int n; Board board;\r
7395         // [HGM] FENedit\r
7396         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7397                 EditPositionPasteFEN(move);\r
7398                 EndDialog(hDlg, TRUE);\r
7399                 return TRUE;\r
7400         }\r
7401         // [HGM] movenum: allow move number to be typed in any mode\r
7402         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7403           currentMove = 2*n-1;\r
7404           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7405           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7406           EndDialog(hDlg, TRUE);\r
7407           DrawPosition(TRUE, boards[currentMove]);\r
7408           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7409           else DisplayMessage("", "");\r
7410           return TRUE;\r
7411         }\r
7412       }\r
7413       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7414         gameMode != Training) {\r
7415         DisplayMoveError("Displayed move is not current");\r
7416       } else {\r
7417 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7418         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7419           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7420         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7421         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7422           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7423           if (gameMode != Training)\r
7424               forwardMostMove = currentMove;\r
7425           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7426         } else {\r
7427           DisplayMoveError("Could not parse move");\r
7428         }\r
7429       }\r
7430       EndDialog(hDlg, TRUE);\r
7431       return TRUE;\r
7432     case IDCANCEL:\r
7433       EndDialog(hDlg, FALSE);\r
7434       return TRUE;\r
7435     default:\r
7436       break;\r
7437     }\r
7438     break;\r
7439   }\r
7440   return FALSE;\r
7441 }\r
7442 \r
7443 VOID\r
7444 PopUpMoveDialog(char firstchar)\r
7445 {\r
7446     FARPROC lpProc;\r
7447     \r
7448     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7449         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7450         gameMode == AnalyzeMode || gameMode == EditGame || \r
7451         gameMode == EditPosition || gameMode == IcsExamining ||\r
7452         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7453         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7454                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7455                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7456         gameMode == Training) {\r
7457       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7458       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7459         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7460       FreeProcInstance(lpProc);\r
7461     }\r
7462 }\r
7463 \r
7464 /*---------------------------------------------------------------------------*\\r
7465  *\r
7466  * Type-in name dialog functions\r
7467  * \r
7468 \*---------------------------------------------------------------------------*/\r
7469 \r
7470 LRESULT CALLBACK\r
7471 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7472 {\r
7473   char move[MSG_SIZ];\r
7474   HWND hInput;\r
7475 \r
7476   switch (message) {\r
7477   case WM_INITDIALOG:\r
7478     move[0] = (char) lParam;\r
7479     move[1] = NULLCHAR;\r
7480     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7481     hInput = GetDlgItem(hDlg, OPT_Name);\r
7482     SetWindowText(hInput, move);\r
7483     SetFocus(hInput);\r
7484     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7485     return FALSE;\r
7486 \r
7487   case WM_COMMAND:\r
7488     switch (LOWORD(wParam)) {\r
7489     case IDOK:\r
7490       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7491       appData.userName = strdup(move);\r
7492       SetUserLogo();\r
7493 \r
7494       EndDialog(hDlg, TRUE);\r
7495       return TRUE;\r
7496     case IDCANCEL:\r
7497       EndDialog(hDlg, FALSE);\r
7498       return TRUE;\r
7499     default:\r
7500       break;\r
7501     }\r
7502     break;\r
7503   }\r
7504   return FALSE;\r
7505 }\r
7506 \r
7507 VOID\r
7508 PopUpNameDialog(char firstchar)\r
7509 {\r
7510     FARPROC lpProc;\r
7511     \r
7512       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7513       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7514         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7515       FreeProcInstance(lpProc);\r
7516 }\r
7517 \r
7518 /*---------------------------------------------------------------------------*\\r
7519  *\r
7520  *  Error dialogs\r
7521  * \r
7522 \*---------------------------------------------------------------------------*/\r
7523 \r
7524 /* Nonmodal error box */\r
7525 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7526                              WPARAM wParam, LPARAM lParam);\r
7527 \r
7528 VOID\r
7529 ErrorPopUp(char *title, char *content)\r
7530 {\r
7531   FARPROC lpProc;\r
7532   char *p, *q;\r
7533   BOOLEAN modal = hwndMain == NULL;\r
7534 \r
7535   p = content;\r
7536   q = errorMessage;\r
7537   while (*p) {\r
7538     if (*p == '\n') {\r
7539       if (modal) {\r
7540         *q++ = ' ';\r
7541         p++;\r
7542       } else {\r
7543         *q++ = '\r';\r
7544         *q++ = *p++;\r
7545       }\r
7546     } else {\r
7547       *q++ = *p++;\r
7548     }\r
7549   }\r
7550   *q = NULLCHAR;\r
7551   strncpy(errorTitle, title, sizeof(errorTitle));\r
7552   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7553   \r
7554   if (modal) {\r
7555     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7556   } else {\r
7557     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7558     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7559                  hwndMain, (DLGPROC)lpProc);\r
7560     FreeProcInstance(lpProc);\r
7561   }\r
7562 }\r
7563 \r
7564 VOID\r
7565 ErrorPopDown()\r
7566 {\r
7567   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7568   if (errorDialog == NULL) return;\r
7569   DestroyWindow(errorDialog);\r
7570   errorDialog = NULL;\r
7571 }\r
7572 \r
7573 LRESULT CALLBACK\r
7574 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7575 {\r
7576   HANDLE hwndText;\r
7577   RECT rChild;\r
7578 \r
7579   switch (message) {\r
7580   case WM_INITDIALOG:\r
7581     GetWindowRect(hDlg, &rChild);\r
7582 \r
7583     /*\r
7584     SetWindowPos(hDlg, NULL, rChild.left,\r
7585       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7586       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7587     */\r
7588 \r
7589     /* \r
7590         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7591         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7592         and it doesn't work when you resize the dialog.\r
7593         For now, just give it a default position.\r
7594     */\r
7595     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7596 \r
7597     errorDialog = hDlg;\r
7598     SetWindowText(hDlg, errorTitle);\r
7599     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7600     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7601     return FALSE;\r
7602 \r
7603   case WM_COMMAND:\r
7604     switch (LOWORD(wParam)) {\r
7605     case IDOK:\r
7606     case IDCANCEL:\r
7607       if (errorDialog == hDlg) errorDialog = NULL;\r
7608       DestroyWindow(hDlg);\r
7609       return TRUE;\r
7610 \r
7611     default:\r
7612       break;\r
7613     }\r
7614     break;\r
7615   }\r
7616   return FALSE;\r
7617 }\r
7618 \r
7619 #ifdef GOTHIC\r
7620 HWND gothicDialog = NULL;\r
7621 \r
7622 LRESULT CALLBACK\r
7623 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7624 {\r
7625   HANDLE hwndText;\r
7626   RECT rChild;\r
7627   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7628 \r
7629   switch (message) {\r
7630   case WM_INITDIALOG:\r
7631     GetWindowRect(hDlg, &rChild);\r
7632 \r
7633     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7634                                                              SWP_NOZORDER);\r
7635 \r
7636     /* \r
7637         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7638         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7639         and it doesn't work when you resize the dialog.\r
7640         For now, just give it a default position.\r
7641     */\r
7642     gothicDialog = hDlg;\r
7643     SetWindowText(hDlg, errorTitle);\r
7644     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7645     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7646     return FALSE;\r
7647 \r
7648   case WM_COMMAND:\r
7649     switch (LOWORD(wParam)) {\r
7650     case IDOK:\r
7651     case IDCANCEL:\r
7652       if (errorDialog == hDlg) errorDialog = NULL;\r
7653       DestroyWindow(hDlg);\r
7654       return TRUE;\r
7655 \r
7656     default:\r
7657       break;\r
7658     }\r
7659     break;\r
7660   }\r
7661   return FALSE;\r
7662 }\r
7663 \r
7664 VOID\r
7665 GothicPopUp(char *title, VariantClass variant)\r
7666 {\r
7667   FARPROC lpProc;\r
7668   static char *lastTitle;\r
7669 \r
7670   strncpy(errorTitle, title, sizeof(errorTitle));\r
7671   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7672 \r
7673   if(lastTitle != title && gothicDialog != NULL) {\r
7674     DestroyWindow(gothicDialog);\r
7675     gothicDialog = NULL;\r
7676   }\r
7677   if(variant != VariantNormal && gothicDialog == NULL) {\r
7678     title = lastTitle;\r
7679     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7680     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7681                  hwndMain, (DLGPROC)lpProc);\r
7682     FreeProcInstance(lpProc);\r
7683   }\r
7684 }\r
7685 #endif\r
7686 \r
7687 /*---------------------------------------------------------------------------*\\r
7688  *\r
7689  *  Ics Interaction console functions\r
7690  *\r
7691 \*---------------------------------------------------------------------------*/\r
7692 \r
7693 #define HISTORY_SIZE 64\r
7694 static char *history[HISTORY_SIZE];\r
7695 int histIn = 0, histP = 0;\r
7696 \r
7697 VOID\r
7698 SaveInHistory(char *cmd)\r
7699 {\r
7700   if (history[histIn] != NULL) {\r
7701     free(history[histIn]);\r
7702     history[histIn] = NULL;\r
7703   }\r
7704   if (*cmd == NULLCHAR) return;\r
7705   history[histIn] = StrSave(cmd);\r
7706   histIn = (histIn + 1) % HISTORY_SIZE;\r
7707   if (history[histIn] != NULL) {\r
7708     free(history[histIn]);\r
7709     history[histIn] = NULL;\r
7710   }\r
7711   histP = histIn;\r
7712 }\r
7713 \r
7714 char *\r
7715 PrevInHistory(char *cmd)\r
7716 {\r
7717   int newhp;\r
7718   if (histP == histIn) {\r
7719     if (history[histIn] != NULL) free(history[histIn]);\r
7720     history[histIn] = StrSave(cmd);\r
7721   }\r
7722   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7723   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7724   histP = newhp;\r
7725   return history[histP];\r
7726 }\r
7727 \r
7728 char *\r
7729 NextInHistory()\r
7730 {\r
7731   if (histP == histIn) return NULL;\r
7732   histP = (histP + 1) % HISTORY_SIZE;\r
7733   return history[histP];\r
7734 }\r
7735 \r
7736 typedef struct {\r
7737   char *item;\r
7738   char *command;\r
7739   BOOLEAN getname;\r
7740   BOOLEAN immediate;\r
7741 } IcsTextMenuEntry;\r
7742 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7743 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7744 \r
7745 void\r
7746 ParseIcsTextMenu(char *icsTextMenuString)\r
7747 {\r
7748 //  int flags = 0;\r
7749   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7750   char *p = icsTextMenuString;\r
7751   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7752     free(e->item);\r
7753     e->item = NULL;\r
7754     if (e->command != NULL) {\r
7755       free(e->command);\r
7756       e->command = NULL;\r
7757     }\r
7758     e++;\r
7759   }\r
7760   e = icsTextMenuEntry;\r
7761   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7762     if (*p == ';' || *p == '\n') {\r
7763       e->item = strdup("-");\r
7764       e->command = NULL;\r
7765       p++;\r
7766     } else if (*p == '-') {\r
7767       e->item = strdup("-");\r
7768       e->command = NULL;\r
7769       p++;\r
7770       if (*p) p++;\r
7771     } else {\r
7772       char *q, *r, *s, *t;\r
7773       char c;\r
7774       q = strchr(p, ',');\r
7775       if (q == NULL) break;\r
7776       *q = NULLCHAR;\r
7777       r = strchr(q + 1, ',');\r
7778       if (r == NULL) break;\r
7779       *r = NULLCHAR;\r
7780       s = strchr(r + 1, ',');\r
7781       if (s == NULL) break;\r
7782       *s = NULLCHAR;\r
7783       c = ';';\r
7784       t = strchr(s + 1, c);\r
7785       if (t == NULL) {\r
7786         c = '\n';\r
7787         t = strchr(s + 1, c);\r
7788       }\r
7789       if (t != NULL) *t = NULLCHAR;\r
7790       e->item = strdup(p);\r
7791       e->command = strdup(q + 1);\r
7792       e->getname = *(r + 1) != '0';\r
7793       e->immediate = *(s + 1) != '0';\r
7794       *q = ',';\r
7795       *r = ',';\r
7796       *s = ',';\r
7797       if (t == NULL) break;\r
7798       *t = c;\r
7799       p = t + 1;\r
7800     }\r
7801     e++;\r
7802   } \r
7803 }\r
7804 \r
7805 HMENU\r
7806 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7807 {\r
7808   HMENU hmenu, h;\r
7809   int i = 0;\r
7810   hmenu = LoadMenu(hInst, "TextMenu");\r
7811   h = GetSubMenu(hmenu, 0);\r
7812   while (e->item) {\r
7813     if (strcmp(e->item, "-") == 0) {\r
7814       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7815     } else {\r
7816       if (e->item[0] == '|') {\r
7817         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7818                    IDM_CommandX + i, &e->item[1]);\r
7819       } else {\r
7820         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7821       }\r
7822     }\r
7823     e++;\r
7824     i++;\r
7825   } \r
7826   return hmenu;\r
7827 }\r
7828 \r
7829 WNDPROC consoleTextWindowProc;\r
7830 \r
7831 void\r
7832 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7833 {\r
7834   char buf[MSG_SIZ], name[MSG_SIZ];\r
7835   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7836   CHARRANGE sel;\r
7837 \r
7838   if (!getname) {\r
7839     SetWindowText(hInput, command);\r
7840     if (immediate) {\r
7841       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7842     } else {\r
7843       sel.cpMin = 999999;\r
7844       sel.cpMax = 999999;\r
7845       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7846       SetFocus(hInput);\r
7847     }\r
7848     return;\r
7849   }    \r
7850   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7851   if (sel.cpMin == sel.cpMax) {\r
7852     /* Expand to surrounding word */\r
7853     TEXTRANGE tr;\r
7854     do {\r
7855       tr.chrg.cpMax = sel.cpMin;\r
7856       tr.chrg.cpMin = --sel.cpMin;\r
7857       if (sel.cpMin < 0) break;\r
7858       tr.lpstrText = name;\r
7859       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7860     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7861     sel.cpMin++;\r
7862 \r
7863     do {\r
7864       tr.chrg.cpMin = sel.cpMax;\r
7865       tr.chrg.cpMax = ++sel.cpMax;\r
7866       tr.lpstrText = name;\r
7867       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7868     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7869     sel.cpMax--;\r
7870 \r
7871     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7872       MessageBeep(MB_ICONEXCLAMATION);\r
7873       return;\r
7874     }\r
7875     tr.chrg = sel;\r
7876     tr.lpstrText = name;\r
7877     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7878   } else {\r
7879     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7880       MessageBeep(MB_ICONEXCLAMATION);\r
7881       return;\r
7882     }\r
7883     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7884   }\r
7885   if (immediate) {\r
7886     sprintf(buf, "%s %s", command, name);\r
7887     SetWindowText(hInput, buf);\r
7888     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7889   } else {\r
7890     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7891     SetWindowText(hInput, buf);\r
7892     sel.cpMin = 999999;\r
7893     sel.cpMax = 999999;\r
7894     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7895     SetFocus(hInput);\r
7896   }\r
7897 }\r
7898 \r
7899 LRESULT CALLBACK \r
7900 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7901 {\r
7902   HWND hInput;\r
7903   CHARRANGE sel;\r
7904 \r
7905   switch (message) {\r
7906   case WM_KEYDOWN:\r
7907     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7908     switch (wParam) {\r
7909     case VK_PRIOR:\r
7910       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7911       return 0;\r
7912     case VK_NEXT:\r
7913       sel.cpMin = 999999;\r
7914       sel.cpMax = 999999;\r
7915       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7916       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7917       return 0;\r
7918     }\r
7919     break;\r
7920   case WM_CHAR:\r
7921    if(wParam != '\022') {\r
7922     if (wParam == '\t') {\r
7923       if (GetKeyState(VK_SHIFT) < 0) {\r
7924         /* shifted */\r
7925         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7926         if (buttonDesc[0].hwnd) {\r
7927           SetFocus(buttonDesc[0].hwnd);\r
7928         } else {\r
7929           SetFocus(hwndMain);\r
7930         }\r
7931       } else {\r
7932         /* unshifted */\r
7933         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7934       }\r
7935     } else {\r
7936       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7937       JAWS_DELETE( SetFocus(hInput); )\r
7938       SendMessage(hInput, message, wParam, lParam);\r
7939     }\r
7940     return 0;\r
7941    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7942   case WM_RBUTTONUP:\r
7943     if (GetKeyState(VK_SHIFT) & ~1) {\r
7944       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7945         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7946     } else {\r
7947       POINT pt;\r
7948       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7949       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7950       if (sel.cpMin == sel.cpMax) {\r
7951         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7952         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7953       }\r
7954       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7955         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7956       }\r
7957       pt.x = LOWORD(lParam);\r
7958       pt.y = HIWORD(lParam);\r
7959       MenuPopup(hwnd, pt, hmenu, -1);\r
7960     }\r
7961     return 0;\r
7962   case WM_PASTE:\r
7963     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7964     SetFocus(hInput);\r
7965     return SendMessage(hInput, message, wParam, lParam);\r
7966   case WM_MBUTTONDOWN:\r
7967     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7968   case WM_RBUTTONDOWN:\r
7969     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7970       /* Move selection here if it was empty */\r
7971       POINT pt;\r
7972       pt.x = LOWORD(lParam);\r
7973       pt.y = HIWORD(lParam);\r
7974       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7975       if (sel.cpMin == sel.cpMax) {\r
7976         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7977         sel.cpMax = sel.cpMin;\r
7978         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7979       }\r
7980       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7981     }\r
7982     return 0;\r
7983   case WM_COMMAND:\r
7984     switch (LOWORD(wParam)) {\r
7985     case IDM_QuickPaste:\r
7986       {\r
7987         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7988         if (sel.cpMin == sel.cpMax) {\r
7989           MessageBeep(MB_ICONEXCLAMATION);\r
7990           return 0;\r
7991         }\r
7992         SendMessage(hwnd, WM_COPY, 0, 0);\r
7993         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7994         SendMessage(hInput, WM_PASTE, 0, 0);\r
7995         SetFocus(hInput);\r
7996         return 0;\r
7997       }\r
7998     case IDM_Cut:\r
7999       SendMessage(hwnd, WM_CUT, 0, 0);\r
8000       return 0;\r
8001     case IDM_Paste:\r
8002       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8003       return 0;\r
8004     case IDM_Copy:\r
8005       SendMessage(hwnd, WM_COPY, 0, 0);\r
8006       return 0;\r
8007     default:\r
8008       {\r
8009         int i = LOWORD(wParam) - IDM_CommandX;\r
8010         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
8011             icsTextMenuEntry[i].command != NULL) {\r
8012           CommandX(hwnd, icsTextMenuEntry[i].command,\r
8013                    icsTextMenuEntry[i].getname,\r
8014                    icsTextMenuEntry[i].immediate);\r
8015           return 0;\r
8016         }\r
8017       }\r
8018       break;\r
8019     }\r
8020     break;\r
8021   }\r
8022   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
8023 }\r
8024 \r
8025 WNDPROC consoleInputWindowProc;\r
8026 \r
8027 LRESULT CALLBACK\r
8028 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8029 {\r
8030   char buf[MSG_SIZ];\r
8031   char *p;\r
8032   static BOOL sendNextChar = FALSE;\r
8033   static BOOL quoteNextChar = FALSE;\r
8034   InputSource *is = consoleInputSource;\r
8035   CHARFORMAT cf;\r
8036   CHARRANGE sel;\r
8037 \r
8038   switch (message) {\r
8039   case WM_CHAR:\r
8040     if (!appData.localLineEditing || sendNextChar) {\r
8041       is->buf[0] = (CHAR) wParam;\r
8042       is->count = 1;\r
8043       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8044       sendNextChar = FALSE;\r
8045       return 0;\r
8046     }\r
8047     if (quoteNextChar) {\r
8048       buf[0] = (char) wParam;\r
8049       buf[1] = NULLCHAR;\r
8050       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
8051       quoteNextChar = FALSE;\r
8052       return 0;\r
8053     }\r
8054     switch (wParam) {\r
8055     case '\r':   /* Enter key */\r
8056       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
8057       if (consoleEcho) SaveInHistory(is->buf);\r
8058       is->buf[is->count++] = '\n';\r
8059       is->buf[is->count] = NULLCHAR;\r
8060       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8061       if (consoleEcho) {\r
8062         ConsoleOutput(is->buf, is->count, TRUE);\r
8063       } else if (appData.localLineEditing) {\r
8064         ConsoleOutput("\n", 1, TRUE);\r
8065       }\r
8066       /* fall thru */\r
8067     case '\033': /* Escape key */\r
8068       SetWindowText(hwnd, "");\r
8069       cf.cbSize = sizeof(CHARFORMAT);\r
8070       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8071       if (consoleEcho) {\r
8072         cf.crTextColor = textAttribs[ColorNormal].color;\r
8073       } else {\r
8074         cf.crTextColor = COLOR_ECHOOFF;\r
8075       }\r
8076       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8077       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8078       return 0;\r
8079     case '\t':   /* Tab key */\r
8080       if (GetKeyState(VK_SHIFT) < 0) {\r
8081         /* shifted */\r
8082         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8083       } else {\r
8084         /* unshifted */\r
8085         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8086         if (buttonDesc[0].hwnd) {\r
8087           SetFocus(buttonDesc[0].hwnd);\r
8088         } else {\r
8089           SetFocus(hwndMain);\r
8090         }\r
8091       }\r
8092       return 0;\r
8093     case '\023': /* Ctrl+S */\r
8094       sendNextChar = TRUE;\r
8095       return 0;\r
8096     case '\021': /* Ctrl+Q */\r
8097       quoteNextChar = TRUE;\r
8098       return 0;\r
8099     JAWS_REPLAY\r
8100     default:\r
8101       break;\r
8102     }\r
8103     break;\r
8104   case WM_KEYDOWN:\r
8105     switch (wParam) {\r
8106     case VK_UP:\r
8107       GetWindowText(hwnd, buf, MSG_SIZ);\r
8108       p = PrevInHistory(buf);\r
8109       if (p != NULL) {\r
8110         SetWindowText(hwnd, p);\r
8111         sel.cpMin = 999999;\r
8112         sel.cpMax = 999999;\r
8113         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8114         return 0;\r
8115       }\r
8116       break;\r
8117     case VK_DOWN:\r
8118       p = NextInHistory();\r
8119       if (p != NULL) {\r
8120         SetWindowText(hwnd, p);\r
8121         sel.cpMin = 999999;\r
8122         sel.cpMax = 999999;\r
8123         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8124         return 0;\r
8125       }\r
8126       break;\r
8127     case VK_HOME:\r
8128     case VK_END:\r
8129       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8130       /* fall thru */\r
8131     case VK_PRIOR:\r
8132     case VK_NEXT:\r
8133       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8134       return 0;\r
8135     }\r
8136     break;\r
8137   case WM_MBUTTONDOWN:\r
8138     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8139       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8140     break;\r
8141   case WM_RBUTTONUP:\r
8142     if (GetKeyState(VK_SHIFT) & ~1) {\r
8143       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8144         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8145     } else {\r
8146       POINT pt;\r
8147       HMENU hmenu;\r
8148       hmenu = LoadMenu(hInst, "InputMenu");\r
8149       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8150       if (sel.cpMin == sel.cpMax) {\r
8151         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8152         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8153       }\r
8154       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8155         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8156       }\r
8157       pt.x = LOWORD(lParam);\r
8158       pt.y = HIWORD(lParam);\r
8159       MenuPopup(hwnd, pt, hmenu, -1);\r
8160     }\r
8161     return 0;\r
8162   case WM_COMMAND:\r
8163     switch (LOWORD(wParam)) { \r
8164     case IDM_Undo:\r
8165       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8166       return 0;\r
8167     case IDM_SelectAll:\r
8168       sel.cpMin = 0;\r
8169       sel.cpMax = -1; /*999999?*/\r
8170       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8171       return 0;\r
8172     case IDM_Cut:\r
8173       SendMessage(hwnd, WM_CUT, 0, 0);\r
8174       return 0;\r
8175     case IDM_Paste:\r
8176       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8177       return 0;\r
8178     case IDM_Copy:\r
8179       SendMessage(hwnd, WM_COPY, 0, 0);\r
8180       return 0;\r
8181     }\r
8182     break;\r
8183   }\r
8184   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8185 }\r
8186 \r
8187 #define CO_MAX  100000\r
8188 #define CO_TRIM   1000\r
8189 \r
8190 LRESULT CALLBACK\r
8191 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8192 {\r
8193   static SnapData sd;\r
8194   static HWND hText, hInput /*, hFocus*/;\r
8195 //  InputSource *is = consoleInputSource;\r
8196   RECT rect;\r
8197   static int sizeX, sizeY;\r
8198   int newSizeX, newSizeY;\r
8199   MINMAXINFO *mmi;\r
8200 \r
8201   switch (message) {\r
8202   case WM_INITDIALOG: /* message: initialize dialog box */\r
8203     hwndConsole = hDlg;\r
8204     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8205     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8206     SetFocus(hInput);\r
8207     consoleTextWindowProc = (WNDPROC)\r
8208       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8209     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8210     consoleInputWindowProc = (WNDPROC)\r
8211       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8212     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8213     Colorize(ColorNormal, TRUE);\r
8214     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8215     ChangedConsoleFont();\r
8216     GetClientRect(hDlg, &rect);\r
8217     sizeX = rect.right;\r
8218     sizeY = rect.bottom;\r
8219     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8220         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8221       WINDOWPLACEMENT wp;\r
8222       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8223       wp.length = sizeof(WINDOWPLACEMENT);\r
8224       wp.flags = 0;\r
8225       wp.showCmd = SW_SHOW;\r
8226       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8227       wp.rcNormalPosition.left = wpConsole.x;\r
8228       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8229       wp.rcNormalPosition.top = wpConsole.y;\r
8230       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8231       SetWindowPlacement(hDlg, &wp);\r
8232     }\r
8233 #if 1\r
8234    // [HGM] Chessknight's change 2004-07-13\r
8235    else { /* Determine Defaults */\r
8236        WINDOWPLACEMENT wp;\r
8237        wpConsole.x = winWidth + 1;\r
8238        wpConsole.y = boardY;\r
8239        wpConsole.width = screenWidth -  winWidth;\r
8240        wpConsole.height = winHeight;\r
8241        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8242        wp.length = sizeof(WINDOWPLACEMENT);\r
8243        wp.flags = 0;\r
8244        wp.showCmd = SW_SHOW;\r
8245        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8246        wp.rcNormalPosition.left = wpConsole.x;\r
8247        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8248        wp.rcNormalPosition.top = wpConsole.y;\r
8249        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8250        SetWindowPlacement(hDlg, &wp);\r
8251     }\r
8252 #endif\r
8253     return FALSE;\r
8254 \r
8255   case WM_SETFOCUS:\r
8256     SetFocus(hInput);\r
8257     return 0;\r
8258 \r
8259   case WM_CLOSE:\r
8260     ExitEvent(0);\r
8261     /* not reached */\r
8262     break;\r
8263 \r
8264   case WM_SIZE:\r
8265     if (IsIconic(hDlg)) break;\r
8266     newSizeX = LOWORD(lParam);\r
8267     newSizeY = HIWORD(lParam);\r
8268     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8269       RECT rectText, rectInput;\r
8270       POINT pt;\r
8271       int newTextHeight, newTextWidth;\r
8272       GetWindowRect(hText, &rectText);\r
8273       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8274       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8275       if (newTextHeight < 0) {\r
8276         newSizeY += -newTextHeight;\r
8277         newTextHeight = 0;\r
8278       }\r
8279       SetWindowPos(hText, NULL, 0, 0,\r
8280         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8281       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8282       pt.x = rectInput.left;\r
8283       pt.y = rectInput.top + newSizeY - sizeY;\r
8284       ScreenToClient(hDlg, &pt);\r
8285       SetWindowPos(hInput, NULL, \r
8286         pt.x, pt.y, /* needs client coords */   \r
8287         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8288         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8289     }\r
8290     sizeX = newSizeX;\r
8291     sizeY = newSizeY;\r
8292     break;\r
8293 \r
8294   case WM_GETMINMAXINFO:\r
8295     /* Prevent resizing window too small */\r
8296     mmi = (MINMAXINFO *) lParam;\r
8297     mmi->ptMinTrackSize.x = 100;\r
8298     mmi->ptMinTrackSize.y = 100;\r
8299     break;\r
8300 \r
8301   /* [AS] Snapping */\r
8302   case WM_ENTERSIZEMOVE:\r
8303     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8304 \r
8305   case WM_SIZING:\r
8306     return OnSizing( &sd, hDlg, wParam, lParam );\r
8307 \r
8308   case WM_MOVING:\r
8309     return OnMoving( &sd, hDlg, wParam, lParam );\r
8310 \r
8311   case WM_EXITSIZEMOVE:\r
8312     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8313   }\r
8314 \r
8315   return DefWindowProc(hDlg, message, wParam, lParam);\r
8316 }\r
8317 \r
8318 \r
8319 VOID\r
8320 ConsoleCreate()\r
8321 {\r
8322   HWND hCons;\r
8323   if (hwndConsole) return;\r
8324   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8325   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8326 }\r
8327 \r
8328 \r
8329 VOID\r
8330 ConsoleOutput(char* data, int length, int forceVisible)\r
8331 {\r
8332   HWND hText;\r
8333   int trim, exlen;\r
8334   char *p, *q;\r
8335   char buf[CO_MAX+1];\r
8336   POINT pEnd;\r
8337   RECT rect;\r
8338   static int delayLF = 0;\r
8339   CHARRANGE savesel, sel;\r
8340 \r
8341   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8342   p = data;\r
8343   q = buf;\r
8344   if (delayLF) {\r
8345     *q++ = '\r';\r
8346     *q++ = '\n';\r
8347     delayLF = 0;\r
8348   }\r
8349   while (length--) {\r
8350     if (*p == '\n') {\r
8351       if (*++p) {\r
8352         *q++ = '\r';\r
8353         *q++ = '\n';\r
8354       } else {\r
8355         delayLF = 1;\r
8356       }\r
8357     } else if (*p == '\007') {\r
8358        MyPlaySound(&sounds[(int)SoundBell]);\r
8359        p++;\r
8360     } else {\r
8361       *q++ = *p++;\r
8362     }\r
8363   }\r
8364   *q = NULLCHAR;\r
8365   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8366   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8367   /* Save current selection */\r
8368   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8369   exlen = GetWindowTextLength(hText);\r
8370   /* Find out whether current end of text is visible */\r
8371   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8372   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8373   /* Trim existing text if it's too long */\r
8374   if (exlen + (q - buf) > CO_MAX) {\r
8375     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8376     sel.cpMin = 0;\r
8377     sel.cpMax = trim;\r
8378     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8379     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8380     exlen -= trim;\r
8381     savesel.cpMin -= trim;\r
8382     savesel.cpMax -= trim;\r
8383     if (exlen < 0) exlen = 0;\r
8384     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8385     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8386   }\r
8387   /* Append the new text */\r
8388   sel.cpMin = exlen;\r
8389   sel.cpMax = exlen;\r
8390   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8391   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8392   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8393   if (forceVisible || exlen == 0 ||\r
8394       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8395        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8396     /* Scroll to make new end of text visible if old end of text\r
8397        was visible or new text is an echo of user typein */\r
8398     sel.cpMin = 9999999;\r
8399     sel.cpMax = 9999999;\r
8400     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8401     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8402     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8403     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8404   }\r
8405   if (savesel.cpMax == exlen || forceVisible) {\r
8406     /* Move insert point to new end of text if it was at the old\r
8407        end of text or if the new text is an echo of user typein */\r
8408     sel.cpMin = 9999999;\r
8409     sel.cpMax = 9999999;\r
8410     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8411   } else {\r
8412     /* Restore previous selection */\r
8413     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8414   }\r
8415   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8416 }\r
8417 \r
8418 /*---------*/\r
8419 \r
8420 \r
8421 void\r
8422 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8423 {\r
8424   char buf[100];\r
8425   char *str;\r
8426   COLORREF oldFg, oldBg;\r
8427   HFONT oldFont;\r
8428   RECT rect;\r
8429 \r
8430   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8431 \r
8432   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8433   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8434   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8435 \r
8436   rect.left = x;\r
8437   rect.right = x + squareSize;\r
8438   rect.top  = y;\r
8439   rect.bottom = y + squareSize;\r
8440   str = buf;\r
8441 \r
8442   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8443                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8444              y, ETO_CLIPPED|ETO_OPAQUE,\r
8445              &rect, str, strlen(str), NULL);\r
8446 \r
8447   (void) SetTextColor(hdc, oldFg);\r
8448   (void) SetBkColor(hdc, oldBg);\r
8449   (void) SelectObject(hdc, oldFont);\r
8450 }\r
8451 \r
8452 void\r
8453 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8454               RECT *rect, char *color, char *flagFell)\r
8455 {\r
8456   char buf[100];\r
8457   char *str;\r
8458   COLORREF oldFg, oldBg;\r
8459   HFONT oldFont;\r
8460 \r
8461   if (appData.clockMode) {\r
8462     if (tinyLayout)\r
8463       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8464     else\r
8465       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8466     str = buf;\r
8467   } else {\r
8468     str = color;\r
8469   }\r
8470 \r
8471   if (highlight) {\r
8472     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8473     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8474   } else {\r
8475     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8476     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8477   }\r
8478   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8479 \r
8480   JAWS_SILENCE\r
8481 \r
8482   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8483              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8484              rect, str, strlen(str), NULL);\r
8485   if(logoHeight > 0 && appData.clockMode) {\r
8486       RECT r;\r
8487       sprintf(buf, "%s %s", buf+7, flagFell);\r
8488       r.top = rect->top + logoHeight/2;\r
8489       r.left = rect->left;\r
8490       r.right = rect->right;\r
8491       r.bottom = rect->bottom;\r
8492       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8493                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8494                  &r, str, strlen(str), NULL);\r
8495   }\r
8496   (void) SetTextColor(hdc, oldFg);\r
8497   (void) SetBkColor(hdc, oldBg);\r
8498   (void) SelectObject(hdc, oldFont);\r
8499 }\r
8500 \r
8501 \r
8502 int\r
8503 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8504            OVERLAPPED *ovl)\r
8505 {\r
8506   int ok, err;\r
8507 \r
8508   /* [AS]  */\r
8509   if( count <= 0 ) {\r
8510     if (appData.debugMode) {\r
8511       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8512     }\r
8513 \r
8514     return ERROR_INVALID_USER_BUFFER;\r
8515   }\r
8516 \r
8517   ResetEvent(ovl->hEvent);\r
8518   ovl->Offset = ovl->OffsetHigh = 0;\r
8519   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8520   if (ok) {\r
8521     err = NO_ERROR;\r
8522   } else {\r
8523     err = GetLastError();\r
8524     if (err == ERROR_IO_PENDING) {\r
8525       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8526       if (ok)\r
8527         err = NO_ERROR;\r
8528       else\r
8529         err = GetLastError();\r
8530     }\r
8531   }\r
8532   return err;\r
8533 }\r
8534 \r
8535 int\r
8536 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8537             OVERLAPPED *ovl)\r
8538 {\r
8539   int ok, err;\r
8540 \r
8541   ResetEvent(ovl->hEvent);\r
8542   ovl->Offset = ovl->OffsetHigh = 0;\r
8543   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8544   if (ok) {\r
8545     err = NO_ERROR;\r
8546   } else {\r
8547     err = GetLastError();\r
8548     if (err == ERROR_IO_PENDING) {\r
8549       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8550       if (ok)\r
8551         err = NO_ERROR;\r
8552       else\r
8553         err = GetLastError();\r
8554     }\r
8555   }\r
8556   return err;\r
8557 }\r
8558 \r
8559 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8560 void CheckForInputBufferFull( InputSource * is )\r
8561 {\r
8562     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8563         /* Look for end of line */\r
8564         char * p = is->buf;\r
8565         \r
8566         while( p < is->next && *p != '\n' ) {\r
8567             p++;\r
8568         }\r
8569 \r
8570         if( p >= is->next ) {\r
8571             if (appData.debugMode) {\r
8572                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8573             }\r
8574 \r
8575             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8576             is->count = (DWORD) -1;\r
8577             is->next = is->buf;\r
8578         }\r
8579     }\r
8580 }\r
8581 \r
8582 DWORD\r
8583 InputThread(LPVOID arg)\r
8584 {\r
8585   InputSource *is;\r
8586   OVERLAPPED ovl;\r
8587 \r
8588   is = (InputSource *) arg;\r
8589   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8590   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8591   while (is->hThread != NULL) {\r
8592     is->error = DoReadFile(is->hFile, is->next,\r
8593                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8594                            &is->count, &ovl);\r
8595     if (is->error == NO_ERROR) {\r
8596       is->next += is->count;\r
8597     } else {\r
8598       if (is->error == ERROR_BROKEN_PIPE) {\r
8599         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8600         is->count = 0;\r
8601       } else {\r
8602         is->count = (DWORD) -1;\r
8603         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8604         break; \r
8605       }\r
8606     }\r
8607 \r
8608     CheckForInputBufferFull( is );\r
8609 \r
8610     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8611 \r
8612     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8613 \r
8614     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8615   }\r
8616 \r
8617   CloseHandle(ovl.hEvent);\r
8618   CloseHandle(is->hFile);\r
8619 \r
8620   if (appData.debugMode) {\r
8621     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8622   }\r
8623 \r
8624   return 0;\r
8625 }\r
8626 \r
8627 \r
8628 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8629 DWORD\r
8630 NonOvlInputThread(LPVOID arg)\r
8631 {\r
8632   InputSource *is;\r
8633   char *p, *q;\r
8634   int i;\r
8635   char prev;\r
8636 \r
8637   is = (InputSource *) arg;\r
8638   while (is->hThread != NULL) {\r
8639     is->error = ReadFile(is->hFile, is->next,\r
8640                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8641                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8642     if (is->error == NO_ERROR) {\r
8643       /* Change CRLF to LF */\r
8644       if (is->next > is->buf) {\r
8645         p = is->next - 1;\r
8646         i = is->count + 1;\r
8647       } else {\r
8648         p = is->next;\r
8649         i = is->count;\r
8650       }\r
8651       q = p;\r
8652       prev = NULLCHAR;\r
8653       while (i > 0) {\r
8654         if (prev == '\r' && *p == '\n') {\r
8655           *(q-1) = '\n';\r
8656           is->count--;\r
8657         } else { \r
8658           *q++ = *p;\r
8659         }\r
8660         prev = *p++;\r
8661         i--;\r
8662       }\r
8663       *q = NULLCHAR;\r
8664       is->next = q;\r
8665     } else {\r
8666       if (is->error == ERROR_BROKEN_PIPE) {\r
8667         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8668         is->count = 0; \r
8669       } else {\r
8670         is->count = (DWORD) -1;\r
8671       }\r
8672     }\r
8673 \r
8674     CheckForInputBufferFull( is );\r
8675 \r
8676     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8677 \r
8678     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8679 \r
8680     if (is->count < 0) break;  /* Quit on error */\r
8681   }\r
8682   CloseHandle(is->hFile);\r
8683   return 0;\r
8684 }\r
8685 \r
8686 DWORD\r
8687 SocketInputThread(LPVOID arg)\r
8688 {\r
8689   InputSource *is;\r
8690 \r
8691   is = (InputSource *) arg;\r
8692   while (is->hThread != NULL) {\r
8693     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8694     if ((int)is->count == SOCKET_ERROR) {\r
8695       is->count = (DWORD) -1;\r
8696       is->error = WSAGetLastError();\r
8697     } else {\r
8698       is->error = NO_ERROR;\r
8699       is->next += is->count;\r
8700       if (is->count == 0 && is->second == is) {\r
8701         /* End of file on stderr; quit with no message */\r
8702         break;\r
8703       }\r
8704     }\r
8705     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8706 \r
8707     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8708 \r
8709     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8710   }\r
8711   return 0;\r
8712 }\r
8713 \r
8714 VOID\r
8715 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8716 {\r
8717   InputSource *is;\r
8718 \r
8719   is = (InputSource *) lParam;\r
8720   if (is->lineByLine) {\r
8721     /* Feed in lines one by one */\r
8722     char *p = is->buf;\r
8723     char *q = p;\r
8724     while (q < is->next) {\r
8725       if (*q++ == '\n') {\r
8726         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8727         p = q;\r
8728       }\r
8729     }\r
8730     \r
8731     /* Move any partial line to the start of the buffer */\r
8732     q = is->buf;\r
8733     while (p < is->next) {\r
8734       *q++ = *p++;\r
8735     }\r
8736     is->next = q;\r
8737 \r
8738     if (is->error != NO_ERROR || is->count == 0) {\r
8739       /* Notify backend of the error.  Note: If there was a partial\r
8740          line at the end, it is not flushed through. */\r
8741       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8742     }\r
8743   } else {\r
8744     /* Feed in the whole chunk of input at once */\r
8745     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8746     is->next = is->buf;\r
8747   }\r
8748 }\r
8749 \r
8750 /*---------------------------------------------------------------------------*\\r
8751  *\r
8752  *  Menu enables. Used when setting various modes.\r
8753  *\r
8754 \*---------------------------------------------------------------------------*/\r
8755 \r
8756 typedef struct {\r
8757   int item;\r
8758   int flags;\r
8759 } Enables;\r
8760 \r
8761 VOID\r
8762 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8763 {\r
8764   while (enab->item > 0) {\r
8765     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8766     enab++;\r
8767   }\r
8768 }\r
8769 \r
8770 Enables gnuEnables[] = {\r
8771   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8772   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8773   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8774   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8775   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8776   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8777   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8778   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8779   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8780   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8781   { -1, -1 }\r
8782 };\r
8783 \r
8784 Enables icsEnables[] = {\r
8785   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8786   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8787   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8788   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8789   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8790   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8791   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8792   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8793   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8794   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8795   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8796   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8797   { -1, -1 }\r
8798 };\r
8799 \r
8800 #ifdef ZIPPY\r
8801 Enables zippyEnables[] = {\r
8802   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8803   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8804   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8805   { -1, -1 }\r
8806 };\r
8807 #endif\r
8808 \r
8809 Enables ncpEnables[] = {\r
8810   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8811   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8812   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8813   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8814   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8815   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8816   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8817   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8818   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8819   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8820   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8821   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8822   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8823   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8824   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8825   { -1, -1 }\r
8826 };\r
8827 \r
8828 Enables trainingOnEnables[] = {\r
8829   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8830   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8831   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8832   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8833   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8834   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8835   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8836   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8837   { -1, -1 }\r
8838 };\r
8839 \r
8840 Enables trainingOffEnables[] = {\r
8841   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8842   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8843   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8844   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8845   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8846   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8847   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8848   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8849   { -1, -1 }\r
8850 };\r
8851 \r
8852 /* These modify either ncpEnables or gnuEnables */\r
8853 Enables cmailEnables[] = {\r
8854   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8855   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8856   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8857   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8858   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8859   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8860   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8861   { -1, -1 }\r
8862 };\r
8863 \r
8864 Enables machineThinkingEnables[] = {\r
8865   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8866   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8867   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8868   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8869   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8870   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8871   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8872   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8873   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8874   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8875   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8876   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8877   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8878   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8879   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8880   { -1, -1 }\r
8881 };\r
8882 \r
8883 Enables userThinkingEnables[] = {\r
8884   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8885   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8886   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8887   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8888   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8889   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8890   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8891   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8892   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8893   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8894   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8895   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8896   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8897   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8898   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8899   { -1, -1 }\r
8900 };\r
8901 \r
8902 /*---------------------------------------------------------------------------*\\r
8903  *\r
8904  *  Front-end interface functions exported by XBoard.\r
8905  *  Functions appear in same order as prototypes in frontend.h.\r
8906  * \r
8907 \*---------------------------------------------------------------------------*/\r
8908 VOID\r
8909 ModeHighlight()\r
8910 {\r
8911   static UINT prevChecked = 0;\r
8912   static int prevPausing = 0;\r
8913   UINT nowChecked;\r
8914 \r
8915   if (pausing != prevPausing) {\r
8916     prevPausing = pausing;\r
8917     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8918                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8919     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8920   }\r
8921 \r
8922   switch (gameMode) {\r
8923   case BeginningOfGame:\r
8924     if (appData.icsActive)\r
8925       nowChecked = IDM_IcsClient;\r
8926     else if (appData.noChessProgram)\r
8927       nowChecked = IDM_EditGame;\r
8928     else\r
8929       nowChecked = IDM_MachineBlack;\r
8930     break;\r
8931   case MachinePlaysBlack:\r
8932     nowChecked = IDM_MachineBlack;\r
8933     break;\r
8934   case MachinePlaysWhite:\r
8935     nowChecked = IDM_MachineWhite;\r
8936     break;\r
8937   case TwoMachinesPlay:\r
8938     nowChecked = IDM_TwoMachines;\r
8939     break;\r
8940   case AnalyzeMode:\r
8941     nowChecked = IDM_AnalysisMode;\r
8942     break;\r
8943   case AnalyzeFile:\r
8944     nowChecked = IDM_AnalyzeFile;\r
8945     break;\r
8946   case EditGame:\r
8947     nowChecked = IDM_EditGame;\r
8948     break;\r
8949   case PlayFromGameFile:\r
8950     nowChecked = IDM_LoadGame;\r
8951     break;\r
8952   case EditPosition:\r
8953     nowChecked = IDM_EditPosition;\r
8954     break;\r
8955   case Training:\r
8956     nowChecked = IDM_Training;\r
8957     break;\r
8958   case IcsPlayingWhite:\r
8959   case IcsPlayingBlack:\r
8960   case IcsObserving:\r
8961   case IcsIdle:\r
8962     nowChecked = IDM_IcsClient;\r
8963     break;\r
8964   default:\r
8965   case EndOfGame:\r
8966     nowChecked = 0;\r
8967     break;\r
8968   }\r
8969   if (prevChecked != 0)\r
8970     (void) CheckMenuItem(GetMenu(hwndMain),\r
8971                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8972   if (nowChecked != 0)\r
8973     (void) CheckMenuItem(GetMenu(hwndMain),\r
8974                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8975 \r
8976   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8977     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8978                           MF_BYCOMMAND|MF_ENABLED);\r
8979   } else {\r
8980     (void) EnableMenuItem(GetMenu(hwndMain), \r
8981                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8982   }\r
8983 \r
8984   prevChecked = nowChecked;\r
8985 \r
8986   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8987   if (appData.icsActive) {\r
8988        if (appData.icsEngineAnalyze) {\r
8989                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8990                        MF_BYCOMMAND|MF_CHECKED);\r
8991        } else {\r
8992                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8993                        MF_BYCOMMAND|MF_UNCHECKED);\r
8994        }\r
8995   }\r
8996 }\r
8997 \r
8998 VOID\r
8999 SetICSMode()\r
9000 {\r
9001   HMENU hmenu = GetMenu(hwndMain);\r
9002   SetMenuEnables(hmenu, icsEnables);\r
9003   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
9004     MF_BYPOSITION|MF_ENABLED);\r
9005 #ifdef ZIPPY\r
9006   if (appData.zippyPlay) {\r
9007     SetMenuEnables(hmenu, zippyEnables);\r
9008     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
9009          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9010           MF_BYCOMMAND|MF_ENABLED);\r
9011   }\r
9012 #endif\r
9013 }\r
9014 \r
9015 VOID\r
9016 SetGNUMode()\r
9017 {\r
9018   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
9019 }\r
9020 \r
9021 VOID\r
9022 SetNCPMode()\r
9023 {\r
9024   HMENU hmenu = GetMenu(hwndMain);\r
9025   SetMenuEnables(hmenu, ncpEnables);\r
9026   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
9027     MF_BYPOSITION|MF_GRAYED);\r
9028     DrawMenuBar(hwndMain);\r
9029 }\r
9030 \r
9031 VOID\r
9032 SetCmailMode()\r
9033 {\r
9034   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9035 }\r
9036 \r
9037 VOID \r
9038 SetTrainingModeOn()\r
9039 {\r
9040   int i;\r
9041   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9042   for (i = 0; i < N_BUTTONS; i++) {\r
9043     if (buttonDesc[i].hwnd != NULL)\r
9044       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9045   }\r
9046   CommentPopDown();\r
9047 }\r
9048 \r
9049 VOID SetTrainingModeOff()\r
9050 {\r
9051   int i;\r
9052   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9053   for (i = 0; i < N_BUTTONS; i++) {\r
9054     if (buttonDesc[i].hwnd != NULL)\r
9055       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9056   }\r
9057 }\r
9058 \r
9059 \r
9060 VOID\r
9061 SetUserThinkingEnables()\r
9062 {\r
9063   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9064 }\r
9065 \r
9066 VOID\r
9067 SetMachineThinkingEnables()\r
9068 {\r
9069   HMENU hMenu = GetMenu(hwndMain);\r
9070   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9071 \r
9072   SetMenuEnables(hMenu, machineThinkingEnables);\r
9073 \r
9074   if (gameMode == MachinePlaysBlack) {\r
9075     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9076   } else if (gameMode == MachinePlaysWhite) {\r
9077     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9078   } else if (gameMode == TwoMachinesPlay) {\r
9079     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9080   }\r
9081 }\r
9082 \r
9083 \r
9084 VOID\r
9085 DisplayTitle(char *str)\r
9086 {\r
9087   char title[MSG_SIZ], *host;\r
9088   if (str[0] != NULLCHAR) {\r
9089     strcpy(title, str);\r
9090   } else if (appData.icsActive) {\r
9091     if (appData.icsCommPort[0] != NULLCHAR)\r
9092       host = "ICS";\r
9093     else \r
9094       host = appData.icsHost;\r
9095     sprintf(title, "%s: %s", szTitle, host);\r
9096   } else if (appData.noChessProgram) {\r
9097     strcpy(title, szTitle);\r
9098   } else {\r
9099     strcpy(title, szTitle);\r
9100     strcat(title, ": ");\r
9101     strcat(title, first.tidy);\r
9102   }\r
9103   SetWindowText(hwndMain, title);\r
9104 }\r
9105 \r
9106 \r
9107 VOID\r
9108 DisplayMessage(char *str1, char *str2)\r
9109 {\r
9110   HDC hdc;\r
9111   HFONT oldFont;\r
9112   int remain = MESSAGE_TEXT_MAX - 1;\r
9113   int len;\r
9114 \r
9115   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9116   messageText[0] = NULLCHAR;\r
9117   if (*str1) {\r
9118     len = strlen(str1);\r
9119     if (len > remain) len = remain;\r
9120     strncpy(messageText, str1, len);\r
9121     messageText[len] = NULLCHAR;\r
9122     remain -= len;\r
9123   }\r
9124   if (*str2 && remain >= 2) {\r
9125     if (*str1) {\r
9126       strcat(messageText, "  ");\r
9127       remain -= 2;\r
9128     }\r
9129     len = strlen(str2);\r
9130     if (len > remain) len = remain;\r
9131     strncat(messageText, str2, len);\r
9132   }\r
9133   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9134 \r
9135   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9136 \r
9137   SAYMACHINEMOVE();\r
9138 \r
9139   hdc = GetDC(hwndMain);\r
9140   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9141   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9142              &messageRect, messageText, strlen(messageText), NULL);\r
9143   (void) SelectObject(hdc, oldFont);\r
9144   (void) ReleaseDC(hwndMain, hdc);\r
9145 }\r
9146 \r
9147 VOID\r
9148 DisplayError(char *str, int error)\r
9149 {\r
9150   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9151   int len;\r
9152 \r
9153   if (error == 0) {\r
9154     strcpy(buf, str);\r
9155   } else {\r
9156     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9157                         NULL, error, LANG_NEUTRAL,\r
9158                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9159     if (len > 0) {\r
9160       sprintf(buf, "%s:\n%s", str, buf2);\r
9161     } else {\r
9162       ErrorMap *em = errmap;\r
9163       while (em->err != 0 && em->err != error) em++;\r
9164       if (em->err != 0) {\r
9165         sprintf(buf, "%s:\n%s", str, em->msg);\r
9166       } else {\r
9167         sprintf(buf, "%s:\nError code %d", str, error);\r
9168       }\r
9169     }\r
9170   }\r
9171   \r
9172   ErrorPopUp("Error", buf);\r
9173 }\r
9174 \r
9175 \r
9176 VOID\r
9177 DisplayMoveError(char *str)\r
9178 {\r
9179   fromX = fromY = -1;\r
9180   ClearHighlights();\r
9181   DrawPosition(FALSE, NULL);\r
9182   if (appData.popupMoveErrors) {\r
9183     ErrorPopUp("Error", str);\r
9184   } else {\r
9185     DisplayMessage(str, "");\r
9186     moveErrorMessageUp = TRUE;\r
9187   }\r
9188 }\r
9189 \r
9190 VOID\r
9191 DisplayFatalError(char *str, int error, int exitStatus)\r
9192 {\r
9193   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9194   int len;\r
9195   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9196 \r
9197   if (error != 0) {\r
9198     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9199                         NULL, error, LANG_NEUTRAL,\r
9200                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9201     if (len > 0) {\r
9202       sprintf(buf, "%s:\n%s", str, buf2);\r
9203     } else {\r
9204       ErrorMap *em = errmap;\r
9205       while (em->err != 0 && em->err != error) em++;\r
9206       if (em->err != 0) {\r
9207         sprintf(buf, "%s:\n%s", str, em->msg);\r
9208       } else {\r
9209         sprintf(buf, "%s:\nError code %d", str, error);\r
9210       }\r
9211     }\r
9212     str = buf;\r
9213   }\r
9214   if (appData.debugMode) {\r
9215     fprintf(debugFP, "%s: %s\n", label, str);\r
9216   }\r
9217   if (appData.popupExitMessage) {\r
9218     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9219                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9220   }\r
9221   ExitEvent(exitStatus);\r
9222 }\r
9223 \r
9224 \r
9225 VOID\r
9226 DisplayInformation(char *str)\r
9227 {\r
9228   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9229 }\r
9230 \r
9231 \r
9232 VOID\r
9233 DisplayNote(char *str)\r
9234 {\r
9235   ErrorPopUp("Note", str);\r
9236 }\r
9237 \r
9238 \r
9239 typedef struct {\r
9240   char *title, *question, *replyPrefix;\r
9241   ProcRef pr;\r
9242 } QuestionParams;\r
9243 \r
9244 LRESULT CALLBACK\r
9245 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9246 {\r
9247   static QuestionParams *qp;\r
9248   char reply[MSG_SIZ];\r
9249   int len, err;\r
9250 \r
9251   switch (message) {\r
9252   case WM_INITDIALOG:\r
9253     qp = (QuestionParams *) lParam;\r
9254     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9255     SetWindowText(hDlg, qp->title);\r
9256     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9257     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9258     return FALSE;\r
9259 \r
9260   case WM_COMMAND:\r
9261     switch (LOWORD(wParam)) {\r
9262     case IDOK:\r
9263       strcpy(reply, qp->replyPrefix);\r
9264       if (*reply) strcat(reply, " ");\r
9265       len = strlen(reply);\r
9266       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9267       strcat(reply, "\n");\r
9268       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9269       EndDialog(hDlg, TRUE);\r
9270       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9271       return TRUE;\r
9272     case IDCANCEL:\r
9273       EndDialog(hDlg, FALSE);\r
9274       return TRUE;\r
9275     default:\r
9276       break;\r
9277     }\r
9278     break;\r
9279   }\r
9280   return FALSE;\r
9281 }\r
9282 \r
9283 VOID\r
9284 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9285 {\r
9286     QuestionParams qp;\r
9287     FARPROC lpProc;\r
9288     \r
9289     qp.title = title;\r
9290     qp.question = question;\r
9291     qp.replyPrefix = replyPrefix;\r
9292     qp.pr = pr;\r
9293     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9294     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9295       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9296     FreeProcInstance(lpProc);\r
9297 }\r
9298 \r
9299 /* [AS] Pick FRC position */\r
9300 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9301 {\r
9302     static int * lpIndexFRC;\r
9303     BOOL index_is_ok;\r
9304     char buf[16];\r
9305 \r
9306     switch( message )\r
9307     {\r
9308     case WM_INITDIALOG:\r
9309         lpIndexFRC = (int *) lParam;\r
9310 \r
9311         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9312 \r
9313         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9314         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9315         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9316         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9317 \r
9318         break;\r
9319 \r
9320     case WM_COMMAND:\r
9321         switch( LOWORD(wParam) ) {\r
9322         case IDOK:\r
9323             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9324             EndDialog( hDlg, 0 );\r
9325             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9326             return TRUE;\r
9327         case IDCANCEL:\r
9328             EndDialog( hDlg, 1 );   \r
9329             return TRUE;\r
9330         case IDC_NFG_Edit:\r
9331             if( HIWORD(wParam) == EN_CHANGE ) {\r
9332                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9333 \r
9334                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9335             }\r
9336             return TRUE;\r
9337         case IDC_NFG_Random:\r
9338             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9339             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9340             return TRUE;\r
9341         }\r
9342 \r
9343         break;\r
9344     }\r
9345 \r
9346     return FALSE;\r
9347 }\r
9348 \r
9349 int NewGameFRC()\r
9350 {\r
9351     int result;\r
9352     int index = appData.defaultFrcPosition;\r
9353     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9354 \r
9355     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9356 \r
9357     if( result == 0 ) {\r
9358         appData.defaultFrcPosition = index;\r
9359     }\r
9360 \r
9361     return result;\r
9362 }\r
9363 \r
9364 /* [AS] Game list options */\r
9365 typedef struct {\r
9366     char id;\r
9367     char * name;\r
9368 } GLT_Item;\r
9369 \r
9370 static GLT_Item GLT_ItemInfo[] = {\r
9371     { GLT_EVENT,      "Event" },\r
9372     { GLT_SITE,       "Site" },\r
9373     { GLT_DATE,       "Date" },\r
9374     { GLT_ROUND,      "Round" },\r
9375     { GLT_PLAYERS,    "Players" },\r
9376     { GLT_RESULT,     "Result" },\r
9377     { GLT_WHITE_ELO,  "White Rating" },\r
9378     { GLT_BLACK_ELO,  "Black Rating" },\r
9379     { GLT_TIME_CONTROL,"Time Control" },\r
9380     { GLT_VARIANT,    "Variant" },\r
9381     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9382     { 0, 0 }\r
9383 };\r
9384 \r
9385 const char * GLT_FindItem( char id )\r
9386 {\r
9387     const char * result = 0;\r
9388 \r
9389     GLT_Item * list = GLT_ItemInfo;\r
9390 \r
9391     while( list->id != 0 ) {\r
9392         if( list->id == id ) {\r
9393             result = list->name;\r
9394             break;\r
9395         }\r
9396 \r
9397         list++;\r
9398     }\r
9399 \r
9400     return result;\r
9401 }\r
9402 \r
9403 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9404 {\r
9405     const char * name = GLT_FindItem( id );\r
9406 \r
9407     if( name != 0 ) {\r
9408         if( index >= 0 ) {\r
9409             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9410         }\r
9411         else {\r
9412             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9413         }\r
9414     }\r
9415 }\r
9416 \r
9417 void GLT_TagsToList( HWND hDlg, char * tags )\r
9418 {\r
9419     char * pc = tags;\r
9420 \r
9421     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9422 \r
9423     while( *pc ) {\r
9424         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9425         pc++;\r
9426     }\r
9427 \r
9428     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9429 \r
9430     pc = GLT_ALL_TAGS;\r
9431 \r
9432     while( *pc ) {\r
9433         if( strchr( tags, *pc ) == 0 ) {\r
9434             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9435         }\r
9436         pc++;\r
9437     }\r
9438 \r
9439     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9440 }\r
9441 \r
9442 char GLT_ListItemToTag( HWND hDlg, int index )\r
9443 {\r
9444     char result = '\0';\r
9445     char name[128];\r
9446 \r
9447     GLT_Item * list = GLT_ItemInfo;\r
9448 \r
9449     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9450         while( list->id != 0 ) {\r
9451             if( strcmp( list->name, name ) == 0 ) {\r
9452                 result = list->id;\r
9453                 break;\r
9454             }\r
9455 \r
9456             list++;\r
9457         }\r
9458     }\r
9459 \r
9460     return result;\r
9461 }\r
9462 \r
9463 void GLT_MoveSelection( HWND hDlg, int delta )\r
9464 {\r
9465     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9466     int idx2 = idx1 + delta;\r
9467     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9468 \r
9469     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9470         char buf[128];\r
9471 \r
9472         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9473         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9474         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9475         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9476     }\r
9477 }\r
9478 \r
9479 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9480 {\r
9481     static char glt[64];\r
9482     static char * lpUserGLT;\r
9483 \r
9484     switch( message )\r
9485     {\r
9486     case WM_INITDIALOG:\r
9487         lpUserGLT = (char *) lParam;\r
9488         \r
9489         strcpy( glt, lpUserGLT );\r
9490 \r
9491         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9492 \r
9493         /* Initialize list */\r
9494         GLT_TagsToList( hDlg, glt );\r
9495 \r
9496         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9497 \r
9498         break;\r
9499 \r
9500     case WM_COMMAND:\r
9501         switch( LOWORD(wParam) ) {\r
9502         case IDOK:\r
9503             {\r
9504                 char * pc = lpUserGLT;\r
9505                 int idx = 0;\r
9506 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9507                 char id;\r
9508 \r
9509                 do {\r
9510                     id = GLT_ListItemToTag( hDlg, idx );\r
9511 \r
9512                     *pc++ = id;\r
9513                     idx++;\r
9514                 } while( id != '\0' );\r
9515             }\r
9516             EndDialog( hDlg, 0 );\r
9517             return TRUE;\r
9518         case IDCANCEL:\r
9519             EndDialog( hDlg, 1 );\r
9520             return TRUE;\r
9521 \r
9522         case IDC_GLT_Default:\r
9523             strcpy( glt, GLT_DEFAULT_TAGS );\r
9524             GLT_TagsToList( hDlg, glt );\r
9525             return TRUE;\r
9526 \r
9527         case IDC_GLT_Restore:\r
9528             strcpy( glt, lpUserGLT );\r
9529             GLT_TagsToList( hDlg, glt );\r
9530             return TRUE;\r
9531 \r
9532         case IDC_GLT_Up:\r
9533             GLT_MoveSelection( hDlg, -1 );\r
9534             return TRUE;\r
9535 \r
9536         case IDC_GLT_Down:\r
9537             GLT_MoveSelection( hDlg, +1 );\r
9538             return TRUE;\r
9539         }\r
9540 \r
9541         break;\r
9542     }\r
9543 \r
9544     return FALSE;\r
9545 }\r
9546 \r
9547 int GameListOptions()\r
9548 {\r
9549     char glt[64];\r
9550     int result;\r
9551     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9552 \r
9553     strcpy( glt, appData.gameListTags );\r
9554 \r
9555     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9556 \r
9557     if( result == 0 ) {\r
9558         /* [AS] Memory leak here! */\r
9559         appData.gameListTags = strdup( glt ); \r
9560     }\r
9561 \r
9562     return result;\r
9563 }\r
9564 \r
9565 \r
9566 VOID\r
9567 DisplayIcsInteractionTitle(char *str)\r
9568 {\r
9569   char consoleTitle[MSG_SIZ];\r
9570 \r
9571   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9572   SetWindowText(hwndConsole, consoleTitle);\r
9573 }\r
9574 \r
9575 void\r
9576 DrawPosition(int fullRedraw, Board board)\r
9577 {\r
9578   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9579 }\r
9580 \r
9581 \r
9582 VOID\r
9583 ResetFrontEnd()\r
9584 {\r
9585   fromX = fromY = -1;\r
9586   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9587     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9588     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9589     dragInfo.lastpos = dragInfo.pos;\r
9590     dragInfo.start.x = dragInfo.start.y = -1;\r
9591     dragInfo.from = dragInfo.start;\r
9592     ReleaseCapture();\r
9593     DrawPosition(TRUE, NULL);\r
9594   }\r
9595 }\r
9596 \r
9597 \r
9598 VOID\r
9599 CommentPopUp(char *title, char *str)\r
9600 {\r
9601   HWND hwnd = GetActiveWindow();\r
9602   EitherCommentPopUp(0, title, str, FALSE);\r
9603   SetActiveWindow(hwnd);\r
9604 }\r
9605 \r
9606 VOID\r
9607 CommentPopDown(void)\r
9608 {\r
9609   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9610   if (commentDialog) {\r
9611     ShowWindow(commentDialog, SW_HIDE);\r
9612   }\r
9613   commentDialogUp = FALSE;\r
9614 }\r
9615 \r
9616 VOID\r
9617 EditCommentPopUp(int index, char *title, char *str)\r
9618 {\r
9619   EitherCommentPopUp(index, title, str, TRUE);\r
9620 }\r
9621 \r
9622 \r
9623 VOID\r
9624 RingBell()\r
9625 {\r
9626   MyPlaySound(&sounds[(int)SoundMove]);\r
9627 }\r
9628 \r
9629 VOID PlayIcsWinSound()\r
9630 {\r
9631   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9632 }\r
9633 \r
9634 VOID PlayIcsLossSound()\r
9635 {\r
9636   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9637 }\r
9638 \r
9639 VOID PlayIcsDrawSound()\r
9640 {\r
9641   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9642 }\r
9643 \r
9644 VOID PlayIcsUnfinishedSound()\r
9645 {\r
9646   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9647 }\r
9648 \r
9649 VOID\r
9650 PlayAlarmSound()\r
9651 {\r
9652   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9653 }\r
9654 \r
9655 \r
9656 VOID\r
9657 EchoOn()\r
9658 {\r
9659   HWND hInput;\r
9660   consoleEcho = TRUE;\r
9661   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9662   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9663   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9664 }\r
9665 \r
9666 \r
9667 VOID\r
9668 EchoOff()\r
9669 {\r
9670   CHARFORMAT cf;\r
9671   HWND hInput;\r
9672   consoleEcho = FALSE;\r
9673   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9674   /* This works OK: set text and background both to the same color */\r
9675   cf = consoleCF;\r
9676   cf.crTextColor = COLOR_ECHOOFF;\r
9677   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9678   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9679 }\r
9680 \r
9681 /* No Raw()...? */\r
9682 \r
9683 void Colorize(ColorClass cc, int continuation)\r
9684 {\r
9685   currentColorClass = cc;\r
9686   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9687   consoleCF.crTextColor = textAttribs[cc].color;\r
9688   consoleCF.dwEffects = textAttribs[cc].effects;\r
9689   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9690 }\r
9691 \r
9692 char *\r
9693 UserName()\r
9694 {\r
9695   static char buf[MSG_SIZ];\r
9696   DWORD bufsiz = MSG_SIZ;\r
9697 \r
9698   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9699         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9700   }\r
9701   if (!GetUserName(buf, &bufsiz)) {\r
9702     /*DisplayError("Error getting user name", GetLastError());*/\r
9703     strcpy(buf, "User");\r
9704   }\r
9705   return buf;\r
9706 }\r
9707 \r
9708 char *\r
9709 HostName()\r
9710 {\r
9711   static char buf[MSG_SIZ];\r
9712   DWORD bufsiz = MSG_SIZ;\r
9713 \r
9714   if (!GetComputerName(buf, &bufsiz)) {\r
9715     /*DisplayError("Error getting host name", GetLastError());*/\r
9716     strcpy(buf, "Unknown");\r
9717   }\r
9718   return buf;\r
9719 }\r
9720 \r
9721 \r
9722 int\r
9723 ClockTimerRunning()\r
9724 {\r
9725   return clockTimerEvent != 0;\r
9726 }\r
9727 \r
9728 int\r
9729 StopClockTimer()\r
9730 {\r
9731   if (clockTimerEvent == 0) return FALSE;\r
9732   KillTimer(hwndMain, clockTimerEvent);\r
9733   clockTimerEvent = 0;\r
9734   return TRUE;\r
9735 }\r
9736 \r
9737 void\r
9738 StartClockTimer(long millisec)\r
9739 {\r
9740   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9741                              (UINT) millisec, NULL);\r
9742 }\r
9743 \r
9744 void\r
9745 DisplayWhiteClock(long timeRemaining, int highlight)\r
9746 {\r
9747   HDC hdc;\r
9748   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9749 \r
9750   if(appData.noGUI) return;\r
9751   hdc = GetDC(hwndMain);\r
9752   if (!IsIconic(hwndMain)) {\r
9753     DisplayAClock(hdc, timeRemaining, highlight, \r
9754                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9755   }\r
9756   if (highlight && iconCurrent == iconBlack) {\r
9757     iconCurrent = iconWhite;\r
9758     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9759     if (IsIconic(hwndMain)) {\r
9760       DrawIcon(hdc, 2, 2, iconCurrent);\r
9761     }\r
9762   }\r
9763   (void) ReleaseDC(hwndMain, hdc);\r
9764   if (hwndConsole)\r
9765     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9766 }\r
9767 \r
9768 void\r
9769 DisplayBlackClock(long timeRemaining, int highlight)\r
9770 {\r
9771   HDC hdc;\r
9772   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9773 \r
9774   if(appData.noGUI) return;\r
9775   hdc = GetDC(hwndMain);\r
9776   if (!IsIconic(hwndMain)) {\r
9777     DisplayAClock(hdc, timeRemaining, highlight, \r
9778                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9779   }\r
9780   if (highlight && iconCurrent == iconWhite) {\r
9781     iconCurrent = iconBlack;\r
9782     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9783     if (IsIconic(hwndMain)) {\r
9784       DrawIcon(hdc, 2, 2, iconCurrent);\r
9785     }\r
9786   }\r
9787   (void) ReleaseDC(hwndMain, hdc);\r
9788   if (hwndConsole)\r
9789     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9790 }\r
9791 \r
9792 \r
9793 int\r
9794 LoadGameTimerRunning()\r
9795 {\r
9796   return loadGameTimerEvent != 0;\r
9797 }\r
9798 \r
9799 int\r
9800 StopLoadGameTimer()\r
9801 {\r
9802   if (loadGameTimerEvent == 0) return FALSE;\r
9803   KillTimer(hwndMain, loadGameTimerEvent);\r
9804   loadGameTimerEvent = 0;\r
9805   return TRUE;\r
9806 }\r
9807 \r
9808 void\r
9809 StartLoadGameTimer(long millisec)\r
9810 {\r
9811   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9812                                 (UINT) millisec, NULL);\r
9813 }\r
9814 \r
9815 void\r
9816 AutoSaveGame()\r
9817 {\r
9818   char *defName;\r
9819   FILE *f;\r
9820   char fileTitle[MSG_SIZ];\r
9821 \r
9822   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9823   f = OpenFileDialog(hwndMain, "a", defName,\r
9824                      appData.oldSaveStyle ? "gam" : "pgn",\r
9825                      GAME_FILT, \r
9826                      "Save Game to File", NULL, fileTitle, NULL);\r
9827   if (f != NULL) {\r
9828     SaveGame(f, 0, "");\r
9829     fclose(f);\r
9830   }\r
9831 }\r
9832 \r
9833 \r
9834 void\r
9835 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9836 {\r
9837   if (delayedTimerEvent != 0) {\r
9838     if (appData.debugMode) {\r
9839       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9840     }\r
9841     KillTimer(hwndMain, delayedTimerEvent);\r
9842     delayedTimerEvent = 0;\r
9843     delayedTimerCallback();\r
9844   }\r
9845   delayedTimerCallback = cb;\r
9846   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9847                                 (UINT) millisec, NULL);\r
9848 }\r
9849 \r
9850 DelayedEventCallback\r
9851 GetDelayedEvent()\r
9852 {\r
9853   if (delayedTimerEvent) {\r
9854     return delayedTimerCallback;\r
9855   } else {\r
9856     return NULL;\r
9857   }\r
9858 }\r
9859 \r
9860 void\r
9861 CancelDelayedEvent()\r
9862 {\r
9863   if (delayedTimerEvent) {\r
9864     KillTimer(hwndMain, delayedTimerEvent);\r
9865     delayedTimerEvent = 0;\r
9866   }\r
9867 }\r
9868 \r
9869 DWORD GetWin32Priority(int nice)\r
9870 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9871 /*\r
9872 REALTIME_PRIORITY_CLASS     0x00000100\r
9873 HIGH_PRIORITY_CLASS         0x00000080\r
9874 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9875 NORMAL_PRIORITY_CLASS       0x00000020\r
9876 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9877 IDLE_PRIORITY_CLASS         0x00000040\r
9878 */\r
9879         if (nice < -15) return 0x00000080;\r
9880         if (nice < 0)   return 0x00008000;\r
9881         if (nice == 0)  return 0x00000020;\r
9882         if (nice < 15)  return 0x00004000;\r
9883         return 0x00000040;\r
9884 }\r
9885 \r
9886 /* Start a child process running the given program.\r
9887    The process's standard output can be read from "from", and its\r
9888    standard input can be written to "to".\r
9889    Exit with fatal error if anything goes wrong.\r
9890    Returns an opaque pointer that can be used to destroy the process\r
9891    later.\r
9892 */\r
9893 int\r
9894 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9895 {\r
9896 #define BUFSIZE 4096\r
9897 \r
9898   HANDLE hChildStdinRd, hChildStdinWr,\r
9899     hChildStdoutRd, hChildStdoutWr;\r
9900   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9901   SECURITY_ATTRIBUTES saAttr;\r
9902   BOOL fSuccess;\r
9903   PROCESS_INFORMATION piProcInfo;\r
9904   STARTUPINFO siStartInfo;\r
9905   ChildProc *cp;\r
9906   char buf[MSG_SIZ];\r
9907   DWORD err;\r
9908 \r
9909   if (appData.debugMode) {\r
9910     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9911   }\r
9912 \r
9913   *pr = NoProc;\r
9914 \r
9915   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9916   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9917   saAttr.bInheritHandle = TRUE;\r
9918   saAttr.lpSecurityDescriptor = NULL;\r
9919 \r
9920   /*\r
9921    * The steps for redirecting child's STDOUT:\r
9922    *     1. Create anonymous pipe to be STDOUT for child.\r
9923    *     2. Create a noninheritable duplicate of read handle,\r
9924    *         and close the inheritable read handle.\r
9925    */\r
9926 \r
9927   /* Create a pipe for the child's STDOUT. */\r
9928   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9929     return GetLastError();\r
9930   }\r
9931 \r
9932   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9933   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9934                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9935                              FALSE,     /* not inherited */\r
9936                              DUPLICATE_SAME_ACCESS);\r
9937   if (! fSuccess) {\r
9938     return GetLastError();\r
9939   }\r
9940   CloseHandle(hChildStdoutRd);\r
9941 \r
9942   /*\r
9943    * The steps for redirecting child's STDIN:\r
9944    *     1. Create anonymous pipe to be STDIN for child.\r
9945    *     2. Create a noninheritable duplicate of write handle,\r
9946    *         and close the inheritable write handle.\r
9947    */\r
9948 \r
9949   /* Create a pipe for the child's STDIN. */\r
9950   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9951     return GetLastError();\r
9952   }\r
9953 \r
9954   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9955   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9956                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9957                              FALSE,     /* not inherited */\r
9958                              DUPLICATE_SAME_ACCESS);\r
9959   if (! fSuccess) {\r
9960     return GetLastError();\r
9961   }\r
9962   CloseHandle(hChildStdinWr);\r
9963 \r
9964   /* Arrange to (1) look in dir for the child .exe file, and\r
9965    * (2) have dir be the child's working directory.  Interpret\r
9966    * dir relative to the directory WinBoard loaded from. */\r
9967   GetCurrentDirectory(MSG_SIZ, buf);\r
9968   SetCurrentDirectory(installDir);\r
9969   SetCurrentDirectory(dir);\r
9970 \r
9971   /* Now create the child process. */\r
9972 \r
9973   siStartInfo.cb = sizeof(STARTUPINFO);\r
9974   siStartInfo.lpReserved = NULL;\r
9975   siStartInfo.lpDesktop = NULL;\r
9976   siStartInfo.lpTitle = NULL;\r
9977   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9978   siStartInfo.cbReserved2 = 0;\r
9979   siStartInfo.lpReserved2 = NULL;\r
9980   siStartInfo.hStdInput = hChildStdinRd;\r
9981   siStartInfo.hStdOutput = hChildStdoutWr;\r
9982   siStartInfo.hStdError = hChildStdoutWr;\r
9983 \r
9984   fSuccess = CreateProcess(NULL,\r
9985                            cmdLine,        /* command line */\r
9986                            NULL,           /* process security attributes */\r
9987                            NULL,           /* primary thread security attrs */\r
9988                            TRUE,           /* handles are inherited */\r
9989                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9990                            NULL,           /* use parent's environment */\r
9991                            NULL,\r
9992                            &siStartInfo, /* STARTUPINFO pointer */\r
9993                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9994 \r
9995   err = GetLastError();\r
9996   SetCurrentDirectory(buf); /* return to prev directory */\r
9997   if (! fSuccess) {\r
9998     return err;\r
9999   }\r
10000 \r
10001   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
10002     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
10003     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
10004   }\r
10005 \r
10006   /* Close the handles we don't need in the parent */\r
10007   CloseHandle(piProcInfo.hThread);\r
10008   CloseHandle(hChildStdinRd);\r
10009   CloseHandle(hChildStdoutWr);\r
10010 \r
10011   /* Prepare return value */\r
10012   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10013   cp->kind = CPReal;\r
10014   cp->hProcess = piProcInfo.hProcess;\r
10015   cp->pid = piProcInfo.dwProcessId;\r
10016   cp->hFrom = hChildStdoutRdDup;\r
10017   cp->hTo = hChildStdinWrDup;\r
10018 \r
10019   *pr = (void *) cp;\r
10020 \r
10021   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10022      2000 where engines sometimes don't see the initial command(s)\r
10023      from WinBoard and hang.  I don't understand how that can happen,\r
10024      but the Sleep is harmless, so I've put it in.  Others have also\r
10025      reported what may be the same problem, so hopefully this will fix\r
10026      it for them too.  */\r
10027   Sleep(500);\r
10028 \r
10029   return NO_ERROR;\r
10030 }\r
10031 \r
10032 \r
10033 void\r
10034 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10035 {\r
10036   ChildProc *cp; int result;\r
10037 \r
10038   cp = (ChildProc *) pr;\r
10039   if (cp == NULL) return;\r
10040 \r
10041   switch (cp->kind) {\r
10042   case CPReal:\r
10043     /* TerminateProcess is considered harmful, so... */\r
10044     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10045     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10046     /* The following doesn't work because the chess program\r
10047        doesn't "have the same console" as WinBoard.  Maybe\r
10048        we could arrange for this even though neither WinBoard\r
10049        nor the chess program uses a console for stdio? */\r
10050     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10051 \r
10052     /* [AS] Special termination modes for misbehaving programs... */\r
10053     if( signal == 9 ) { \r
10054         result = TerminateProcess( cp->hProcess, 0 );\r
10055 \r
10056         if ( appData.debugMode) {\r
10057             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10058         }\r
10059     }\r
10060     else if( signal == 10 ) {\r
10061         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10062 \r
10063         if( dw != WAIT_OBJECT_0 ) {\r
10064             result = TerminateProcess( cp->hProcess, 0 );\r
10065 \r
10066             if ( appData.debugMode) {\r
10067                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10068             }\r
10069 \r
10070         }\r
10071     }\r
10072 \r
10073     CloseHandle(cp->hProcess);\r
10074     break;\r
10075 \r
10076   case CPComm:\r
10077     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10078     break;\r
10079 \r
10080   case CPSock:\r
10081     closesocket(cp->sock);\r
10082     WSACleanup();\r
10083     break;\r
10084 \r
10085   case CPRcmd:\r
10086     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10087     closesocket(cp->sock);\r
10088     closesocket(cp->sock2);\r
10089     WSACleanup();\r
10090     break;\r
10091   }\r
10092   free(cp);\r
10093 }\r
10094 \r
10095 void\r
10096 InterruptChildProcess(ProcRef pr)\r
10097 {\r
10098   ChildProc *cp;\r
10099 \r
10100   cp = (ChildProc *) pr;\r
10101   if (cp == NULL) return;\r
10102   switch (cp->kind) {\r
10103   case CPReal:\r
10104     /* The following doesn't work because the chess program\r
10105        doesn't "have the same console" as WinBoard.  Maybe\r
10106        we could arrange for this even though neither WinBoard\r
10107        nor the chess program uses a console for stdio */\r
10108     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10109     break;\r
10110 \r
10111   case CPComm:\r
10112   case CPSock:\r
10113     /* Can't interrupt */\r
10114     break;\r
10115 \r
10116   case CPRcmd:\r
10117     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10118     break;\r
10119   }\r
10120 }\r
10121 \r
10122 \r
10123 int\r
10124 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10125 {\r
10126   char cmdLine[MSG_SIZ];\r
10127 \r
10128   if (port[0] == NULLCHAR) {\r
10129     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10130   } else {\r
10131     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10132   }\r
10133   return StartChildProcess(cmdLine, "", pr);\r
10134 }\r
10135 \r
10136 \r
10137 /* Code to open TCP sockets */\r
10138 \r
10139 int\r
10140 OpenTCP(char *host, char *port, ProcRef *pr)\r
10141 {\r
10142   ChildProc *cp;\r
10143   int err;\r
10144   SOCKET s;\r
10145   struct sockaddr_in sa, mysa;\r
10146   struct hostent FAR *hp;\r
10147   unsigned short uport;\r
10148   WORD wVersionRequested;\r
10149   WSADATA wsaData;\r
10150 \r
10151   /* Initialize socket DLL */\r
10152   wVersionRequested = MAKEWORD(1, 1);\r
10153   err = WSAStartup(wVersionRequested, &wsaData);\r
10154   if (err != 0) return err;\r
10155 \r
10156   /* Make socket */\r
10157   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10158     err = WSAGetLastError();\r
10159     WSACleanup();\r
10160     return err;\r
10161   }\r
10162 \r
10163   /* Bind local address using (mostly) don't-care values.\r
10164    */\r
10165   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10166   mysa.sin_family = AF_INET;\r
10167   mysa.sin_addr.s_addr = INADDR_ANY;\r
10168   uport = (unsigned short) 0;\r
10169   mysa.sin_port = htons(uport);\r
10170   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10171       == SOCKET_ERROR) {\r
10172     err = WSAGetLastError();\r
10173     WSACleanup();\r
10174     return err;\r
10175   }\r
10176 \r
10177   /* Resolve remote host name */\r
10178   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10179   if (!(hp = gethostbyname(host))) {\r
10180     unsigned int b0, b1, b2, b3;\r
10181 \r
10182     err = WSAGetLastError();\r
10183 \r
10184     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10185       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10186       hp->h_addrtype = AF_INET;\r
10187       hp->h_length = 4;\r
10188       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10189       hp->h_addr_list[0] = (char *) malloc(4);\r
10190       hp->h_addr_list[0][0] = (char) b0;\r
10191       hp->h_addr_list[0][1] = (char) b1;\r
10192       hp->h_addr_list[0][2] = (char) b2;\r
10193       hp->h_addr_list[0][3] = (char) b3;\r
10194     } else {\r
10195       WSACleanup();\r
10196       return err;\r
10197     }\r
10198   }\r
10199   sa.sin_family = hp->h_addrtype;\r
10200   uport = (unsigned short) atoi(port);\r
10201   sa.sin_port = htons(uport);\r
10202   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10203 \r
10204   /* Make connection */\r
10205   if (connect(s, (struct sockaddr *) &sa,\r
10206               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10207     err = WSAGetLastError();\r
10208     WSACleanup();\r
10209     return err;\r
10210   }\r
10211 \r
10212   /* Prepare return value */\r
10213   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10214   cp->kind = CPSock;\r
10215   cp->sock = s;\r
10216   *pr = (ProcRef *) cp;\r
10217 \r
10218   return NO_ERROR;\r
10219 }\r
10220 \r
10221 int\r
10222 OpenCommPort(char *name, ProcRef *pr)\r
10223 {\r
10224   HANDLE h;\r
10225   COMMTIMEOUTS ct;\r
10226   ChildProc *cp;\r
10227   char fullname[MSG_SIZ];\r
10228 \r
10229   if (*name != '\\')\r
10230     sprintf(fullname, "\\\\.\\%s", name);\r
10231   else\r
10232     strcpy(fullname, name);\r
10233 \r
10234   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10235                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10236   if (h == (HANDLE) -1) {\r
10237     return GetLastError();\r
10238   }\r
10239   hCommPort = h;\r
10240 \r
10241   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10242 \r
10243   /* Accumulate characters until a 100ms pause, then parse */\r
10244   ct.ReadIntervalTimeout = 100;\r
10245   ct.ReadTotalTimeoutMultiplier = 0;\r
10246   ct.ReadTotalTimeoutConstant = 0;\r
10247   ct.WriteTotalTimeoutMultiplier = 0;\r
10248   ct.WriteTotalTimeoutConstant = 0;\r
10249   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10250 \r
10251   /* Prepare return value */\r
10252   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10253   cp->kind = CPComm;\r
10254   cp->hFrom = h;\r
10255   cp->hTo = h;\r
10256   *pr = (ProcRef *) cp;\r
10257 \r
10258   return NO_ERROR;\r
10259 }\r
10260 \r
10261 int\r
10262 OpenLoopback(ProcRef *pr)\r
10263 {\r
10264   DisplayFatalError("Not implemented", 0, 1);\r
10265   return NO_ERROR;\r
10266 }\r
10267 \r
10268 \r
10269 int\r
10270 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10271 {\r
10272   ChildProc *cp;\r
10273   int err;\r
10274   SOCKET s, s2, s3;\r
10275   struct sockaddr_in sa, mysa;\r
10276   struct hostent FAR *hp;\r
10277   unsigned short uport;\r
10278   WORD wVersionRequested;\r
10279   WSADATA wsaData;\r
10280   int fromPort;\r
10281   char stderrPortStr[MSG_SIZ];\r
10282 \r
10283   /* Initialize socket DLL */\r
10284   wVersionRequested = MAKEWORD(1, 1);\r
10285   err = WSAStartup(wVersionRequested, &wsaData);\r
10286   if (err != 0) return err;\r
10287 \r
10288   /* Resolve remote host name */\r
10289   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10290   if (!(hp = gethostbyname(host))) {\r
10291     unsigned int b0, b1, b2, b3;\r
10292 \r
10293     err = WSAGetLastError();\r
10294 \r
10295     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10296       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10297       hp->h_addrtype = AF_INET;\r
10298       hp->h_length = 4;\r
10299       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10300       hp->h_addr_list[0] = (char *) malloc(4);\r
10301       hp->h_addr_list[0][0] = (char) b0;\r
10302       hp->h_addr_list[0][1] = (char) b1;\r
10303       hp->h_addr_list[0][2] = (char) b2;\r
10304       hp->h_addr_list[0][3] = (char) b3;\r
10305     } else {\r
10306       WSACleanup();\r
10307       return err;\r
10308     }\r
10309   }\r
10310   sa.sin_family = hp->h_addrtype;\r
10311   uport = (unsigned short) 514;\r
10312   sa.sin_port = htons(uport);\r
10313   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10314 \r
10315   /* Bind local socket to unused "privileged" port address\r
10316    */\r
10317   s = INVALID_SOCKET;\r
10318   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10319   mysa.sin_family = AF_INET;\r
10320   mysa.sin_addr.s_addr = INADDR_ANY;\r
10321   for (fromPort = 1023;; fromPort--) {\r
10322     if (fromPort < 0) {\r
10323       WSACleanup();\r
10324       return WSAEADDRINUSE;\r
10325     }\r
10326     if (s == INVALID_SOCKET) {\r
10327       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10328         err = WSAGetLastError();\r
10329         WSACleanup();\r
10330         return err;\r
10331       }\r
10332     }\r
10333     uport = (unsigned short) fromPort;\r
10334     mysa.sin_port = htons(uport);\r
10335     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10336         == SOCKET_ERROR) {\r
10337       err = WSAGetLastError();\r
10338       if (err == WSAEADDRINUSE) continue;\r
10339       WSACleanup();\r
10340       return err;\r
10341     }\r
10342     if (connect(s, (struct sockaddr *) &sa,\r
10343       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10344       err = WSAGetLastError();\r
10345       if (err == WSAEADDRINUSE) {\r
10346         closesocket(s);\r
10347         s = -1;\r
10348         continue;\r
10349       }\r
10350       WSACleanup();\r
10351       return err;\r
10352     }\r
10353     break;\r
10354   }\r
10355 \r
10356   /* Bind stderr local socket to unused "privileged" port address\r
10357    */\r
10358   s2 = INVALID_SOCKET;\r
10359   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10360   mysa.sin_family = AF_INET;\r
10361   mysa.sin_addr.s_addr = INADDR_ANY;\r
10362   for (fromPort = 1023;; fromPort--) {\r
10363     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10364     if (fromPort < 0) {\r
10365       (void) closesocket(s);\r
10366       WSACleanup();\r
10367       return WSAEADDRINUSE;\r
10368     }\r
10369     if (s2 == INVALID_SOCKET) {\r
10370       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10371         err = WSAGetLastError();\r
10372         closesocket(s);\r
10373         WSACleanup();\r
10374         return err;\r
10375       }\r
10376     }\r
10377     uport = (unsigned short) fromPort;\r
10378     mysa.sin_port = htons(uport);\r
10379     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10380         == SOCKET_ERROR) {\r
10381       err = WSAGetLastError();\r
10382       if (err == WSAEADDRINUSE) continue;\r
10383       (void) closesocket(s);\r
10384       WSACleanup();\r
10385       return err;\r
10386     }\r
10387     if (listen(s2, 1) == SOCKET_ERROR) {\r
10388       err = WSAGetLastError();\r
10389       if (err == WSAEADDRINUSE) {\r
10390         closesocket(s2);\r
10391         s2 = INVALID_SOCKET;\r
10392         continue;\r
10393       }\r
10394       (void) closesocket(s);\r
10395       (void) closesocket(s2);\r
10396       WSACleanup();\r
10397       return err;\r
10398     }\r
10399     break;\r
10400   }\r
10401   prevStderrPort = fromPort; // remember port used\r
10402   sprintf(stderrPortStr, "%d", fromPort);\r
10403 \r
10404   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10405     err = WSAGetLastError();\r
10406     (void) closesocket(s);\r
10407     (void) closesocket(s2);\r
10408     WSACleanup();\r
10409     return err;\r
10410   }\r
10411 \r
10412   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10413     err = WSAGetLastError();\r
10414     (void) closesocket(s);\r
10415     (void) closesocket(s2);\r
10416     WSACleanup();\r
10417     return err;\r
10418   }\r
10419   if (*user == NULLCHAR) user = UserName();\r
10420   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10421     err = WSAGetLastError();\r
10422     (void) closesocket(s);\r
10423     (void) closesocket(s2);\r
10424     WSACleanup();\r
10425     return err;\r
10426   }\r
10427   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10428     err = WSAGetLastError();\r
10429     (void) closesocket(s);\r
10430     (void) closesocket(s2);\r
10431     WSACleanup();\r
10432     return err;\r
10433   }\r
10434 \r
10435   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10436     err = WSAGetLastError();\r
10437     (void) closesocket(s);\r
10438     (void) closesocket(s2);\r
10439     WSACleanup();\r
10440     return err;\r
10441   }\r
10442   (void) closesocket(s2);  /* Stop listening */\r
10443 \r
10444   /* Prepare return value */\r
10445   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10446   cp->kind = CPRcmd;\r
10447   cp->sock = s;\r
10448   cp->sock2 = s3;\r
10449   *pr = (ProcRef *) cp;\r
10450 \r
10451   return NO_ERROR;\r
10452 }\r
10453 \r
10454 \r
10455 InputSourceRef\r
10456 AddInputSource(ProcRef pr, int lineByLine,\r
10457                InputCallback func, VOIDSTAR closure)\r
10458 {\r
10459   InputSource *is, *is2 = NULL;\r
10460   ChildProc *cp = (ChildProc *) pr;\r
10461 \r
10462   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10463   is->lineByLine = lineByLine;\r
10464   is->func = func;\r
10465   is->closure = closure;\r
10466   is->second = NULL;\r
10467   is->next = is->buf;\r
10468   if (pr == NoProc) {\r
10469     is->kind = CPReal;\r
10470     consoleInputSource = is;\r
10471   } else {\r
10472     is->kind = cp->kind;\r
10473     /* \r
10474         [AS] Try to avoid a race condition if the thread is given control too early:\r
10475         we create all threads suspended so that the is->hThread variable can be\r
10476         safely assigned, then let the threads start with ResumeThread.\r
10477     */\r
10478     switch (cp->kind) {\r
10479     case CPReal:\r
10480       is->hFile = cp->hFrom;\r
10481       cp->hFrom = NULL; /* now owned by InputThread */\r
10482       is->hThread =\r
10483         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10484                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10485       break;\r
10486 \r
10487     case CPComm:\r
10488       is->hFile = cp->hFrom;\r
10489       cp->hFrom = NULL; /* now owned by InputThread */\r
10490       is->hThread =\r
10491         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10492                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10493       break;\r
10494 \r
10495     case CPSock:\r
10496       is->sock = cp->sock;\r
10497       is->hThread =\r
10498         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10499                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10500       break;\r
10501 \r
10502     case CPRcmd:\r
10503       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10504       *is2 = *is;\r
10505       is->sock = cp->sock;\r
10506       is->second = is2;\r
10507       is2->sock = cp->sock2;\r
10508       is2->second = is2;\r
10509       is->hThread =\r
10510         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10511                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10512       is2->hThread =\r
10513         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10514                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10515       break;\r
10516     }\r
10517 \r
10518     if( is->hThread != NULL ) {\r
10519         ResumeThread( is->hThread );\r
10520     }\r
10521 \r
10522     if( is2 != NULL && is2->hThread != NULL ) {\r
10523         ResumeThread( is2->hThread );\r
10524     }\r
10525   }\r
10526 \r
10527   return (InputSourceRef) is;\r
10528 }\r
10529 \r
10530 void\r
10531 RemoveInputSource(InputSourceRef isr)\r
10532 {\r
10533   InputSource *is;\r
10534 \r
10535   is = (InputSource *) isr;\r
10536   is->hThread = NULL;  /* tell thread to stop */\r
10537   CloseHandle(is->hThread);\r
10538   if (is->second != NULL) {\r
10539     is->second->hThread = NULL;\r
10540     CloseHandle(is->second->hThread);\r
10541   }\r
10542 }\r
10543 \r
10544 \r
10545 int\r
10546 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10547 {\r
10548   DWORD dOutCount;\r
10549   int outCount = SOCKET_ERROR;\r
10550   ChildProc *cp = (ChildProc *) pr;\r
10551   static OVERLAPPED ovl;\r
10552 \r
10553   if (pr == NoProc) {\r
10554     ConsoleOutput(message, count, FALSE);\r
10555     return count;\r
10556   } \r
10557 \r
10558   if (ovl.hEvent == NULL) {\r
10559     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10560   }\r
10561   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10562 \r
10563   switch (cp->kind) {\r
10564   case CPSock:\r
10565   case CPRcmd:\r
10566     outCount = send(cp->sock, message, count, 0);\r
10567     if (outCount == SOCKET_ERROR) {\r
10568       *outError = WSAGetLastError();\r
10569     } else {\r
10570       *outError = NO_ERROR;\r
10571     }\r
10572     break;\r
10573 \r
10574   case CPReal:\r
10575     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10576                   &dOutCount, NULL)) {\r
10577       *outError = NO_ERROR;\r
10578       outCount = (int) dOutCount;\r
10579     } else {\r
10580       *outError = GetLastError();\r
10581     }\r
10582     break;\r
10583 \r
10584   case CPComm:\r
10585     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10586                             &dOutCount, &ovl);\r
10587     if (*outError == NO_ERROR) {\r
10588       outCount = (int) dOutCount;\r
10589     }\r
10590     break;\r
10591   }\r
10592   return outCount;\r
10593 }\r
10594 \r
10595 int\r
10596 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10597                        long msdelay)\r
10598 {\r
10599   /* Ignore delay, not implemented for WinBoard */\r
10600   return OutputToProcess(pr, message, count, outError);\r
10601 }\r
10602 \r
10603 \r
10604 void\r
10605 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10606                         char *buf, int count, int error)\r
10607 {\r
10608   DisplayFatalError("Not implemented", 0, 1);\r
10609 }\r
10610 \r
10611 /* see wgamelist.c for Game List functions */\r
10612 /* see wedittags.c for Edit Tags functions */\r
10613 \r
10614 \r
10615 VOID\r
10616 ICSInitScript()\r
10617 {\r
10618   FILE *f;\r
10619   char buf[MSG_SIZ];\r
10620   char *dummy;\r
10621 \r
10622   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10623     f = fopen(buf, "r");\r
10624     if (f != NULL) {\r
10625       ProcessICSInitScript(f);\r
10626       fclose(f);\r
10627     }\r
10628   }\r
10629 }\r
10630 \r
10631 \r
10632 VOID\r
10633 StartAnalysisClock()\r
10634 {\r
10635   if (analysisTimerEvent) return;\r
10636   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10637                                         (UINT) 2000, NULL);\r
10638 }\r
10639 \r
10640 LRESULT CALLBACK\r
10641 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10642 {\r
10643   static HANDLE hwndText;\r
10644   RECT rect;\r
10645   static int sizeX, sizeY;\r
10646   int newSizeX, newSizeY, flags;\r
10647   MINMAXINFO *mmi;\r
10648 \r
10649   switch (message) {\r
10650   case WM_INITDIALOG: /* message: initialize dialog box */\r
10651     /* Initialize the dialog items */\r
10652     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10653     SetWindowText(hDlg, analysisTitle);\r
10654     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10655     /* Size and position the dialog */\r
10656     if (!analysisDialog) {\r
10657       analysisDialog = hDlg;\r
10658       flags = SWP_NOZORDER;\r
10659       GetClientRect(hDlg, &rect);\r
10660       sizeX = rect.right;\r
10661       sizeY = rect.bottom;\r
10662       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10663           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10664         WINDOWPLACEMENT wp;\r
10665         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10666         wp.length = sizeof(WINDOWPLACEMENT);\r
10667         wp.flags = 0;\r
10668         wp.showCmd = SW_SHOW;\r
10669         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10670         wp.rcNormalPosition.left = analysisX;\r
10671         wp.rcNormalPosition.right = analysisX + analysisW;\r
10672         wp.rcNormalPosition.top = analysisY;\r
10673         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10674         SetWindowPlacement(hDlg, &wp);\r
10675 \r
10676         GetClientRect(hDlg, &rect);\r
10677         newSizeX = rect.right;\r
10678         newSizeY = rect.bottom;\r
10679         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10680                               newSizeX, newSizeY);\r
10681         sizeX = newSizeX;\r
10682         sizeY = newSizeY;\r
10683       }\r
10684     }\r
10685     return FALSE;\r
10686 \r
10687   case WM_COMMAND: /* message: received a command */\r
10688     switch (LOWORD(wParam)) {\r
10689     case IDCANCEL:\r
10690       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10691           ExitAnalyzeMode();\r
10692           ModeHighlight();\r
10693           return TRUE;\r
10694       }\r
10695       EditGameEvent();\r
10696       return TRUE;\r
10697     default:\r
10698       break;\r
10699     }\r
10700     break;\r
10701 \r
10702   case WM_SIZE:\r
10703     newSizeX = LOWORD(lParam);\r
10704     newSizeY = HIWORD(lParam);\r
10705     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10706     sizeX = newSizeX;\r
10707     sizeY = newSizeY;\r
10708     break;\r
10709 \r
10710   case WM_GETMINMAXINFO:\r
10711     /* Prevent resizing window too small */\r
10712     mmi = (MINMAXINFO *) lParam;\r
10713     mmi->ptMinTrackSize.x = 100;\r
10714     mmi->ptMinTrackSize.y = 100;\r
10715     break;\r
10716   }\r
10717   return FALSE;\r
10718 }\r
10719 \r
10720 VOID\r
10721 AnalysisPopUp(char* title, char* str)\r
10722 {\r
10723   FARPROC lpProc;\r
10724   char *p, *q;\r
10725 \r
10726   /* [AS] */\r
10727   EngineOutputPopUp();\r
10728   return;\r
10729 \r
10730   if (str == NULL) str = "";\r
10731   p = (char *) malloc(2 * strlen(str) + 2);\r
10732   q = p;\r
10733   while (*str) {\r
10734     if (*str == '\n') *q++ = '\r';\r
10735     *q++ = *str++;\r
10736   }\r
10737   *q = NULLCHAR;\r
10738   if (analysisText != NULL) free(analysisText);\r
10739   analysisText = p;\r
10740 \r
10741   if (analysisDialog) {\r
10742     SetWindowText(analysisDialog, title);\r
10743     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10744     ShowWindow(analysisDialog, SW_SHOW);\r
10745   } else {\r
10746     analysisTitle = title;\r
10747     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10748     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10749                  hwndMain, (DLGPROC)lpProc);\r
10750     FreeProcInstance(lpProc);\r
10751   }\r
10752   analysisDialogUp = TRUE;  \r
10753 }\r
10754 \r
10755 VOID\r
10756 AnalysisPopDown()\r
10757 {\r
10758   if (analysisDialog) {\r
10759     ShowWindow(analysisDialog, SW_HIDE);\r
10760   }\r
10761   analysisDialogUp = FALSE;  \r
10762 }\r
10763 \r
10764 \r
10765 VOID\r
10766 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10767 {\r
10768   highlightInfo.sq[0].x = fromX;\r
10769   highlightInfo.sq[0].y = fromY;\r
10770   highlightInfo.sq[1].x = toX;\r
10771   highlightInfo.sq[1].y = toY;\r
10772 }\r
10773 \r
10774 VOID\r
10775 ClearHighlights()\r
10776 {\r
10777   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10778     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10779 }\r
10780 \r
10781 VOID\r
10782 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10783 {\r
10784   premoveHighlightInfo.sq[0].x = fromX;\r
10785   premoveHighlightInfo.sq[0].y = fromY;\r
10786   premoveHighlightInfo.sq[1].x = toX;\r
10787   premoveHighlightInfo.sq[1].y = toY;\r
10788 }\r
10789 \r
10790 VOID\r
10791 ClearPremoveHighlights()\r
10792 {\r
10793   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10794     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10795 }\r
10796 \r
10797 VOID\r
10798 ShutDownFrontEnd()\r
10799 {\r
10800   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10801   DeleteClipboardTempFiles();\r
10802 }\r
10803 \r
10804 void\r
10805 BoardToTop()\r
10806 {\r
10807     if (IsIconic(hwndMain))\r
10808       ShowWindow(hwndMain, SW_RESTORE);\r
10809 \r
10810     SetActiveWindow(hwndMain);\r
10811 }\r
10812 \r
10813 /*\r
10814  * Prototypes for animation support routines\r
10815  */\r
10816 static void ScreenSquare(int column, int row, POINT * pt);\r
10817 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10818      POINT frames[], int * nFrames);\r
10819 \r
10820 \r
10821 void\r
10822 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10823 {       // [HGM] atomic: animate blast wave\r
10824         int i;\r
10825 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10826         explodeInfo.fromX = fromX;\r
10827         explodeInfo.fromY = fromY;\r
10828         explodeInfo.toX = toX;\r
10829         explodeInfo.toY = toY;\r
10830         for(i=1; i<nFrames; i++) {\r
10831             explodeInfo.radius = (i*180)/(nFrames-1);\r
10832             DrawPosition(FALSE, NULL);\r
10833             Sleep(appData.animSpeed);\r
10834         }\r
10835         explodeInfo.radius = 0;\r
10836         DrawPosition(TRUE, NULL);\r
10837 }\r
10838 \r
10839 #define kFactor 4\r
10840 \r
10841 void\r
10842 AnimateMove(board, fromX, fromY, toX, toY)\r
10843      Board board;\r
10844      int fromX;\r
10845      int fromY;\r
10846      int toX;\r
10847      int toY;\r
10848 {\r
10849   ChessSquare piece;\r
10850   POINT start, finish, mid;\r
10851   POINT frames[kFactor * 2 + 1];\r
10852   int nFrames, n;\r
10853 \r
10854   if (!appData.animate) return;\r
10855   if (doingSizing) return;\r
10856   if (fromY < 0 || fromX < 0) return;\r
10857   piece = board[fromY][fromX];\r
10858   if (piece >= EmptySquare) return;\r
10859 \r
10860   ScreenSquare(fromX, fromY, &start);\r
10861   ScreenSquare(toX, toY, &finish);\r
10862 \r
10863   /* All pieces except knights move in straight line */\r
10864   if (piece != WhiteKnight && piece != BlackKnight) {\r
10865     mid.x = start.x + (finish.x - start.x) / 2;\r
10866     mid.y = start.y + (finish.y - start.y) / 2;\r
10867   } else {\r
10868     /* Knight: make diagonal movement then straight */\r
10869     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10870        mid.x = start.x + (finish.x - start.x) / 2;\r
10871        mid.y = finish.y;\r
10872      } else {\r
10873        mid.x = finish.x;\r
10874        mid.y = start.y + (finish.y - start.y) / 2;\r
10875      }\r
10876   }\r
10877   \r
10878   /* Don't use as many frames for very short moves */\r
10879   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10880     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10881   else\r
10882     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10883 \r
10884   animInfo.from.x = fromX;\r
10885   animInfo.from.y = fromY;\r
10886   animInfo.to.x = toX;\r
10887   animInfo.to.y = toY;\r
10888   animInfo.lastpos = start;\r
10889   animInfo.piece = piece;\r
10890   for (n = 0; n < nFrames; n++) {\r
10891     animInfo.pos = frames[n];\r
10892     DrawPosition(FALSE, NULL);\r
10893     animInfo.lastpos = animInfo.pos;\r
10894     Sleep(appData.animSpeed);\r
10895   }\r
10896   animInfo.pos = finish;\r
10897   DrawPosition(FALSE, NULL);\r
10898   animInfo.piece = EmptySquare;\r
10899   if(gameInfo.variant == VariantAtomic && \r
10900      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10901         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10902 }\r
10903 \r
10904 /*      Convert board position to corner of screen rect and color       */\r
10905 \r
10906 static void\r
10907 ScreenSquare(column, row, pt)\r
10908      int column; int row; POINT * pt;\r
10909 {\r
10910   if (flipView) {\r
10911     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10912     pt->y = lineGap + row * (squareSize + lineGap);\r
10913   } else {\r
10914     pt->x = lineGap + column * (squareSize + lineGap);\r
10915     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10916   }\r
10917 }\r
10918 \r
10919 /*      Generate a series of frame coords from start->mid->finish.\r
10920         The movement rate doubles until the half way point is\r
10921         reached, then halves back down to the final destination,\r
10922         which gives a nice slow in/out effect. The algorithmn\r
10923         may seem to generate too many intermediates for short\r
10924         moves, but remember that the purpose is to attract the\r
10925         viewers attention to the piece about to be moved and\r
10926         then to where it ends up. Too few frames would be less\r
10927         noticeable.                                             */\r
10928 \r
10929 static void\r
10930 Tween(start, mid, finish, factor, frames, nFrames)\r
10931      POINT * start; POINT * mid;\r
10932      POINT * finish; int factor;\r
10933      POINT frames[]; int * nFrames;\r
10934 {\r
10935   int n, fraction = 1, count = 0;\r
10936 \r
10937   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10938   for (n = 0; n < factor; n++)\r
10939     fraction *= 2;\r
10940   for (n = 0; n < factor; n++) {\r
10941     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10942     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10943     count ++;\r
10944     fraction = fraction / 2;\r
10945   }\r
10946   \r
10947   /* Midpoint */\r
10948   frames[count] = *mid;\r
10949   count ++;\r
10950   \r
10951   /* Slow out, stepping 1/2, then 1/4, ... */\r
10952   fraction = 2;\r
10953   for (n = 0; n < factor; n++) {\r
10954     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10955     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10956     count ++;\r
10957     fraction = fraction * 2;\r
10958   }\r
10959   *nFrames = count;\r
10960 }\r
10961 \r
10962 void\r
10963 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10964 {\r
10965 #if 0\r
10966     char buf[256];\r
10967 \r
10968     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10969         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10970 \r
10971     OutputDebugString( buf );\r
10972 #endif\r
10973 \r
10974     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10975 \r
10976     EvalGraphSet( first, last, current, pvInfoList );\r
10977 }\r
10978 \r
10979 void SetProgramStats( FrontEndProgramStats * stats )\r
10980 {\r
10981 #if 0\r
10982     char buf[1024];\r
10983 \r
10984     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10985         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10986 \r
10987     OutputDebugString( buf );\r
10988 #endif\r
10989 \r
10990     EngineOutputUpdate( stats );\r
10991 }\r