fixed a few warnings in connection with the help patch (now warning free)
[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 #ifdef VISTA\r
110 #include "htmlhelp.h"\r
111 #else\r
112 HWND WINAPI HtmlHelp( HWND hwnd, LPCSTR helpFile, UINT action, DWORD_PTR data );\r
113 #endif\r
114 \r
115 typedef struct {\r
116   ChessSquare piece;  \r
117   POINT pos;      /* window coordinates of current pos */\r
118   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
119   POINT from;     /* board coordinates of the piece's orig pos */\r
120   POINT to;       /* board coordinates of the piece's new pos */\r
121 } AnimInfo;\r
122 \r
123 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
124 \r
125 typedef struct {\r
126   POINT start;    /* window coordinates of start pos */\r
127   POINT pos;      /* window coordinates of current pos */\r
128   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
129   POINT from;     /* board coordinates of the piece's orig pos */\r
130 } DragInfo;\r
131 \r
132 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
133 \r
134 typedef struct {\r
135   POINT sq[2];    /* board coordinates of from, to squares */\r
136 } HighlightInfo;\r
137 \r
138 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
139 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
140 \r
141 typedef struct { // [HGM] atomic\r
142   int fromX, fromY, toX, toY, radius;\r
143 } ExplodeInfo;\r
144 \r
145 static ExplodeInfo explodeInfo;\r
146 \r
147 /* Window class names */\r
148 char szAppName[] = "WinBoard";\r
149 char szConsoleName[] = "WBConsole";\r
150 \r
151 /* Title bar text */\r
152 char szTitle[] = "WinBoard";\r
153 char szConsoleTitle[] = "I C S Interaction";\r
154 \r
155 char *programName;\r
156 char *settingsFileName;\r
157 BOOLEAN saveSettingsOnExit;\r
158 char installDir[MSG_SIZ];\r
159 \r
160 BoardSize boardSize;\r
161 BOOLEAN chessProgram;\r
162 static int boardX, boardY;\r
163 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
164 static int squareSize, lineGap, minorSize;\r
165 static int winWidth, winHeight, winW, winH;\r
166 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
167 static int logoHeight = 0;\r
168 static char messageText[MESSAGE_TEXT_MAX];\r
169 static int clockTimerEvent = 0;\r
170 static int loadGameTimerEvent = 0;\r
171 static int analysisTimerEvent = 0;\r
172 static DelayedEventCallback delayedTimerCallback;\r
173 static int delayedTimerEvent = 0;\r
174 static int buttonCount = 2;\r
175 char *icsTextMenuString;\r
176 char *icsNames;\r
177 char *firstChessProgramNames;\r
178 char *secondChessProgramNames;\r
179 \r
180 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
181 \r
182 #define PALETTESIZE 256\r
183 \r
184 HINSTANCE hInst;          /* current instance */\r
185 HWND hwndMain = NULL;        /* root window*/\r
186 HWND hwndConsole = NULL;\r
187 BOOLEAN alwaysOnTop = FALSE;\r
188 RECT boardRect;\r
189 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
190   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
191 HPALETTE hPal;\r
192 ColorClass currentColorClass;\r
193 \r
194 HWND hCommPort = NULL;    /* currently open comm port */\r
195 static HWND hwndPause;    /* pause button */\r
196 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
197 static HBRUSH lightSquareBrush, darkSquareBrush,\r
198   blackSquareBrush, /* [HGM] for band between board and holdings */\r
199   explodeBrush,     /* [HGM] atomic */\r
200   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
201 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
202 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
203 static HPEN gridPen = NULL;\r
204 static HPEN highlightPen = NULL;\r
205 static HPEN premovePen = NULL;\r
206 static NPLOGPALETTE pLogPal;\r
207 static BOOL paletteChanged = FALSE;\r
208 static HICON iconWhite, iconBlack, iconCurrent;\r
209 static int doingSizing = FALSE;\r
210 static int lastSizing = 0;\r
211 static int prevStderrPort;\r
212 static HBITMAP userLogo;\r
213 \r
214 /* [AS] Support for background textures */\r
215 #define BACK_TEXTURE_MODE_DISABLED      0\r
216 #define BACK_TEXTURE_MODE_PLAIN         1\r
217 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
218 \r
219 static HBITMAP liteBackTexture = NULL;\r
220 static HBITMAP darkBackTexture = NULL;\r
221 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
223 static int backTextureSquareSize = 0;\r
224 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
225 \r
226 #if __GNUC__ && !defined(_winmajor)\r
227 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
228 #else\r
229 #define oldDialog (_winmajor < 4)\r
230 #endif\r
231 \r
232 char *defaultTextAttribs[] = \r
233 {\r
234   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
235   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
236   COLOR_NONE\r
237 };\r
238 \r
239 typedef struct {\r
240   char *name;\r
241   int squareSize;\r
242   int lineGap;\r
243   int smallLayout;\r
244   int tinyLayout;\r
245   int cliWidth, cliHeight;\r
246 } SizeInfo;\r
247 \r
248 SizeInfo sizeInfo[] = \r
249 {\r
250   { "tiny",     21, 0, 1, 1, 0, 0 },\r
251   { "teeny",    25, 1, 1, 1, 0, 0 },\r
252   { "dinky",    29, 1, 1, 1, 0, 0 },\r
253   { "petite",   33, 1, 1, 1, 0, 0 },\r
254   { "slim",     37, 2, 1, 0, 0, 0 },\r
255   { "small",    40, 2, 1, 0, 0, 0 },\r
256   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
257   { "middling", 49, 2, 0, 0, 0, 0 },\r
258   { "average",  54, 2, 0, 0, 0, 0 },\r
259   { "moderate", 58, 3, 0, 0, 0, 0 },\r
260   { "medium",   64, 3, 0, 0, 0, 0 },\r
261   { "bulky",    72, 3, 0, 0, 0, 0 },\r
262   { "large",    80, 3, 0, 0, 0, 0 },\r
263   { "big",      87, 3, 0, 0, 0, 0 },\r
264   { "huge",     95, 3, 0, 0, 0, 0 },\r
265   { "giant",    108, 3, 0, 0, 0, 0 },\r
266   { "colossal", 116, 4, 0, 0, 0, 0 },\r
267   { "titanic",  129, 4, 0, 0, 0, 0 },\r
268   { NULL, 0, 0, 0, 0, 0, 0 }\r
269 };\r
270 \r
271 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
272 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
273 {\r
274   { 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
275   { 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
276   { 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
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283   { 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
284   { 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
285   { 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
286   { 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
287   { 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
288   { 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
289   { 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
290   { 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
291   { 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
292 };\r
293 \r
294 MyFont *font[NUM_SIZES][NUM_FONTS];\r
295 \r
296 typedef struct {\r
297   char *label;\r
298   int id;\r
299   HWND hwnd;\r
300   WNDPROC wndproc;\r
301 } MyButtonDesc;\r
302 \r
303 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
304 #define N_BUTTONS 5\r
305 \r
306 MyButtonDesc buttonDesc[N_BUTTONS] =\r
307 {\r
308   {"<<", IDM_ToStart, NULL, NULL},\r
309   {"<", IDM_Backward, NULL, NULL},\r
310   {"P", IDM_Pause, NULL, NULL},\r
311   {">", IDM_Forward, NULL, NULL},\r
312   {">>", IDM_ToEnd, NULL, NULL},\r
313 };\r
314 \r
315 int tinyLayout = 0, smallLayout = 0;\r
316 #define MENU_BAR_ITEMS 6\r
317 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
318   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
319   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
320 };\r
321 \r
322 \r
323 MySound sounds[(int)NSoundClasses];\r
324 MyTextAttribs textAttribs[(int)NColorClasses];\r
325 \r
326 MyColorizeAttribs colorizeAttribs[] = {\r
327   { (COLORREF)0, 0, "Shout Text" },\r
328   { (COLORREF)0, 0, "SShout/CShout" },\r
329   { (COLORREF)0, 0, "Channel 1 Text" },\r
330   { (COLORREF)0, 0, "Channel Text" },\r
331   { (COLORREF)0, 0, "Kibitz Text" },\r
332   { (COLORREF)0, 0, "Tell Text" },\r
333   { (COLORREF)0, 0, "Challenge Text" },\r
334   { (COLORREF)0, 0, "Request Text" },\r
335   { (COLORREF)0, 0, "Seek Text" },\r
336   { (COLORREF)0, 0, "Normal Text" },\r
337   { (COLORREF)0, 0, "None" }\r
338 };\r
339 \r
340 \r
341 \r
342 static char *commentTitle;\r
343 static char *commentText;\r
344 static int commentIndex;\r
345 static Boolean editComment = FALSE;\r
346 HWND commentDialog = NULL;\r
347 BOOLEAN commentDialogUp = FALSE;\r
348 static int commentX, commentY, commentH, commentW;\r
349 \r
350 static char *analysisTitle;\r
351 static char *analysisText;\r
352 HWND analysisDialog = NULL;\r
353 BOOLEAN analysisDialogUp = FALSE;\r
354 static int analysisX, analysisY, analysisH, analysisW;\r
355 \r
356 char errorTitle[MSG_SIZ];\r
357 char errorMessage[2*MSG_SIZ];\r
358 HWND errorDialog = NULL;\r
359 BOOLEAN moveErrorMessageUp = FALSE;\r
360 BOOLEAN consoleEcho = TRUE;\r
361 CHARFORMAT consoleCF;\r
362 COLORREF consoleBackgroundColor;\r
363 \r
364 char *programVersion;\r
365 \r
366 #define CPReal 1\r
367 #define CPComm 2\r
368 #define CPSock 3\r
369 #define CPRcmd 4\r
370 typedef int CPKind;\r
371 \r
372 typedef struct {\r
373   CPKind kind;\r
374   HANDLE hProcess;\r
375   DWORD pid;\r
376   HANDLE hTo;\r
377   HANDLE hFrom;\r
378   SOCKET sock;\r
379   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
380 } ChildProc;\r
381 \r
382 #define INPUT_SOURCE_BUF_SIZE 4096\r
383 \r
384 typedef struct _InputSource {\r
385   CPKind kind;\r
386   HANDLE hFile;\r
387   SOCKET sock;\r
388   int lineByLine;\r
389   HANDLE hThread;\r
390   DWORD id;\r
391   char buf[INPUT_SOURCE_BUF_SIZE];\r
392   char *next;\r
393   DWORD count;\r
394   int error;\r
395   InputCallback func;\r
396   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
397   VOIDSTAR closure;\r
398 } InputSource;\r
399 \r
400 InputSource *consoleInputSource;\r
401 \r
402 DCB dcb;\r
403 \r
404 /* forward */\r
405 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
406 VOID ConsoleCreate();\r
407 LRESULT CALLBACK\r
408   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
409 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
410 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
411 VOID ParseCommSettings(char *arg, DCB *dcb);\r
412 LRESULT CALLBACK\r
413   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
414 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
415 void ParseIcsTextMenu(char *icsTextMenuString);\r
416 VOID PopUpMoveDialog(char firstchar);\r
417 VOID PopUpNameDialog(char firstchar);\r
418 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
419 \r
420 /* [AS] */\r
421 int NewGameFRC();\r
422 int GameListOptions();\r
423 \r
424 HWND moveHistoryDialog = NULL;\r
425 BOOLEAN moveHistoryDialogUp = FALSE;\r
426 \r
427 WindowPlacement wpMoveHistory;\r
428 \r
429 HWND evalGraphDialog = NULL;\r
430 BOOLEAN evalGraphDialogUp = FALSE;\r
431 \r
432 WindowPlacement wpEvalGraph;\r
433 \r
434 HWND engineOutputDialog = NULL;\r
435 BOOLEAN engineOutputDialogUp = FALSE;\r
436 \r
437 WindowPlacement wpEngineOutput;\r
438 WindowPlacement wpGameList;\r
439 WindowPlacement wpConsole;\r
440 \r
441 VOID MoveHistoryPopUp();\r
442 VOID MoveHistoryPopDown();\r
443 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
444 BOOL MoveHistoryIsUp();\r
445 \r
446 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
447 VOID EvalGraphPopUp();\r
448 VOID EvalGraphPopDown();\r
449 BOOL EvalGraphIsUp();\r
450 \r
451 VOID EngineOutputPopUp();\r
452 VOID EngineOutputPopDown();\r
453 BOOL EngineOutputIsUp();\r
454 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
455 \r
456 VOID GothicPopUp(char *title, VariantClass variant);\r
457 /*\r
458  * Setting "frozen" should disable all user input other than deleting\r
459  * the window.  We do this while engines are initializing themselves.\r
460  */\r
461 static int frozen = 0;\r
462 static int oldMenuItemState[MENU_BAR_ITEMS];\r
463 void FreezeUI()\r
464 {\r
465   HMENU hmenu;\r
466   int i;\r
467 \r
468   if (frozen) return;\r
469   frozen = 1;\r
470   hmenu = GetMenu(hwndMain);\r
471   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
472     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
473   }\r
474   DrawMenuBar(hwndMain);\r
475 }\r
476 \r
477 /* Undo a FreezeUI */\r
478 void ThawUI()\r
479 {\r
480   HMENU hmenu;\r
481   int i;\r
482 \r
483   if (!frozen) return;\r
484   frozen = 0;\r
485   hmenu = GetMenu(hwndMain);\r
486   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
487     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
488   }\r
489   DrawMenuBar(hwndMain);\r
490 }\r
491 \r
492 static int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
493 \r
494 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
495 #ifdef JAWS\r
496 #include "jaws.c"\r
497 #else\r
498 #define JAWS_INIT\r
499 #define JAWS_ALT_INTERCEPT\r
500 #define JAWS_KB_NAVIGATION\r
501 #define JAWS_MENU_ITEMS\r
502 #define JAWS_SILENCE\r
503 #define JAWS_REPLAY\r
504 #define JAWS_DELETE(X) X\r
505 #define SAYMACHINEMOVE()\r
506 #define SAY(X)\r
507 #endif\r
508 \r
509 /*---------------------------------------------------------------------------*\\r
510  *\r
511  * WinMain\r
512  *\r
513 \*---------------------------------------------------------------------------*/\r
514 \r
515 int APIENTRY\r
516 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
517         LPSTR lpCmdLine, int nCmdShow)\r
518 {\r
519   MSG msg;\r
520   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
521 //  INITCOMMONCONTROLSEX ex;\r
522 \r
523   debugFP = stderr;\r
524 \r
525   LoadLibrary("RICHED32.DLL");\r
526   consoleCF.cbSize = sizeof(CHARFORMAT);\r
527 \r
528   if (!InitApplication(hInstance)) {\r
529     return (FALSE);\r
530   }\r
531   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
532     return (FALSE);\r
533   }\r
534 \r
535   JAWS_INIT\r
536 \r
537 //  InitCommonControlsEx(&ex);\r
538   InitCommonControls();\r
539 \r
540   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
541   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
542   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
543 \r
544   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
545 \r
546   while (GetMessage(&msg, /* message structure */\r
547                     NULL, /* handle of window receiving the message */\r
548                     0,    /* lowest message to examine */\r
549                     0))   /* highest message to examine */\r
550     {\r
551 \r
552       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
553         // [HGM] navigate: switch between all windows with tab\r
554         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
555         int i, currentElement = 0;\r
556 \r
557         // first determine what element of the chain we come from (if any)\r
558         if(appData.icsActive) {\r
559             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
560             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
561         }\r
562         if(engineOutputDialog && EngineOutputIsUp()) {\r
563             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
564             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
565         }\r
566         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
567             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
568         }\r
569         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
570         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
571         if(msg.hwnd == e1)                 currentElement = 2; else\r
572         if(msg.hwnd == e2)                 currentElement = 3; else\r
573         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
574         if(msg.hwnd == mh)                currentElement = 4; else\r
575         if(msg.hwnd == evalGraphDialog)    currentElement = 7; else\r
576         if(msg.hwnd == hText)  currentElement = 5; else\r
577         if(msg.hwnd == hInput) currentElement = 6; else\r
578         for (i = 0; i < N_BUTTONS; i++) {\r
579             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
580         }\r
581 \r
582         // determine where to go to\r
583         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
584           do {\r
585             currentElement = (currentElement + direction) % 7;\r
586             switch(currentElement) {\r
587                 case 0:\r
588                   h = hwndMain; break; // passing this case always makes the loop exit\r
589                 case 1:\r
590                   h = buttonDesc[0].hwnd; break; // could be NULL\r
591                 case 2:\r
592                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
593                   h = e1; break;\r
594                 case 3:\r
595                   if(!EngineOutputIsUp()) continue;\r
596                   h = e2; break;\r
597                 case 4:\r
598                   if(!MoveHistoryIsUp()) continue;\r
599                   h = mh; break;\r
600 //              case 5: // input to eval graph does not seem to get here!\r
601 //                if(!EvalGraphIsUp()) continue;\r
602 //                h = evalGraphDialog; break;\r
603                 case 5:\r
604                   if(!appData.icsActive) continue;\r
605                   SAY("display");\r
606                   h = hText; break;\r
607                 case 6:\r
608                   if(!appData.icsActive) continue;\r
609                   SAY("input");\r
610                   h = hInput; break;\r
611             }\r
612           } while(h == 0);\r
613 \r
614           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
615           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
616           SetFocus(h);\r
617 \r
618           continue; // this message now has been processed\r
619         }\r
620       }\r
621 \r
622       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
623           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
624           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
625           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
626           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
627           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
628           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
629           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
630           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
631           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
632         TranslateMessage(&msg); /* Translates virtual key codes */\r
633         DispatchMessage(&msg);  /* Dispatches message to window */\r
634       }\r
635     }\r
636 \r
637 \r
638   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
639 }\r
640 \r
641 /*---------------------------------------------------------------------------*\\r
642  *\r
643  * Initialization functions\r
644  *\r
645 \*---------------------------------------------------------------------------*/\r
646 \r
647 void\r
648 SetUserLogo()\r
649 {   // update user logo if necessary\r
650     static char oldUserName[MSG_SIZ], *curName;\r
651 \r
652     if(appData.autoLogo) {\r
653           curName = UserName();\r
654           if(strcmp(curName, oldUserName)) {\r
655                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
656                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
657                 strcpy(oldUserName, curName);\r
658           }\r
659     }\r
660 }\r
661 \r
662 BOOL\r
663 InitApplication(HINSTANCE hInstance)\r
664 {\r
665   WNDCLASS wc;\r
666 \r
667   /* Fill in window class structure with parameters that describe the */\r
668   /* main window. */\r
669 \r
670   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
671   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
672   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
673   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
674   wc.hInstance     = hInstance;         /* Owner of this class */\r
675   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
676   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
677   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
678   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
679   wc.lpszClassName = szAppName;                 /* Name to register as */\r
680 \r
681   /* Register the window class and return success/failure code. */\r
682   if (!RegisterClass(&wc)) return FALSE;\r
683 \r
684   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
685   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
686   wc.cbClsExtra    = 0;\r
687   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
688   wc.hInstance     = hInstance;\r
689   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
690   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
691   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
692   wc.lpszMenuName  = NULL;\r
693   wc.lpszClassName = szConsoleName;\r
694 \r
695   if (!RegisterClass(&wc)) return FALSE;\r
696   return TRUE;\r
697 }\r
698 \r
699 \r
700 /* Set by InitInstance, used by EnsureOnScreen */\r
701 int screenHeight, screenWidth;\r
702 \r
703 void\r
704 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
705 {\r
706 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
707   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
708   if (*x > screenWidth - 32) *x = 0;\r
709   if (*y > screenHeight - 32) *y = 0;\r
710   if (*x < minX) *x = minX;\r
711   if (*y < minY) *y = minY;\r
712 }\r
713 \r
714 BOOL\r
715 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
716 {\r
717   HWND hwnd; /* Main window handle. */\r
718   int ibs;\r
719   WINDOWPLACEMENT wp;\r
720   char *filepart;\r
721 \r
722   hInst = hInstance;    /* Store instance handle in our global variable */\r
723 \r
724   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
725     *filepart = NULLCHAR;\r
726   } else {\r
727     GetCurrentDirectory(MSG_SIZ, installDir);\r
728   }\r
729   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
730   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
731   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
732   if (appData.debugMode) {\r
733     debugFP = fopen(appData.nameOfDebugFile, "w");\r
734     setbuf(debugFP, NULL);\r
735   }\r
736 \r
737   InitBackEnd1();\r
738 \r
739 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
740 //  InitEngineUCI( installDir, &second );\r
741 \r
742   /* Create a main window for this application instance. */\r
743   hwnd = CreateWindow(szAppName, szTitle,\r
744                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
745                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
746                       NULL, NULL, hInstance, NULL);\r
747   hwndMain = hwnd;\r
748 \r
749   /* If window could not be created, return "failure" */\r
750   if (!hwnd) {\r
751     return (FALSE);\r
752   }\r
753 \r
754   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
755   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
756       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
757 \r
758       if (first.programLogo == NULL && appData.debugMode) {\r
759           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
760       }\r
761   } else if(appData.autoLogo) {\r
762       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
763         char buf[MSG_SIZ];\r
764         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
765         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
766       }\r
767   }\r
768 \r
769   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
770       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
771 \r
772       if (second.programLogo == NULL && appData.debugMode) {\r
773           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
774       }\r
775   } else if(appData.autoLogo) {\r
776       char buf[MSG_SIZ];\r
777       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
778         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
779         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
780       } else\r
781       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
782         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
783         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
784       }\r
785   }\r
786 \r
787   SetUserLogo();\r
788 \r
789   iconWhite = LoadIcon(hInstance, "icon_white");\r
790   iconBlack = LoadIcon(hInstance, "icon_black");\r
791   iconCurrent = iconWhite;\r
792   InitDrawingColors();\r
793   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
794   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
795   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
796     /* Compute window size for each board size, and use the largest\r
797        size that fits on this screen as the default. */\r
798     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
799     if (boardSize == (BoardSize)-1 &&\r
800         winH <= screenHeight\r
801            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
802         && winW <= screenWidth) {\r
803       boardSize = (BoardSize)ibs;\r
804     }\r
805   }\r
806 \r
807   InitDrawingSizes(boardSize, 0);\r
808   InitMenuChecks();\r
809   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
810 \r
811   /* [AS] Load textures if specified */\r
812   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
813   \r
814   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
815       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
816       liteBackTextureMode = appData.liteBackTextureMode;\r
817 \r
818       if (liteBackTexture == NULL && appData.debugMode) {\r
819           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
820       }\r
821   }\r
822   \r
823   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
824       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
825       darkBackTextureMode = appData.darkBackTextureMode;\r
826 \r
827       if (darkBackTexture == NULL && appData.debugMode) {\r
828           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
829       }\r
830   }\r
831 \r
832   mysrandom( (unsigned) time(NULL) );\r
833 \r
834   /* [AS] Restore layout */\r
835   if( wpMoveHistory.visible ) {\r
836       MoveHistoryPopUp();\r
837   }\r
838 \r
839   if( wpEvalGraph.visible ) {\r
840       EvalGraphPopUp();\r
841   }\r
842 \r
843   if( wpEngineOutput.visible ) {\r
844       EngineOutputPopUp();\r
845   }\r
846 \r
847   InitBackEnd2();\r
848 \r
849   /* Make the window visible; update its client area; and return "success" */\r
850   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
851   wp.length = sizeof(WINDOWPLACEMENT);\r
852   wp.flags = 0;\r
853   wp.showCmd = nCmdShow;\r
854   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
855   wp.rcNormalPosition.left = boardX;\r
856   wp.rcNormalPosition.right = boardX + winWidth;\r
857   wp.rcNormalPosition.top = boardY;\r
858   wp.rcNormalPosition.bottom = boardY + winHeight;\r
859   SetWindowPlacement(hwndMain, &wp);\r
860 \r
861   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
862                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
863 \r
864   if (hwndConsole) {\r
865 #if AOT_CONSOLE\r
866     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
867                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
868 #endif\r
869     ShowWindow(hwndConsole, nCmdShow);\r
870   }\r
871   UpdateWindow(hwnd);\r
872 \r
873   return TRUE;\r
874 \r
875 }\r
876 \r
877 \r
878 typedef enum {\r
879   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
880   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
881   ArgSettingsFilename,\r
882   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
883 } ArgType;\r
884 \r
885 typedef struct {\r
886   char *argName;\r
887   ArgType argType;\r
888   /***\r
889   union {\r
890     String *pString;       // ArgString\r
891     int *pInt;             // ArgInt\r
892     float *pFloat;         // ArgFloat\r
893     Boolean *pBoolean;     // ArgBoolean\r
894     COLORREF *pColor;      // ArgColor\r
895     ColorClass cc;         // ArgAttribs\r
896     String *pFilename;     // ArgFilename\r
897     BoardSize *pBoardSize; // ArgBoardSize\r
898     int whichFont;         // ArgFont\r
899     DCB *pDCB;             // ArgCommSettings\r
900     String *pFilename;     // ArgSettingsFilename\r
901   } argLoc;\r
902   ***/\r
903   LPVOID argLoc;\r
904   BOOL save;\r
905 } ArgDescriptor;\r
906 \r
907 int junk;\r
908 ArgDescriptor argDescriptors[] = {\r
909   /* positional arguments */\r
910   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
911   { "", ArgNone, NULL },\r
912   /* keyword arguments */\r
913   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
914   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
915   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
916   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
917   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
918   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
919   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
920   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
921   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
922   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
923   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
924   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
925   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
926   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
927   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
928   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
929   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
930   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
931     FALSE },\r
932   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
933     FALSE },\r
934   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
935     FALSE },\r
936   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
937   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
938     FALSE },\r
939   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
940   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
941   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
942   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
943   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
944   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
945   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
946   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
947   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
948   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
949   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
950   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
951   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
952   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
953   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
954   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
955   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
956   /*!!bitmapDirectory?*/\r
957   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
958   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
959   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
960   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
961   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
962   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
963   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
964   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
965   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
966   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
967   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
968   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
969   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
970   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
971   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
972   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
973   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
974   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
975   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
976   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
977   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
978   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
979   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
980   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
981   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
982   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
983   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
984   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
985   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
986   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
987   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
988   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
989   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
990   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
991   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
992   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
993   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
994   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
995   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
996   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
997   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
998   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
999   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1000   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1001   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1002   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1003   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1004   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
1005   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1006   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1007   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1008   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1009   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1010   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1011   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1012   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1013   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1014   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1015   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1016   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1017   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1018   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1019   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1020   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1021   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1022   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1023   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1024   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1025   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1026   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1027   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1028   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1029   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1030   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1031   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1032   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1033   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1034   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1035   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1036   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1037   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1038   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1039   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1040   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1041   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1042   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1043   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1044   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1045   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1046   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1047   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1048   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1049   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1050   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1051     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1052   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1053   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1054   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1055   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1056   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1057   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1058   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1059   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1060     TRUE }, /* must come after all fonts */\r
1061   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1062   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1063     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1064   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1065   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1066   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1067   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1068   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1069   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1070   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1071   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1072   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1073   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1074   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1075   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1076   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1077   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1078   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1079   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1080   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1081   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1082   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1083   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1084   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1085   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1086   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1087   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1088   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1089   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1090   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1091   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1092 #if 0\r
1093   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1094   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1095 #endif\r
1096   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1097   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1098   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1099   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1100   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1101   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1102   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1103   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1104   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1105   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1106   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1107   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1108   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1109   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1110   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1111   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1112   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1113   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1114   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1115   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1116   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1117   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1118   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1119   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1120   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1121   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1122   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1123   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1124   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1125   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1126   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1127   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1128   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1129   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1130   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1131   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1132   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1133   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1134   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1135   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1136   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1137   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1138   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1139   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1140   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1141   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1142   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1143   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1144   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1145   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1146   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1147   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1148   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1149   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1150   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1151   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1152   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1153   { "highlightLastMove", ArgBoolean,\r
1154     (LPVOID) &appData.highlightLastMove, TRUE },\r
1155   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1156   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1157   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1158   { "highlightDragging", ArgBoolean,\r
1159     (LPVOID) &appData.highlightDragging, TRUE },\r
1160   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1161   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1162   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1163   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1164   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1165   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1166   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1167   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1168   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1169   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1170   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1171   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1172   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1173   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1174   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1175   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1176   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1177   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1178   { "soundShout", ArgFilename,\r
1179     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1180   { "soundSShout", ArgFilename,\r
1181     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1182   { "soundChannel1", ArgFilename,\r
1183     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1184   { "soundChannel", ArgFilename,\r
1185     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1186   { "soundKibitz", ArgFilename,\r
1187     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1188   { "soundTell", ArgFilename,\r
1189     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1190   { "soundChallenge", ArgFilename,\r
1191     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1192   { "soundRequest", ArgFilename,\r
1193     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1194   { "soundSeek", ArgFilename,\r
1195     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1196   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1197   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1198   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1199   { "soundIcsLoss", ArgFilename, \r
1200     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1201   { "soundIcsDraw", ArgFilename, \r
1202     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1203   { "soundIcsUnfinished", ArgFilename, \r
1204     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1205   { "soundIcsAlarm", ArgFilename, \r
1206     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1207   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1208   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1209   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1210   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1211   { "reuseChessPrograms", ArgBoolean,\r
1212     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1213   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1214   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1215   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1216   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1217   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1218   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1219   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1220   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1221   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1222   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1223   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1224   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1225   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1226   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1227   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1228     TRUE },\r
1229   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1230     TRUE },\r
1231   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1232   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1233   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1234   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1235   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1236   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1237   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1238   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1239   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1240   /* [AS] New features */\r
1241   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1242   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1243   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1244   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1245   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1246   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1247   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1248   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1249   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1250   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1251   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1252   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1253   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1254   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1255   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1256   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1257   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1258   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1259   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1260   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1261   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1262   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1263   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1264   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1265   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1266   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1267   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1268   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1269   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1270   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1271   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1272   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1273   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1274   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1275   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1276   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1277   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1278   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1279   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1280   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1281   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1282   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1283   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1284   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1285   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1286   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1287   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1288   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1289   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1290   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1291 \r
1292   /* [HGM] board-size, adjudication and misc. options */\r
1293   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1294   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1295   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1296   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1297   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1298   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1299   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1300   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1301   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1302   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1303   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1304   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1305   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1306   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1307   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1308   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1309   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1310   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1311   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1312   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1313   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1314   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1315   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1316   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1317   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1318   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1319   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1320   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1321   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1322   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1323   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1324 \r
1325 #ifdef ZIPPY\r
1326   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1327   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1328   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1329   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1330   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1331   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1332   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1333   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1334   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1335   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1336   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1337   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1338   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1339     FALSE },\r
1340   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1341   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1342   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1343   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1344   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1345   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1346   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1347     FALSE },\r
1348   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1349   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1350   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1351   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1352   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1353   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1354   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1355   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1356   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1357   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1358   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1359   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1360   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1361   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1362   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1363   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1364   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1365   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1366   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1367 #endif\r
1368   /* [HGM] options for broadcasting and time odds */\r
1369   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1370   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1371   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1372   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1373   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1374   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1375   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1376   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1377   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1378   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1379   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1380 \r
1381   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1382   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1383   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1384   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1385   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1386   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1387   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1388   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1389   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1390   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1391   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1392   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1393   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1394   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1395   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1396   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1397   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1398   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1399   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1400   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1401   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1402   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1403   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1404   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1405   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1406   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1407   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1408   /* [AS] Layout stuff */\r
1409   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1410   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1411   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1412   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1413   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1414 \r
1415   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1416   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1417   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1418   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1419   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1420 \r
1421   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1422   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1423   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1424   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1425   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1426 \r
1427   { NULL, ArgNone, NULL, FALSE }\r
1428 };\r
1429 \r
1430 \r
1431 /* Kludge for indirection files on command line */\r
1432 char* lastIndirectionFilename;\r
1433 ArgDescriptor argDescriptorIndirection =\r
1434 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1435 \r
1436 \r
1437 VOID\r
1438 ExitArgError(char *msg, char *badArg)\r
1439 {\r
1440   char buf[MSG_SIZ];\r
1441 \r
1442   sprintf(buf, "%s %s", msg, badArg);\r
1443   DisplayFatalError(buf, 0, 2);\r
1444   exit(2);\r
1445 }\r
1446 \r
1447 /* Command line font name parser.  NULL name means do nothing.\r
1448    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1449    For backward compatibility, syntax without the colon is also\r
1450    accepted, but font names with digits in them won't work in that case.\r
1451 */\r
1452 VOID\r
1453 ParseFontName(char *name, MyFontParams *mfp)\r
1454 {\r
1455   char *p, *q;\r
1456   if (name == NULL) return;\r
1457   p = name;\r
1458   q = strchr(p, ':');\r
1459   if (q) {\r
1460     if (q - p >= sizeof(mfp->faceName))\r
1461       ExitArgError("Font name too long:", name);\r
1462     memcpy(mfp->faceName, p, q - p);\r
1463     mfp->faceName[q - p] = NULLCHAR;\r
1464     p = q + 1;\r
1465   } else {\r
1466     q = mfp->faceName;\r
1467     while (*p && !isdigit(*p)) {\r
1468       *q++ = *p++;\r
1469       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1470         ExitArgError("Font name too long:", name);\r
1471     }\r
1472     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1473     *q = NULLCHAR;\r
1474   }\r
1475   if (!*p) ExitArgError("Font point size missing:", name);\r
1476   mfp->pointSize = (float) atof(p);\r
1477   mfp->bold = (strchr(p, 'b') != NULL);\r
1478   mfp->italic = (strchr(p, 'i') != NULL);\r
1479   mfp->underline = (strchr(p, 'u') != NULL);\r
1480   mfp->strikeout = (strchr(p, 's') != NULL);\r
1481 }\r
1482 \r
1483 /* Color name parser.\r
1484    X version accepts X color names, but this one\r
1485    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1486 COLORREF\r
1487 ParseColorName(char *name)\r
1488 {\r
1489   int red, green, blue, count;\r
1490   char buf[MSG_SIZ];\r
1491 \r
1492   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1493   if (count != 3) {\r
1494     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1495       &red, &green, &blue);\r
1496   }\r
1497   if (count != 3) {\r
1498     sprintf(buf, "Can't parse color name %s", name);\r
1499     DisplayError(buf, 0);\r
1500     return RGB(0, 0, 0);\r
1501   }\r
1502   return PALETTERGB(red, green, blue);\r
1503 }\r
1504 \r
1505 \r
1506 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1507 {\r
1508   char *e = argValue;\r
1509   int eff = 0;\r
1510 \r
1511   while (*e) {\r
1512     if (*e == 'b')      eff |= CFE_BOLD;\r
1513     else if (*e == 'i') eff |= CFE_ITALIC;\r
1514     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1515     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1516     else if (*e == '#' || isdigit(*e)) break;\r
1517     e++;\r
1518   }\r
1519   *effects = eff;\r
1520   *color   = ParseColorName(e);\r
1521 }\r
1522 \r
1523 \r
1524 BoardSize\r
1525 ParseBoardSize(char *name)\r
1526 {\r
1527   BoardSize bs = SizeTiny;\r
1528   while (sizeInfo[bs].name != NULL) {\r
1529     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1530     bs++;\r
1531   }\r
1532   ExitArgError("Unrecognized board size value", name);\r
1533   return bs; /* not reached */\r
1534 }\r
1535 \r
1536 \r
1537 char\r
1538 StringGet(void *getClosure)\r
1539 {\r
1540   char **p = (char **) getClosure;\r
1541   return *((*p)++);\r
1542 }\r
1543 \r
1544 char\r
1545 FileGet(void *getClosure)\r
1546 {\r
1547   int c;\r
1548   FILE* f = (FILE*) getClosure;\r
1549 \r
1550   c = getc(f);\r
1551   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1552   if (c == EOF)\r
1553     return NULLCHAR;\r
1554   else\r
1555     return (char) c;\r
1556 }\r
1557 \r
1558 /* Parse settings file named "name". If file found, return the\r
1559    full name in fullname and return TRUE; else return FALSE */\r
1560 BOOLEAN\r
1561 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1562 {\r
1563   char *dummy;\r
1564   FILE *f;\r
1565   int ok; char buf[MSG_SIZ];\r
1566 \r
1567   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1568   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1569     sprintf(buf, "%s.ini", name);\r
1570     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1571   }\r
1572   if (ok) {\r
1573     f = fopen(fullname, "r");\r
1574     if (f != NULL) {\r
1575       ParseArgs(FileGet, f);\r
1576       fclose(f);\r
1577       return TRUE;\r
1578     }\r
1579   }\r
1580   return FALSE;\r
1581 }\r
1582 \r
1583 VOID\r
1584 ParseArgs(GetFunc get, void *cl)\r
1585 {\r
1586   char argName[ARG_MAX];\r
1587   char argValue[ARG_MAX];\r
1588   ArgDescriptor *ad;\r
1589   char start;\r
1590   char *q;\r
1591   int i, octval;\r
1592   char ch;\r
1593   int posarg = 0;\r
1594 \r
1595   ch = get(cl);\r
1596   for (;;) {\r
1597     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1598     if (ch == NULLCHAR) break;\r
1599     if (ch == ';') {\r
1600       /* Comment to end of line */\r
1601       ch = get(cl);\r
1602       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1603       continue;\r
1604     } else if (ch == '/' || ch == '-') {\r
1605       /* Switch */\r
1606       q = argName;\r
1607       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1608              ch != '\n' && ch != '\t') {\r
1609         *q++ = ch;\r
1610         ch = get(cl);\r
1611       }\r
1612       *q = NULLCHAR;\r
1613 \r
1614       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1615         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1616 \r
1617       if (ad->argName == NULL)\r
1618         ExitArgError("Unrecognized argument", argName);\r
1619 \r
1620     } else if (ch == '@') {\r
1621       /* Indirection file */\r
1622       ad = &argDescriptorIndirection;\r
1623       ch = get(cl);\r
1624     } else {\r
1625       /* Positional argument */\r
1626       ad = &argDescriptors[posarg++];\r
1627       strcpy(argName, ad->argName);\r
1628     }\r
1629 \r
1630     if (ad->argType == ArgTrue) {\r
1631       *(Boolean *) ad->argLoc = TRUE;\r
1632       continue;\r
1633     }\r
1634     if (ad->argType == ArgFalse) {\r
1635       *(Boolean *) ad->argLoc = FALSE;\r
1636       continue;\r
1637     }\r
1638 \r
1639     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1640     if (ch == NULLCHAR || ch == '\n') {\r
1641       ExitArgError("No value provided for argument", argName);\r
1642     }\r
1643     q = argValue;\r
1644     if (ch == '{') {\r
1645       // Quoting with { }.  No characters have to (or can) be escaped.\r
1646       // Thus the string cannot contain a '}' character.\r
1647       start = ch;\r
1648       ch = get(cl);\r
1649       while (start) {\r
1650         switch (ch) {\r
1651         case NULLCHAR:\r
1652           start = NULLCHAR;\r
1653           break;\r
1654           \r
1655         case '}':\r
1656           ch = get(cl);\r
1657           start = NULLCHAR;\r
1658           break;\r
1659 \r
1660         default:\r
1661           *q++ = ch;\r
1662           ch = get(cl);\r
1663           break;\r
1664         }\r
1665       }   \r
1666     } else if (ch == '\'' || ch == '"') {\r
1667       // Quoting with ' ' or " ", with \ as escape character.\r
1668       // Inconvenient for long strings that may contain Windows filenames.\r
1669       start = ch;\r
1670       ch = get(cl);\r
1671       while (start) {\r
1672         switch (ch) {\r
1673         case NULLCHAR:\r
1674           start = NULLCHAR;\r
1675           break;\r
1676 \r
1677         default:\r
1678         not_special:\r
1679           *q++ = ch;\r
1680           ch = get(cl);\r
1681           break;\r
1682 \r
1683         case '\'':\r
1684         case '\"':\r
1685           if (ch == start) {\r
1686             ch = get(cl);\r
1687             start = NULLCHAR;\r
1688             break;\r
1689           } else {\r
1690             goto not_special;\r
1691           }\r
1692 \r
1693         case '\\':\r
1694           if (ad->argType == ArgFilename\r
1695               || ad->argType == ArgSettingsFilename) {\r
1696               goto not_special;\r
1697           }\r
1698           ch = get(cl);\r
1699           switch (ch) {\r
1700           case NULLCHAR:\r
1701             ExitArgError("Incomplete \\ escape in value for", argName);\r
1702             break;\r
1703           case 'n':\r
1704             *q++ = '\n';\r
1705             ch = get(cl);\r
1706             break;\r
1707           case 'r':\r
1708             *q++ = '\r';\r
1709             ch = get(cl);\r
1710             break;\r
1711           case 't':\r
1712             *q++ = '\t';\r
1713             ch = get(cl);\r
1714             break;\r
1715           case 'b':\r
1716             *q++ = '\b';\r
1717             ch = get(cl);\r
1718             break;\r
1719           case 'f':\r
1720             *q++ = '\f';\r
1721             ch = get(cl);\r
1722             break;\r
1723           default:\r
1724             octval = 0;\r
1725             for (i = 0; i < 3; i++) {\r
1726               if (ch >= '0' && ch <= '7') {\r
1727                 octval = octval*8 + (ch - '0');\r
1728                 ch = get(cl);\r
1729               } else {\r
1730                 break;\r
1731               }\r
1732             }\r
1733             if (i > 0) {\r
1734               *q++ = (char) octval;\r
1735             } else {\r
1736               *q++ = ch;\r
1737               ch = get(cl);\r
1738             }\r
1739             break;\r
1740           }\r
1741           break;\r
1742         }\r
1743       }\r
1744     } else {\r
1745       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1746         *q++ = ch;\r
1747         ch = get(cl);\r
1748       }\r
1749     }\r
1750     *q = NULLCHAR;\r
1751 \r
1752     switch (ad->argType) {\r
1753     case ArgInt:\r
1754       *(int *) ad->argLoc = atoi(argValue);\r
1755       break;\r
1756 \r
1757     case ArgX:\r
1758       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1759       break;\r
1760 \r
1761     case ArgY:\r
1762       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1763       break;\r
1764 \r
1765     case ArgZ:\r
1766       *(int *) ad->argLoc = atoi(argValue);\r
1767       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1768       break;\r
1769 \r
1770     case ArgFloat:\r
1771       *(float *) ad->argLoc = (float) atof(argValue);\r
1772       break;\r
1773 \r
1774     case ArgString:\r
1775     case ArgFilename:\r
1776       *(char **) ad->argLoc = strdup(argValue);\r
1777       break;\r
1778 \r
1779     case ArgSettingsFilename:\r
1780       {\r
1781         char fullname[MSG_SIZ];\r
1782         if (ParseSettingsFile(argValue, fullname)) {\r
1783           if (ad->argLoc != NULL) {\r
1784             *(char **) ad->argLoc = strdup(fullname);\r
1785           }\r
1786         } else {\r
1787           if (ad->argLoc != NULL) {\r
1788           } else {\r
1789             ExitArgError("Failed to open indirection file", argValue);\r
1790           }\r
1791         }\r
1792       }\r
1793       break;\r
1794 \r
1795     case ArgBoolean:\r
1796       switch (argValue[0]) {\r
1797       case 't':\r
1798       case 'T':\r
1799         *(Boolean *) ad->argLoc = TRUE;\r
1800         break;\r
1801       case 'f':\r
1802       case 'F':\r
1803         *(Boolean *) ad->argLoc = FALSE;\r
1804         break;\r
1805       default:\r
1806         ExitArgError("Unrecognized boolean argument value", argValue);\r
1807         break;\r
1808       }\r
1809       break;\r
1810 \r
1811     case ArgColor:\r
1812       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1813       break;\r
1814 \r
1815     case ArgAttribs: {\r
1816       ColorClass cc = (ColorClass)ad->argLoc;\r
1817       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1818       }\r
1819       break;\r
1820       \r
1821     case ArgBoardSize:\r
1822       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1823       break;\r
1824 \r
1825     case ArgFont:\r
1826       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1827       break;\r
1828 \r
1829     case ArgCommSettings:\r
1830       ParseCommSettings(argValue, &dcb);\r
1831       break;\r
1832 \r
1833     case ArgNone:\r
1834       ExitArgError("Unrecognized argument", argValue);\r
1835       break;\r
1836     case ArgTrue:\r
1837     case ArgFalse: ;\r
1838     }\r
1839   }\r
1840 }\r
1841 \r
1842 VOID\r
1843 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1844 {\r
1845   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1846   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1847   DeleteDC(hdc);\r
1848   lf->lfWidth = 0;\r
1849   lf->lfEscapement = 0;\r
1850   lf->lfOrientation = 0;\r
1851   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1852   lf->lfItalic = mfp->italic;\r
1853   lf->lfUnderline = mfp->underline;\r
1854   lf->lfStrikeOut = mfp->strikeout;\r
1855   lf->lfCharSet = DEFAULT_CHARSET;\r
1856   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1857   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1858   lf->lfQuality = DEFAULT_QUALITY;\r
1859   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1860   strcpy(lf->lfFaceName, mfp->faceName);\r
1861 }\r
1862 \r
1863 VOID\r
1864 CreateFontInMF(MyFont *mf)\r
1865 {\r
1866   LFfromMFP(&mf->lf, &mf->mfp);\r
1867   if (mf->hf) DeleteObject(mf->hf);\r
1868   mf->hf = CreateFontIndirect(&mf->lf);\r
1869 }\r
1870 \r
1871 VOID\r
1872 SetDefaultTextAttribs()\r
1873 {\r
1874   ColorClass cc;\r
1875   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1876     ParseAttribs(&textAttribs[cc].color, \r
1877                  &textAttribs[cc].effects, \r
1878                  defaultTextAttribs[cc]);\r
1879   }\r
1880 }\r
1881 \r
1882 VOID\r
1883 SetDefaultSounds()\r
1884 {\r
1885   ColorClass cc;\r
1886   SoundClass sc;\r
1887   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1888     textAttribs[cc].sound.name = strdup("");\r
1889     textAttribs[cc].sound.data = NULL;\r
1890   }\r
1891   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1892     sounds[sc].name = strdup("");\r
1893     sounds[sc].data = NULL;\r
1894   }\r
1895   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1896 }\r
1897 \r
1898 VOID\r
1899 LoadAllSounds()\r
1900 {\r
1901   ColorClass cc;\r
1902   SoundClass sc;\r
1903   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1904     MyLoadSound(&textAttribs[cc].sound);\r
1905   }\r
1906   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1907     MyLoadSound(&sounds[sc]);\r
1908   }\r
1909 }\r
1910 \r
1911 VOID\r
1912 InitAppData(LPSTR lpCmdLine)\r
1913 {\r
1914   int i, j;\r
1915   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1916   char *dummy, *p;\r
1917 \r
1918   programName = szAppName;\r
1919 \r
1920   /* Initialize to defaults */\r
1921   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1922   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1923   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1924   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1925   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1926   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1927   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1928   SetDefaultTextAttribs();\r
1929   SetDefaultSounds();\r
1930   appData.movesPerSession = MOVES_PER_SESSION;\r
1931   appData.initString = INIT_STRING;\r
1932   appData.secondInitString = INIT_STRING;\r
1933   appData.firstComputerString = COMPUTER_STRING;\r
1934   appData.secondComputerString = COMPUTER_STRING;\r
1935   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1936   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1937   appData.firstPlaysBlack = FALSE;\r
1938   appData.noChessProgram = FALSE;\r
1939   chessProgram = FALSE;\r
1940   appData.firstHost = FIRST_HOST;\r
1941   appData.secondHost = SECOND_HOST;\r
1942   appData.firstDirectory = FIRST_DIRECTORY;\r
1943   appData.secondDirectory = SECOND_DIRECTORY;\r
1944   appData.bitmapDirectory = "";\r
1945   appData.remoteShell = REMOTE_SHELL;\r
1946   appData.remoteUser = "";\r
1947   appData.timeDelay = TIME_DELAY;\r
1948   appData.timeControl = TIME_CONTROL;\r
1949   appData.timeIncrement = TIME_INCREMENT;\r
1950   appData.icsActive = FALSE;\r
1951   appData.icsHost = "";\r
1952   appData.icsPort = ICS_PORT;\r
1953   appData.icsCommPort = ICS_COMM_PORT;\r
1954   appData.icsLogon = ICS_LOGON;\r
1955   appData.icsHelper = "";\r
1956   appData.useTelnet = FALSE;\r
1957   appData.telnetProgram = TELNET_PROGRAM;\r
1958   appData.gateway = "";\r
1959   appData.loadGameFile = "";\r
1960   appData.loadGameIndex = 0;\r
1961   appData.saveGameFile = "";\r
1962   appData.autoSaveGames = FALSE;\r
1963   appData.loadPositionFile = "";\r
1964   appData.loadPositionIndex = 1;\r
1965   appData.savePositionFile = "";\r
1966   appData.matchMode = FALSE;\r
1967   appData.matchGames = 0;\r
1968   appData.monoMode = FALSE;\r
1969   appData.debugMode = FALSE;\r
1970   appData.clockMode = TRUE;\r
1971   boardSize = (BoardSize) -1; /* determine by screen size */\r
1972   appData.Iconic = FALSE; /*unused*/\r
1973   appData.searchTime = "";\r
1974   appData.searchDepth = 0;\r
1975   appData.showCoords = FALSE;\r
1976   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1977   appData.autoCallFlag = FALSE;\r
1978   appData.flipView = FALSE;\r
1979   appData.autoFlipView = TRUE;\r
1980   appData.cmailGameName = "";\r
1981   appData.alwaysPromoteToQueen = FALSE;\r
1982   appData.oldSaveStyle = FALSE;\r
1983   appData.quietPlay = FALSE;\r
1984   appData.showThinking = FALSE;\r
1985   appData.ponderNextMove = TRUE;\r
1986   appData.periodicUpdates = TRUE;\r
1987   appData.popupExitMessage = TRUE;\r
1988   appData.popupMoveErrors = FALSE;\r
1989   appData.autoObserve = FALSE;\r
1990   appData.autoComment = FALSE;\r
1991   appData.animate = TRUE;\r
1992   appData.animSpeed = 10;\r
1993   appData.animateDragging = TRUE;\r
1994   appData.highlightLastMove = TRUE;\r
1995   appData.getMoveList = TRUE;\r
1996   appData.testLegality = TRUE;\r
1997   appData.premove = TRUE;\r
1998   appData.premoveWhite = FALSE;\r
1999   appData.premoveWhiteText = "";\r
2000   appData.premoveBlack = FALSE;\r
2001   appData.premoveBlackText = "";\r
2002   appData.icsAlarm = TRUE;\r
2003   appData.icsAlarmTime = 5000;\r
2004   appData.autoRaiseBoard = TRUE;\r
2005   appData.localLineEditing = TRUE;\r
2006   appData.colorize = TRUE;\r
2007   appData.reuseFirst = TRUE;\r
2008   appData.reuseSecond = TRUE;\r
2009   appData.blindfold = FALSE;\r
2010   appData.icsEngineAnalyze = FALSE;\r
2011   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2012   dcb.DCBlength = sizeof(DCB);\r
2013   dcb.BaudRate = 9600;\r
2014   dcb.fBinary = TRUE;\r
2015   dcb.fParity = FALSE;\r
2016   dcb.fOutxCtsFlow = FALSE;\r
2017   dcb.fOutxDsrFlow = FALSE;\r
2018   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2019   dcb.fDsrSensitivity = FALSE;\r
2020   dcb.fTXContinueOnXoff = TRUE;\r
2021   dcb.fOutX = FALSE;\r
2022   dcb.fInX = FALSE;\r
2023   dcb.fNull = FALSE;\r
2024   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2025   dcb.fAbortOnError = FALSE;\r
2026   dcb.ByteSize = 7;\r
2027   dcb.Parity = SPACEPARITY;\r
2028   dcb.StopBits = ONESTOPBIT;\r
2029   settingsFileName = SETTINGS_FILE;\r
2030   saveSettingsOnExit = TRUE;\r
2031   boardX = CW_USEDEFAULT;\r
2032   boardY = CW_USEDEFAULT;\r
2033   analysisX = CW_USEDEFAULT; \r
2034   analysisY = CW_USEDEFAULT; \r
2035   analysisW = CW_USEDEFAULT;\r
2036   analysisH = CW_USEDEFAULT;\r
2037   commentX = CW_USEDEFAULT; \r
2038   commentY = CW_USEDEFAULT; \r
2039   commentW = CW_USEDEFAULT;\r
2040   commentH = CW_USEDEFAULT;\r
2041   editTagsX = CW_USEDEFAULT; \r
2042   editTagsY = CW_USEDEFAULT; \r
2043   editTagsW = CW_USEDEFAULT;\r
2044   editTagsH = CW_USEDEFAULT;\r
2045   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2046   icsNames = ICS_NAMES;\r
2047   firstChessProgramNames = FCP_NAMES;\r
2048   secondChessProgramNames = SCP_NAMES;\r
2049   appData.initialMode = "";\r
2050   appData.variant = "normal";\r
2051   appData.firstProtocolVersion = PROTOVER;\r
2052   appData.secondProtocolVersion = PROTOVER;\r
2053   appData.showButtonBar = TRUE;\r
2054 \r
2055    /* [AS] New properties (see comments in header file) */\r
2056   appData.firstScoreIsAbsolute = FALSE;\r
2057   appData.secondScoreIsAbsolute = FALSE;\r
2058   appData.saveExtendedInfoInPGN = FALSE;\r
2059   appData.hideThinkingFromHuman = FALSE;\r
2060   appData.liteBackTextureFile = "";\r
2061   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2062   appData.darkBackTextureFile = "";\r
2063   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2064   appData.renderPiecesWithFont = "";\r
2065   appData.fontToPieceTable = "";\r
2066   appData.fontBackColorWhite = 0;\r
2067   appData.fontForeColorWhite = 0;\r
2068   appData.fontBackColorBlack = 0;\r
2069   appData.fontForeColorBlack = 0;\r
2070   appData.fontPieceSize = 80;\r
2071   appData.overrideLineGap = 1;\r
2072   appData.adjudicateLossThreshold = 0;\r
2073   appData.delayBeforeQuit = 0;\r
2074   appData.delayAfterQuit = 0;\r
2075   appData.nameOfDebugFile = "winboard.debug";\r
2076   appData.pgnEventHeader = "Computer Chess Game";\r
2077   appData.defaultFrcPosition = -1;\r
2078   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2079   appData.saveOutOfBookInfo = TRUE;\r
2080   appData.showEvalInMoveHistory = TRUE;\r
2081   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2082   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2083   appData.highlightMoveWithArrow = FALSE;\r
2084   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2085   appData.useStickyWindows = TRUE;\r
2086   appData.adjudicateDrawMoves = 0;\r
2087   appData.autoDisplayComment = TRUE;\r
2088   appData.autoDisplayTags = TRUE;\r
2089   appData.firstIsUCI = FALSE;\r
2090   appData.secondIsUCI = FALSE;\r
2091   appData.firstHasOwnBookUCI = TRUE;\r
2092   appData.secondHasOwnBookUCI = TRUE;\r
2093   appData.polyglotDir = "";\r
2094   appData.usePolyglotBook = FALSE;\r
2095   appData.polyglotBook = "";\r
2096   appData.defaultHashSize = 64;\r
2097   appData.defaultCacheSizeEGTB = 4;\r
2098   appData.defaultPathEGTB = "c:\\egtb";\r
2099   appData.firstOptions = "";\r
2100   appData.secondOptions = "";\r
2101 \r
2102   InitWindowPlacement( &wpGameList );\r
2103   InitWindowPlacement( &wpMoveHistory );\r
2104   InitWindowPlacement( &wpEvalGraph );\r
2105   InitWindowPlacement( &wpEngineOutput );\r
2106   InitWindowPlacement( &wpConsole );\r
2107 \r
2108   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2109   appData.NrFiles      = -1;\r
2110   appData.NrRanks      = -1;\r
2111   appData.holdingsSize = -1;\r
2112   appData.testClaims   = FALSE;\r
2113   appData.checkMates   = FALSE;\r
2114   appData.materialDraws= FALSE;\r
2115   appData.trivialDraws = FALSE;\r
2116   appData.ruleMoves    = 51;\r
2117   appData.drawRepeats  = 6;\r
2118   appData.matchPause   = 10000;\r
2119   appData.alphaRank    = FALSE;\r
2120   appData.allWhite     = FALSE;\r
2121   appData.upsideDown   = FALSE;\r
2122   appData.serverPause  = 15;\r
2123   appData.serverMovesName   = NULL;\r
2124   appData.suppressLoadMoves = FALSE;\r
2125   appData.firstTimeOdds  = 1;\r
2126   appData.secondTimeOdds = 1;\r
2127   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2128   appData.secondAccumulateTC = 1;\r
2129   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2130   appData.secondNPS = -1;\r
2131   appData.engineComments = 1;\r
2132   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2133   appData.egtFormats = "";\r
2134 \r
2135 #ifdef ZIPPY\r
2136   appData.zippyTalk = ZIPPY_TALK;\r
2137   appData.zippyPlay = ZIPPY_PLAY;\r
2138   appData.zippyLines = ZIPPY_LINES;\r
2139   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2140   appData.zippyPassword = ZIPPY_PASSWORD;\r
2141   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2142   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2143   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2144   appData.zippyUseI = ZIPPY_USE_I;\r
2145   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2146   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2147   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2148   appData.zippyGameStart = ZIPPY_GAME_START;\r
2149   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2150   appData.zippyAbort = ZIPPY_ABORT;\r
2151   appData.zippyVariants = ZIPPY_VARIANTS;\r
2152   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2153   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2154 #endif\r
2155 \r
2156   /* Point font array elements to structures and\r
2157      parse default font names */\r
2158   for (i=0; i<NUM_FONTS; i++) {\r
2159     for (j=0; j<NUM_SIZES; j++) {\r
2160       font[j][i] = &fontRec[j][i];\r
2161       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2162     }\r
2163   }\r
2164   \r
2165   /* Parse default settings file if any */\r
2166   if (ParseSettingsFile(settingsFileName, buf)) {\r
2167     settingsFileName = strdup(buf);\r
2168   }\r
2169 \r
2170   /* Parse command line */\r
2171   ParseArgs(StringGet, &lpCmdLine);\r
2172 \r
2173   /* [HGM] make sure board size is acceptable */\r
2174   if(appData.NrFiles > BOARD_SIZE ||\r
2175      appData.NrRanks > BOARD_SIZE   )\r
2176       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2177 \r
2178   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2179    * with options from the command line, we now make an even higher priority\r
2180    * overrule by WB options attached to the engine command line. This so that\r
2181    * tournament managers can use WB options (such as /timeOdds) that follow\r
2182    * the engines.\r
2183    */\r
2184   if(appData.firstChessProgram != NULL) {\r
2185       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2186       static char *f = "first";\r
2187       char buf[MSG_SIZ], *q = buf;\r
2188       if(p != NULL) { // engine command line contains WinBoard options\r
2189           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2190           ParseArgs(StringGet, &q);\r
2191           p[-1] = 0; // cut them offengine command line\r
2192       }\r
2193   }\r
2194   // now do same for second chess program\r
2195   if(appData.secondChessProgram != NULL) {\r
2196       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2197       static char *s = "second";\r
2198       char buf[MSG_SIZ], *q = buf;\r
2199       if(p != NULL) { // engine command line contains WinBoard options\r
2200           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2201           ParseArgs(StringGet, &q);\r
2202           p[-1] = 0; // cut them offengine command line\r
2203       }\r
2204   }\r
2205 \r
2206 \r
2207   /* Propagate options that affect others */\r
2208   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2209   if (appData.icsActive || appData.noChessProgram) {\r
2210      chessProgram = FALSE;  /* not local chess program mode */\r
2211   }\r
2212 \r
2213   /* Open startup dialog if needed */\r
2214   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2215       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2216       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2217                         *appData.secondChessProgram == NULLCHAR))) {\r
2218     FARPROC lpProc;\r
2219     \r
2220     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2221     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2222     FreeProcInstance(lpProc);\r
2223   }\r
2224 \r
2225   /* Make sure save files land in the right (?) directory */\r
2226   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2227     appData.saveGameFile = strdup(buf);\r
2228   }\r
2229   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2230     appData.savePositionFile = strdup(buf);\r
2231   }\r
2232 \r
2233   /* Finish initialization for fonts and sounds */\r
2234   for (i=0; i<NUM_FONTS; i++) {\r
2235     for (j=0; j<NUM_SIZES; j++) {\r
2236       CreateFontInMF(font[j][i]);\r
2237     }\r
2238   }\r
2239   /* xboard, and older WinBoards, controlled the move sound with the\r
2240      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2241      always turn the option on (so that the backend will call us),\r
2242      then let the user turn the sound off by setting it to silence if\r
2243      desired.  To accommodate old winboard.ini files saved by old\r
2244      versions of WinBoard, we also turn off the sound if the option\r
2245      was initially set to false. */\r
2246   if (!appData.ringBellAfterMoves) {\r
2247     sounds[(int)SoundMove].name = strdup("");\r
2248     appData.ringBellAfterMoves = TRUE;\r
2249   }\r
2250   GetCurrentDirectory(MSG_SIZ, currDir);\r
2251   SetCurrentDirectory(installDir);\r
2252   LoadAllSounds();\r
2253   SetCurrentDirectory(currDir);\r
2254 \r
2255   p = icsTextMenuString;\r
2256   if (p[0] == '@') {\r
2257     FILE* f = fopen(p + 1, "r");\r
2258     if (f == NULL) {\r
2259       DisplayFatalError(p + 1, errno, 2);\r
2260       return;\r
2261     }\r
2262     i = fread(buf, 1, sizeof(buf)-1, f);\r
2263     fclose(f);\r
2264     buf[i] = NULLCHAR;\r
2265     p = buf;\r
2266   }\r
2267   ParseIcsTextMenu(strdup(p));\r
2268 }\r
2269 \r
2270 \r
2271 VOID\r
2272 InitMenuChecks()\r
2273 {\r
2274   HMENU hmenu = GetMenu(hwndMain);\r
2275 \r
2276   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2277                         MF_BYCOMMAND|((appData.icsActive &&\r
2278                                        *appData.icsCommPort != NULLCHAR) ?\r
2279                                       MF_ENABLED : MF_GRAYED));\r
2280   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2281                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2282                                      MF_CHECKED : MF_UNCHECKED));\r
2283 }\r
2284 \r
2285 \r
2286 VOID\r
2287 SaveSettings(char* name)\r
2288 {\r
2289   FILE *f;\r
2290   ArgDescriptor *ad;\r
2291   WINDOWPLACEMENT wp;\r
2292   char dir[MSG_SIZ];\r
2293 \r
2294   if (!hwndMain) return;\r
2295 \r
2296   GetCurrentDirectory(MSG_SIZ, dir);\r
2297   SetCurrentDirectory(installDir);\r
2298   f = fopen(name, "w");\r
2299   SetCurrentDirectory(dir);\r
2300   if (f == NULL) {\r
2301     DisplayError(name, errno);\r
2302     return;\r
2303   }\r
2304   fprintf(f, ";\n");\r
2305   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2306   fprintf(f, ";\n");\r
2307   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2308   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2309   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2310   fprintf(f, ";\n");\r
2311 \r
2312   wp.length = sizeof(WINDOWPLACEMENT);\r
2313   GetWindowPlacement(hwndMain, &wp);\r
2314   boardX = wp.rcNormalPosition.left;\r
2315   boardY = wp.rcNormalPosition.top;\r
2316 \r
2317   if (hwndConsole) {\r
2318     GetWindowPlacement(hwndConsole, &wp);\r
2319     wpConsole.x = wp.rcNormalPosition.left;\r
2320     wpConsole.y = wp.rcNormalPosition.top;\r
2321     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2322     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2323   }\r
2324 \r
2325   if (analysisDialog) {\r
2326     GetWindowPlacement(analysisDialog, &wp);\r
2327     analysisX = wp.rcNormalPosition.left;\r
2328     analysisY = wp.rcNormalPosition.top;\r
2329     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2330     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2331   }\r
2332 \r
2333   if (commentDialog) {\r
2334     GetWindowPlacement(commentDialog, &wp);\r
2335     commentX = wp.rcNormalPosition.left;\r
2336     commentY = wp.rcNormalPosition.top;\r
2337     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2338     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2339   }\r
2340 \r
2341   if (editTagsDialog) {\r
2342     GetWindowPlacement(editTagsDialog, &wp);\r
2343     editTagsX = wp.rcNormalPosition.left;\r
2344     editTagsY = wp.rcNormalPosition.top;\r
2345     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2346     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2347   }\r
2348 \r
2349   if (gameListDialog) {\r
2350     GetWindowPlacement(gameListDialog, &wp);\r
2351     wpGameList.x = wp.rcNormalPosition.left;\r
2352     wpGameList.y = wp.rcNormalPosition.top;\r
2353     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2354     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2355   }\r
2356 \r
2357   /* [AS] Move history */\r
2358   wpMoveHistory.visible = MoveHistoryIsUp();\r
2359   \r
2360   if( moveHistoryDialog ) {\r
2361     GetWindowPlacement(moveHistoryDialog, &wp);\r
2362     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2363     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2364     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2365     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2366   }\r
2367 \r
2368   /* [AS] Eval graph */\r
2369   wpEvalGraph.visible = EvalGraphIsUp();\r
2370 \r
2371   if( evalGraphDialog ) {\r
2372     GetWindowPlacement(evalGraphDialog, &wp);\r
2373     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2374     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2375     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2376     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2377   }\r
2378 \r
2379   /* [AS] Engine output */\r
2380   wpEngineOutput.visible = EngineOutputIsUp();\r
2381 \r
2382   if( engineOutputDialog ) {\r
2383     GetWindowPlacement(engineOutputDialog, &wp);\r
2384     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2385     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2386     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2387     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2388   }\r
2389 \r
2390   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2391     if (!ad->save) continue;\r
2392     switch (ad->argType) {\r
2393     case ArgString:\r
2394       {\r
2395         char *p = *(char **)ad->argLoc;\r
2396         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2397           /* Quote multiline values or \-containing values\r
2398              with { } if possible */\r
2399           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2400         } else {\r
2401           /* Else quote with " " */\r
2402           fprintf(f, "/%s=\"", ad->argName);\r
2403           while (*p) {\r
2404             if (*p == '\n') fprintf(f, "\n");\r
2405             else if (*p == '\r') fprintf(f, "\\r");\r
2406             else if (*p == '\t') fprintf(f, "\\t");\r
2407             else if (*p == '\b') fprintf(f, "\\b");\r
2408             else if (*p == '\f') fprintf(f, "\\f");\r
2409             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2410             else if (*p == '\"') fprintf(f, "\\\"");\r
2411             else if (*p == '\\') fprintf(f, "\\\\");\r
2412             else putc(*p, f);\r
2413             p++;\r
2414           }\r
2415           fprintf(f, "\"\n");\r
2416         }\r
2417       }\r
2418       break;\r
2419     case ArgInt:\r
2420     case ArgZ:\r
2421       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2422       break;\r
2423     case ArgX:\r
2424       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2425       break;\r
2426     case ArgY:\r
2427       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2428       break;\r
2429     case ArgFloat:\r
2430       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2431       break;\r
2432     case ArgBoolean:\r
2433       fprintf(f, "/%s=%s\n", ad->argName, \r
2434         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2435       break;\r
2436     case ArgTrue:\r
2437       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2438       break;\r
2439     case ArgFalse:\r
2440       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2441       break;\r
2442     case ArgColor:\r
2443       {\r
2444         COLORREF color = *(COLORREF *)ad->argLoc;\r
2445         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2446           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2447       }\r
2448       break;\r
2449     case ArgAttribs:\r
2450       {\r
2451         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2452         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2453           (ta->effects & CFE_BOLD) ? "b" : "",\r
2454           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2455           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2456           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2457           (ta->effects) ? " " : "",\r
2458           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2459       }\r
2460       break;\r
2461     case ArgFilename:\r
2462       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2463         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2464       } else {\r
2465         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2466       }\r
2467       break;\r
2468     case ArgBoardSize:\r
2469       fprintf(f, "/%s=%s\n", ad->argName,\r
2470               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2471       break;\r
2472     case ArgFont:\r
2473       {\r
2474         int bs;\r
2475         for (bs=0; bs<NUM_SIZES; bs++) {\r
2476           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2477           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2478           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2479             ad->argName, mfp->faceName, mfp->pointSize,\r
2480             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2481             mfp->bold ? "b" : "",\r
2482             mfp->italic ? "i" : "",\r
2483             mfp->underline ? "u" : "",\r
2484             mfp->strikeout ? "s" : "");\r
2485         }\r
2486       }\r
2487       break;\r
2488     case ArgCommSettings:\r
2489       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2490     case ArgNone:\r
2491     case ArgSettingsFilename: ;\r
2492     }\r
2493   }\r
2494   fclose(f);\r
2495 }\r
2496 \r
2497 \r
2498 \r
2499 /*---------------------------------------------------------------------------*\\r
2500  *\r
2501  * GDI board drawing routines\r
2502  *\r
2503 \*---------------------------------------------------------------------------*/\r
2504 \r
2505 /* [AS] Draw square using background texture */\r
2506 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2507 {\r
2508     XFORM   x;\r
2509 \r
2510     if( mode == 0 ) {\r
2511         return; /* Should never happen! */\r
2512     }\r
2513 \r
2514     SetGraphicsMode( dst, GM_ADVANCED );\r
2515 \r
2516     switch( mode ) {\r
2517     case 1:\r
2518         /* Identity */\r
2519         break;\r
2520     case 2:\r
2521         /* X reflection */\r
2522         x.eM11 = -1.0;\r
2523         x.eM12 = 0;\r
2524         x.eM21 = 0;\r
2525         x.eM22 = 1.0;\r
2526         x.eDx = (FLOAT) dw + dx - 1;\r
2527         x.eDy = 0;\r
2528         dx = 0;\r
2529         SetWorldTransform( dst, &x );\r
2530         break;\r
2531     case 3:\r
2532         /* Y reflection */\r
2533         x.eM11 = 1.0;\r
2534         x.eM12 = 0;\r
2535         x.eM21 = 0;\r
2536         x.eM22 = -1.0;\r
2537         x.eDx = 0;\r
2538         x.eDy = (FLOAT) dh + dy - 1;\r
2539         dy = 0;\r
2540         SetWorldTransform( dst, &x );\r
2541         break;\r
2542     case 4:\r
2543         /* X/Y flip */\r
2544         x.eM11 = 0;\r
2545         x.eM12 = 1.0;\r
2546         x.eM21 = 1.0;\r
2547         x.eM22 = 0;\r
2548         x.eDx = (FLOAT) dx;\r
2549         x.eDy = (FLOAT) dy;\r
2550         dx = 0;\r
2551         dy = 0;\r
2552         SetWorldTransform( dst, &x );\r
2553         break;\r
2554     }\r
2555 \r
2556     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2557 \r
2558     x.eM11 = 1.0;\r
2559     x.eM12 = 0;\r
2560     x.eM21 = 0;\r
2561     x.eM22 = 1.0;\r
2562     x.eDx = 0;\r
2563     x.eDy = 0;\r
2564     SetWorldTransform( dst, &x );\r
2565 \r
2566     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2567 }\r
2568 \r
2569 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2570 enum {\r
2571     PM_WP = (int) WhitePawn, \r
2572     PM_WN = (int) WhiteKnight, \r
2573     PM_WB = (int) WhiteBishop, \r
2574     PM_WR = (int) WhiteRook, \r
2575     PM_WQ = (int) WhiteQueen, \r
2576     PM_WF = (int) WhiteFerz, \r
2577     PM_WW = (int) WhiteWazir, \r
2578     PM_WE = (int) WhiteAlfil, \r
2579     PM_WM = (int) WhiteMan, \r
2580     PM_WO = (int) WhiteCannon, \r
2581     PM_WU = (int) WhiteUnicorn, \r
2582     PM_WH = (int) WhiteNightrider, \r
2583     PM_WA = (int) WhiteAngel, \r
2584     PM_WC = (int) WhiteMarshall, \r
2585     PM_WAB = (int) WhiteCardinal, \r
2586     PM_WD = (int) WhiteDragon, \r
2587     PM_WL = (int) WhiteLance, \r
2588     PM_WS = (int) WhiteCobra, \r
2589     PM_WV = (int) WhiteFalcon, \r
2590     PM_WSG = (int) WhiteSilver, \r
2591     PM_WG = (int) WhiteGrasshopper, \r
2592     PM_WK = (int) WhiteKing,\r
2593     PM_BP = (int) BlackPawn, \r
2594     PM_BN = (int) BlackKnight, \r
2595     PM_BB = (int) BlackBishop, \r
2596     PM_BR = (int) BlackRook, \r
2597     PM_BQ = (int) BlackQueen, \r
2598     PM_BF = (int) BlackFerz, \r
2599     PM_BW = (int) BlackWazir, \r
2600     PM_BE = (int) BlackAlfil, \r
2601     PM_BM = (int) BlackMan,\r
2602     PM_BO = (int) BlackCannon, \r
2603     PM_BU = (int) BlackUnicorn, \r
2604     PM_BH = (int) BlackNightrider, \r
2605     PM_BA = (int) BlackAngel, \r
2606     PM_BC = (int) BlackMarshall, \r
2607     PM_BG = (int) BlackGrasshopper, \r
2608     PM_BAB = (int) BlackCardinal,\r
2609     PM_BD = (int) BlackDragon,\r
2610     PM_BL = (int) BlackLance,\r
2611     PM_BS = (int) BlackCobra,\r
2612     PM_BV = (int) BlackFalcon,\r
2613     PM_BSG = (int) BlackSilver,\r
2614     PM_BK = (int) BlackKing\r
2615 };\r
2616 \r
2617 static HFONT hPieceFont = NULL;\r
2618 static HBITMAP hPieceMask[(int) EmptySquare];\r
2619 static HBITMAP hPieceFace[(int) EmptySquare];\r
2620 static int fontBitmapSquareSize = 0;\r
2621 static char pieceToFontChar[(int) EmptySquare] =\r
2622                               { 'p', 'n', 'b', 'r', 'q', \r
2623                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2624                       'k', 'o', 'm', 'v', 't', 'w', \r
2625                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2626                                                               'l' };\r
2627 \r
2628 extern BOOL SetCharTable( char *table, const char * map );\r
2629 /* [HGM] moved to backend.c */\r
2630 \r
2631 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2632 {\r
2633     HBRUSH hbrush;\r
2634     BYTE r1 = GetRValue( color );\r
2635     BYTE g1 = GetGValue( color );\r
2636     BYTE b1 = GetBValue( color );\r
2637     BYTE r2 = r1 / 2;\r
2638     BYTE g2 = g1 / 2;\r
2639     BYTE b2 = b1 / 2;\r
2640     RECT rc;\r
2641 \r
2642     /* Create a uniform background first */\r
2643     hbrush = CreateSolidBrush( color );\r
2644     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2645     FillRect( hdc, &rc, hbrush );\r
2646     DeleteObject( hbrush );\r
2647     \r
2648     if( mode == 1 ) {\r
2649         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2650         int steps = squareSize / 2;\r
2651         int i;\r
2652 \r
2653         for( i=0; i<steps; i++ ) {\r
2654             BYTE r = r1 - (r1-r2) * i / steps;\r
2655             BYTE g = g1 - (g1-g2) * i / steps;\r
2656             BYTE b = b1 - (b1-b2) * i / steps;\r
2657 \r
2658             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2659             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2660             FillRect( hdc, &rc, hbrush );\r
2661             DeleteObject(hbrush);\r
2662         }\r
2663     }\r
2664     else if( mode == 2 ) {\r
2665         /* Diagonal gradient, good more or less for every piece */\r
2666         POINT triangle[3];\r
2667         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2668         HBRUSH hbrush_old;\r
2669         int steps = squareSize;\r
2670         int i;\r
2671 \r
2672         triangle[0].x = squareSize - steps;\r
2673         triangle[0].y = squareSize;\r
2674         triangle[1].x = squareSize;\r
2675         triangle[1].y = squareSize;\r
2676         triangle[2].x = squareSize;\r
2677         triangle[2].y = squareSize - steps;\r
2678 \r
2679         for( i=0; i<steps; i++ ) {\r
2680             BYTE r = r1 - (r1-r2) * i / steps;\r
2681             BYTE g = g1 - (g1-g2) * i / steps;\r
2682             BYTE b = b1 - (b1-b2) * i / steps;\r
2683 \r
2684             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2685             hbrush_old = SelectObject( hdc, hbrush );\r
2686             Polygon( hdc, triangle, 3 );\r
2687             SelectObject( hdc, hbrush_old );\r
2688             DeleteObject(hbrush);\r
2689             triangle[0].x++;\r
2690             triangle[2].y++;\r
2691         }\r
2692 \r
2693         SelectObject( hdc, hpen );\r
2694     }\r
2695 }\r
2696 \r
2697 /*\r
2698     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2699     seems to work ok. The main problem here is to find the "inside" of a chess\r
2700     piece: follow the steps as explained below.\r
2701 */\r
2702 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2703 {\r
2704     HBITMAP hbm;\r
2705     HBITMAP hbm_old;\r
2706     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2707     RECT rc;\r
2708     SIZE sz;\r
2709     POINT pt;\r
2710     int backColor = whitePieceColor; \r
2711     int foreColor = blackPieceColor;\r
2712     \r
2713     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2714         backColor = appData.fontBackColorWhite;\r
2715         foreColor = appData.fontForeColorWhite;\r
2716     }\r
2717     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2718         backColor = appData.fontBackColorBlack;\r
2719         foreColor = appData.fontForeColorBlack;\r
2720     }\r
2721 \r
2722     /* Mask */\r
2723     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2724 \r
2725     hbm_old = SelectObject( hdc, hbm );\r
2726 \r
2727     rc.left = 0;\r
2728     rc.top = 0;\r
2729     rc.right = squareSize;\r
2730     rc.bottom = squareSize;\r
2731 \r
2732     /* Step 1: background is now black */\r
2733     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2734 \r
2735     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2736 \r
2737     pt.x = (squareSize - sz.cx) / 2;\r
2738     pt.y = (squareSize - sz.cy) / 2;\r
2739 \r
2740     SetBkMode( hdc, TRANSPARENT );\r
2741     SetTextColor( hdc, chroma );\r
2742     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2743     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2744 \r
2745     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2746     /* Step 3: the area outside the piece is filled with white */\r
2747 //    FloodFill( hdc, 0, 0, chroma );\r
2748     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2749     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2750     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2751     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2752     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2753     /* \r
2754         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2755         but if the start point is not inside the piece we're lost!\r
2756         There should be a better way to do this... if we could create a region or path\r
2757         from the fill operation we would be fine for example.\r
2758     */\r
2759 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2760     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2761 \r
2762     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2763         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2764         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2765 \r
2766         SelectObject( dc2, bm2 );\r
2767         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2768         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2769         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2770         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2771         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2772 \r
2773         DeleteDC( dc2 );\r
2774         DeleteObject( bm2 );\r
2775     }\r
2776 \r
2777     SetTextColor( hdc, 0 );\r
2778     /* \r
2779         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2780         draw the piece again in black for safety.\r
2781     */\r
2782     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2783 \r
2784     SelectObject( hdc, hbm_old );\r
2785 \r
2786     if( hPieceMask[index] != NULL ) {\r
2787         DeleteObject( hPieceMask[index] );\r
2788     }\r
2789 \r
2790     hPieceMask[index] = hbm;\r
2791 \r
2792     /* Face */\r
2793     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2794 \r
2795     SelectObject( hdc, hbm );\r
2796 \r
2797     {\r
2798         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2799         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2800         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2801 \r
2802         SelectObject( dc1, hPieceMask[index] );\r
2803         SelectObject( dc2, bm2 );\r
2804         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2805         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2806         \r
2807         /* \r
2808             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2809             the piece background and deletes (makes transparent) the rest.\r
2810             Thanks to that mask, we are free to paint the background with the greates\r
2811             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2812             We use this, to make gradients and give the pieces a "roundish" look.\r
2813         */\r
2814         SetPieceBackground( hdc, backColor, 2 );\r
2815         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2816 \r
2817         DeleteDC( dc2 );\r
2818         DeleteDC( dc1 );\r
2819         DeleteObject( bm2 );\r
2820     }\r
2821 \r
2822     SetTextColor( hdc, foreColor );\r
2823     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2824 \r
2825     SelectObject( hdc, hbm_old );\r
2826 \r
2827     if( hPieceFace[index] != NULL ) {\r
2828         DeleteObject( hPieceFace[index] );\r
2829     }\r
2830 \r
2831     hPieceFace[index] = hbm;\r
2832 }\r
2833 \r
2834 static int TranslatePieceToFontPiece( int piece )\r
2835 {\r
2836     switch( piece ) {\r
2837     case BlackPawn:\r
2838         return PM_BP;\r
2839     case BlackKnight:\r
2840         return PM_BN;\r
2841     case BlackBishop:\r
2842         return PM_BB;\r
2843     case BlackRook:\r
2844         return PM_BR;\r
2845     case BlackQueen:\r
2846         return PM_BQ;\r
2847     case BlackKing:\r
2848         return PM_BK;\r
2849     case WhitePawn:\r
2850         return PM_WP;\r
2851     case WhiteKnight:\r
2852         return PM_WN;\r
2853     case WhiteBishop:\r
2854         return PM_WB;\r
2855     case WhiteRook:\r
2856         return PM_WR;\r
2857     case WhiteQueen:\r
2858         return PM_WQ;\r
2859     case WhiteKing:\r
2860         return PM_WK;\r
2861 \r
2862     case BlackAngel:\r
2863         return PM_BA;\r
2864     case BlackMarshall:\r
2865         return PM_BC;\r
2866     case BlackFerz:\r
2867         return PM_BF;\r
2868     case BlackNightrider:\r
2869         return PM_BH;\r
2870     case BlackAlfil:\r
2871         return PM_BE;\r
2872     case BlackWazir:\r
2873         return PM_BW;\r
2874     case BlackUnicorn:\r
2875         return PM_BU;\r
2876     case BlackCannon:\r
2877         return PM_BO;\r
2878     case BlackGrasshopper:\r
2879         return PM_BG;\r
2880     case BlackMan:\r
2881         return PM_BM;\r
2882     case BlackSilver:\r
2883         return PM_BSG;\r
2884     case BlackLance:\r
2885         return PM_BL;\r
2886     case BlackFalcon:\r
2887         return PM_BV;\r
2888     case BlackCobra:\r
2889         return PM_BS;\r
2890     case BlackCardinal:\r
2891         return PM_BAB;\r
2892     case BlackDragon:\r
2893         return PM_BD;\r
2894 \r
2895     case WhiteAngel:\r
2896         return PM_WA;\r
2897     case WhiteMarshall:\r
2898         return PM_WC;\r
2899     case WhiteFerz:\r
2900         return PM_WF;\r
2901     case WhiteNightrider:\r
2902         return PM_WH;\r
2903     case WhiteAlfil:\r
2904         return PM_WE;\r
2905     case WhiteWazir:\r
2906         return PM_WW;\r
2907     case WhiteUnicorn:\r
2908         return PM_WU;\r
2909     case WhiteCannon:\r
2910         return PM_WO;\r
2911     case WhiteGrasshopper:\r
2912         return PM_WG;\r
2913     case WhiteMan:\r
2914         return PM_WM;\r
2915     case WhiteSilver:\r
2916         return PM_WSG;\r
2917     case WhiteLance:\r
2918         return PM_WL;\r
2919     case WhiteFalcon:\r
2920         return PM_WV;\r
2921     case WhiteCobra:\r
2922         return PM_WS;\r
2923     case WhiteCardinal:\r
2924         return PM_WAB;\r
2925     case WhiteDragon:\r
2926         return PM_WD;\r
2927     }\r
2928 \r
2929     return 0;\r
2930 }\r
2931 \r
2932 void CreatePiecesFromFont()\r
2933 {\r
2934     LOGFONT lf;\r
2935     HDC hdc_window = NULL;\r
2936     HDC hdc = NULL;\r
2937     HFONT hfont_old;\r
2938     int fontHeight;\r
2939     int i;\r
2940 \r
2941     if( fontBitmapSquareSize < 0 ) {\r
2942         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2943         return;\r
2944     }\r
2945 \r
2946     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2947         fontBitmapSquareSize = -1;\r
2948         return;\r
2949     }\r
2950 \r
2951     if( fontBitmapSquareSize != squareSize ) {\r
2952         hdc_window = GetDC( hwndMain );\r
2953         hdc = CreateCompatibleDC( hdc_window );\r
2954 \r
2955         if( hPieceFont != NULL ) {\r
2956             DeleteObject( hPieceFont );\r
2957         }\r
2958         else {\r
2959             for( i=0; i<=(int)BlackKing; i++ ) {\r
2960                 hPieceMask[i] = NULL;\r
2961                 hPieceFace[i] = NULL;\r
2962             }\r
2963         }\r
2964 \r
2965         fontHeight = 75;\r
2966 \r
2967         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2968             fontHeight = appData.fontPieceSize;\r
2969         }\r
2970 \r
2971         fontHeight = (fontHeight * squareSize) / 100;\r
2972 \r
2973         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2974         lf.lfWidth = 0;\r
2975         lf.lfEscapement = 0;\r
2976         lf.lfOrientation = 0;\r
2977         lf.lfWeight = FW_NORMAL;\r
2978         lf.lfItalic = 0;\r
2979         lf.lfUnderline = 0;\r
2980         lf.lfStrikeOut = 0;\r
2981         lf.lfCharSet = DEFAULT_CHARSET;\r
2982         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2983         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2984         lf.lfQuality = PROOF_QUALITY;\r
2985         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2986         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2987         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2988 \r
2989         hPieceFont = CreateFontIndirect( &lf );\r
2990 \r
2991         if( hPieceFont == NULL ) {\r
2992             fontBitmapSquareSize = -2;\r
2993         }\r
2994         else {\r
2995             /* Setup font-to-piece character table */\r
2996             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2997                 /* No (or wrong) global settings, try to detect the font */\r
2998                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2999                     /* Alpha */\r
3000                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
3001                 }\r
3002                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3003                     /* DiagramTT* family */\r
3004                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3005                 }\r
3006                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3007                     /* Fairy symbols */\r
3008                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3009                 }\r
3010                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3011                     /* Good Companion (Some characters get warped as literal :-( */\r
3012                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3013                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3014                     SetCharTable(pieceToFontChar, s);\r
3015                 }\r
3016                 else {\r
3017                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3018                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3019                 }\r
3020             }\r
3021 \r
3022             /* Create bitmaps */\r
3023             hfont_old = SelectObject( hdc, hPieceFont );\r
3024 #if 0\r
3025             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
3026             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
3027             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
3028             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
3029             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
3030             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
3031             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
3032             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
3033             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
3034             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
3035             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
3036             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
3037 \r
3038             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
3039             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
3040             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
3041             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
3042             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
3043             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
3044             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
3045             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
3046             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
3047             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
3048             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
3049             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
3050             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
3051             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
3052             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
3053             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
3054             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
3055             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
3056             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
3057             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
3058             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
3059             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
3060             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
3061             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
3062             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
3063             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
3064             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
3065             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
3066             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
3067             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
3068             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
3069             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
3070 #else\r
3071             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3072                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3073                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3074 #endif\r
3075             SelectObject( hdc, hfont_old );\r
3076 \r
3077             fontBitmapSquareSize = squareSize;\r
3078         }\r
3079     }\r
3080 \r
3081     if( hdc != NULL ) {\r
3082         DeleteDC( hdc );\r
3083     }\r
3084 \r
3085     if( hdc_window != NULL ) {\r
3086         ReleaseDC( hwndMain, hdc_window );\r
3087     }\r
3088 }\r
3089 \r
3090 HBITMAP\r
3091 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3092 {\r
3093   char name[128];\r
3094 \r
3095   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3096   if (gameInfo.event &&\r
3097       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3098       strcmp(name, "k80s") == 0) {\r
3099     strcpy(name, "tim");\r
3100   }\r
3101   return LoadBitmap(hinst, name);\r
3102 }\r
3103 \r
3104 \r
3105 /* Insert a color into the program's logical palette\r
3106    structure.  This code assumes the given color is\r
3107    the result of the RGB or PALETTERGB macro, and it\r
3108    knows how those macros work (which is documented).\r
3109 */\r
3110 VOID\r
3111 InsertInPalette(COLORREF color)\r
3112 {\r
3113   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3114 \r
3115   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3116     DisplayFatalError("Too many colors", 0, 1);\r
3117     pLogPal->palNumEntries--;\r
3118     return;\r
3119   }\r
3120 \r
3121   pe->peFlags = (char) 0;\r
3122   pe->peRed = (char) (0xFF & color);\r
3123   pe->peGreen = (char) (0xFF & (color >> 8));\r
3124   pe->peBlue = (char) (0xFF & (color >> 16));\r
3125   return;\r
3126 }\r
3127 \r
3128 \r
3129 VOID\r
3130 InitDrawingColors()\r
3131 {\r
3132   if (pLogPal == NULL) {\r
3133     /* Allocate enough memory for a logical palette with\r
3134      * PALETTESIZE entries and set the size and version fields\r
3135      * of the logical palette structure.\r
3136      */\r
3137     pLogPal = (NPLOGPALETTE)\r
3138       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3139                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3140     pLogPal->palVersion    = 0x300;\r
3141   }\r
3142   pLogPal->palNumEntries = 0;\r
3143 \r
3144   InsertInPalette(lightSquareColor);\r
3145   InsertInPalette(darkSquareColor);\r
3146   InsertInPalette(whitePieceColor);\r
3147   InsertInPalette(blackPieceColor);\r
3148   InsertInPalette(highlightSquareColor);\r
3149   InsertInPalette(premoveHighlightColor);\r
3150 \r
3151   /*  create a logical color palette according the information\r
3152    *  in the LOGPALETTE structure.\r
3153    */\r
3154   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3155 \r
3156   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3157   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3158   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3159   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3160   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3161   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3162   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3163   /* [AS] Force rendering of the font-based pieces */\r
3164   if( fontBitmapSquareSize > 0 ) {\r
3165     fontBitmapSquareSize = 0;\r
3166   }\r
3167 }\r
3168 \r
3169 \r
3170 int\r
3171 BoardWidth(int boardSize, int n)\r
3172 { /* [HGM] argument n added to allow different width and height */\r
3173   int lineGap = sizeInfo[boardSize].lineGap;\r
3174 \r
3175   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3176       lineGap = appData.overrideLineGap;\r
3177   }\r
3178 \r
3179   return (n + 1) * lineGap +\r
3180           n * sizeInfo[boardSize].squareSize;\r
3181 }\r
3182 \r
3183 /* Respond to board resize by dragging edge */\r
3184 VOID\r
3185 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3186 {\r
3187   BoardSize newSize = NUM_SIZES - 1;\r
3188   static int recurse = 0;\r
3189   if (IsIconic(hwndMain)) return;\r
3190   if (recurse > 0) return;\r
3191   recurse++;\r
3192   while (newSize > 0) {\r
3193         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3194         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3195            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3196     newSize--;\r
3197   } \r
3198   boardSize = newSize;\r
3199   InitDrawingSizes(boardSize, flags);\r
3200   recurse--;\r
3201 }\r
3202 \r
3203 \r
3204 \r
3205 VOID\r
3206 InitDrawingSizes(BoardSize boardSize, int flags)\r
3207 {\r
3208   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3209   ChessSquare piece;\r
3210   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3211   HDC hdc;\r
3212   SIZE clockSize, messageSize;\r
3213   HFONT oldFont;\r
3214   char buf[MSG_SIZ];\r
3215   char *str;\r
3216   HMENU hmenu = GetMenu(hwndMain);\r
3217   RECT crect, wrect, oldRect;\r
3218   int offby;\r
3219   LOGBRUSH logbrush;\r
3220 \r
3221   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3222   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3223 \r
3224   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3225   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3226 \r
3227   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3228   oldRect.top = boardY;\r
3229   oldRect.right = boardX + winWidth;\r
3230   oldRect.bottom = boardY + winHeight;\r
3231 \r
3232   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3233   smallLayout = sizeInfo[boardSize].smallLayout;\r
3234   squareSize = sizeInfo[boardSize].squareSize;\r
3235   lineGap = sizeInfo[boardSize].lineGap;\r
3236   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3237 \r
3238   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3239       lineGap = appData.overrideLineGap;\r
3240   }\r
3241 \r
3242   if (tinyLayout != oldTinyLayout) {\r
3243     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3244     if (tinyLayout) {\r
3245       style &= ~WS_SYSMENU;\r
3246       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3247                  "&Minimize\tCtrl+F4");\r
3248     } else {\r
3249       style |= WS_SYSMENU;\r
3250       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3251     }\r
3252     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3253 \r
3254     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3255       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3256         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3257     }\r
3258     DrawMenuBar(hwndMain);\r
3259   }\r
3260 \r
3261   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3262   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3263 \r
3264   /* Get text area sizes */\r
3265   hdc = GetDC(hwndMain);\r
3266   if (appData.clockMode) {\r
3267     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3268   } else {\r
3269     sprintf(buf, "White");\r
3270   }\r
3271   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3272   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3273   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3274   str = "We only care about the height here";\r
3275   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3276   SelectObject(hdc, oldFont);\r
3277   ReleaseDC(hwndMain, hdc);\r
3278 \r
3279   /* Compute where everything goes */\r
3280   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3281         /* [HGM] logo: if either logo is on, reserve space for it */\r
3282         logoHeight =  2*clockSize.cy;\r
3283         leftLogoRect.left   = OUTER_MARGIN;\r
3284         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3285         leftLogoRect.top    = OUTER_MARGIN;\r
3286         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3287 \r
3288         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3289         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3290         rightLogoRect.top    = OUTER_MARGIN;\r
3291         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3292 \r
3293 \r
3294     whiteRect.left = leftLogoRect.right;\r
3295     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3296     whiteRect.top = OUTER_MARGIN;\r
3297     whiteRect.bottom = whiteRect.top + logoHeight;\r
3298 \r
3299     blackRect.right = rightLogoRect.left;\r
3300     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3301     blackRect.top = whiteRect.top;\r
3302     blackRect.bottom = whiteRect.bottom;\r
3303   } else {\r
3304     whiteRect.left = OUTER_MARGIN;\r
3305     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3306     whiteRect.top = OUTER_MARGIN;\r
3307     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3308 \r
3309     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3310     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3311     blackRect.top = whiteRect.top;\r
3312     blackRect.bottom = whiteRect.bottom;\r
3313   }\r
3314 \r
3315   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3316   if (appData.showButtonBar) {\r
3317     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3318       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3319   } else {\r
3320     messageRect.right = OUTER_MARGIN + boardWidth;\r
3321   }\r
3322   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3323   messageRect.bottom = messageRect.top + messageSize.cy;\r
3324 \r
3325   boardRect.left = OUTER_MARGIN;\r
3326   boardRect.right = boardRect.left + boardWidth;\r
3327   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3328   boardRect.bottom = boardRect.top + boardHeight;\r
3329 \r
3330   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3331   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3332   oldBoardSize = boardSize;\r
3333   oldTinyLayout = tinyLayout;\r
3334   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3335   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3336     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3337   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3338   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3339   winHeight = winH; //       without disturbing window attachments\r
3340   GetWindowRect(hwndMain, &wrect);\r
3341   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3342                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3343 \r
3344   // [HGM] placement: let attached windows follow size change.\r
3345   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3346   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3347   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3348   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3349   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3350 \r
3351   /* compensate if menu bar wrapped */\r
3352   GetClientRect(hwndMain, &crect);\r
3353   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3354   winHeight += offby;\r
3355   switch (flags) {\r
3356   case WMSZ_TOPLEFT:\r
3357     SetWindowPos(hwndMain, NULL, \r
3358                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3359                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3360     break;\r
3361 \r
3362   case WMSZ_TOPRIGHT:\r
3363   case WMSZ_TOP:\r
3364     SetWindowPos(hwndMain, NULL, \r
3365                  wrect.left, wrect.bottom - winHeight, \r
3366                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3367     break;\r
3368 \r
3369   case WMSZ_BOTTOMLEFT:\r
3370   case WMSZ_LEFT:\r
3371     SetWindowPos(hwndMain, NULL, \r
3372                  wrect.right - winWidth, wrect.top, \r
3373                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3374     break;\r
3375 \r
3376   case WMSZ_BOTTOMRIGHT:\r
3377   case WMSZ_BOTTOM:\r
3378   case WMSZ_RIGHT:\r
3379   default:\r
3380     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3381                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3382     break;\r
3383   }\r
3384 \r
3385   hwndPause = NULL;\r
3386   for (i = 0; i < N_BUTTONS; i++) {\r
3387     if (buttonDesc[i].hwnd != NULL) {\r
3388       DestroyWindow(buttonDesc[i].hwnd);\r
3389       buttonDesc[i].hwnd = NULL;\r
3390     }\r
3391     if (appData.showButtonBar) {\r
3392       buttonDesc[i].hwnd =\r
3393         CreateWindow("BUTTON", buttonDesc[i].label,\r
3394                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3395                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3396                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3397                      (HMENU) buttonDesc[i].id,\r
3398                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3399       if (tinyLayout) {\r
3400         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3401                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3402                     MAKELPARAM(FALSE, 0));\r
3403       }\r
3404       if (buttonDesc[i].id == IDM_Pause)\r
3405         hwndPause = buttonDesc[i].hwnd;\r
3406       buttonDesc[i].wndproc = (WNDPROC)\r
3407         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3408     }\r
3409   }\r
3410   if (gridPen != NULL) DeleteObject(gridPen);\r
3411   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3412   if (premovePen != NULL) DeleteObject(premovePen);\r
3413   if (lineGap != 0) {\r
3414     logbrush.lbStyle = BS_SOLID;\r
3415     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3416     gridPen =\r
3417       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3418                    lineGap, &logbrush, 0, NULL);\r
3419     logbrush.lbColor = highlightSquareColor;\r
3420     highlightPen =\r
3421       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3422                    lineGap, &logbrush, 0, NULL);\r
3423 \r
3424     logbrush.lbColor = premoveHighlightColor; \r
3425     premovePen =\r
3426       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3427                    lineGap, &logbrush, 0, NULL);\r
3428 \r
3429     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3430     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3431       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3432       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3433         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3434       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3435         BOARD_WIDTH * (squareSize + lineGap);\r
3436       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3437     }\r
3438     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3439       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3440       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3441         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3442         lineGap / 2 + (i * (squareSize + lineGap));\r
3443       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3444         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3445       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3446     }\r
3447   }\r
3448 \r
3449   /* [HGM] Licensing requirement */\r
3450 #ifdef GOTHIC\r
3451   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3452 #endif\r
3453 #ifdef FALCON\r
3454   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3455 #endif\r
3456   GothicPopUp( "", VariantNormal);\r
3457 \r
3458 \r
3459 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3460 \r
3461   /* Load piece bitmaps for this board size */\r
3462   for (i=0; i<=2; i++) {\r
3463     for (piece = WhitePawn;\r
3464          (int) piece < (int) BlackPawn;\r
3465          piece = (ChessSquare) ((int) piece + 1)) {\r
3466       if (pieceBitmap[i][piece] != NULL)\r
3467         DeleteObject(pieceBitmap[i][piece]);\r
3468     }\r
3469   }\r
3470 \r
3471   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3472   // Orthodox Chess pieces\r
3473   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3474   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3475   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3476   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3477   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3478   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3479   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3480   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3481   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3482   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3483   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3484   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3485   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3486   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3487   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3488   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3489     // in Shogi, Hijack the unused Queen for Lance\r
3490     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3491     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3492     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3493   } else {\r
3494     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3495     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3496     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3497   }\r
3498 \r
3499   if(squareSize <= 72 && squareSize >= 33) { \r
3500     /* A & C are available in most sizes now */\r
3501     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3502       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3503       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3504       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3505       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3506       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3507       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3508       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3509       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3510       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3511       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3512       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3513       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3514     } else { // Smirf-like\r
3515       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3516       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3517       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3518     }\r
3519     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3520       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3521       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3522       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3523     } else { // WinBoard standard\r
3524       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3525       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3526       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3527     }\r
3528   }\r
3529 \r
3530 \r
3531   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3532     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3533     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3534     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3535     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3536     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3537     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3538     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3539     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3540     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3541     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3542     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3543     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3544     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3545     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3546     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3547     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3548     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3549     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3550     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3551     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3552     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3553     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3554     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3555     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3556     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3557     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3558     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3559     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3560     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3561     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3562 \r
3563     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3564       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3565       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3566       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3567       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3568       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3569       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3570       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3571       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3572       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3573       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3574       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3575       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3576     } else {\r
3577       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3578       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3579       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3580       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3581       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3582       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3583       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3584       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3585       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3586       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3587       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3588       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3589     }\r
3590 \r
3591   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3592     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3593     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3594     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3595     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3596     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3597     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3598     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3599     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3600     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3601     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3602     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3603     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3604     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3605     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3606   }\r
3607 \r
3608 \r
3609   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3610   /* special Shogi support in this size */\r
3611   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3612       for (piece = WhitePawn;\r
3613            (int) piece < (int) BlackPawn;\r
3614            piece = (ChessSquare) ((int) piece + 1)) {\r
3615         if (pieceBitmap[i][piece] != NULL)\r
3616           DeleteObject(pieceBitmap[i][piece]);\r
3617       }\r
3618     }\r
3619   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3620   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3621   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3622   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3623   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3624   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3625   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3626   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3627   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3628   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3629   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3630   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3631   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3632   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3633   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3634   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3635   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3636   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3637   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3638   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3639   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3640   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3641   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3642   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3643   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3644   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3645   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3646   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3647   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3648   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3649   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3650   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3651   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3652   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3653   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3654   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3655   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3656   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3657   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3658   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3659   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3660   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3661   minorSize = 0;\r
3662   }\r
3663 }\r
3664 \r
3665 HBITMAP\r
3666 PieceBitmap(ChessSquare p, int kind)\r
3667 {\r
3668   if ((int) p >= (int) BlackPawn)\r
3669     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3670 \r
3671   return pieceBitmap[kind][(int) p];\r
3672 }\r
3673 \r
3674 /***************************************************************/\r
3675 \r
3676 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3677 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3678 /*\r
3679 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3680 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3681 */\r
3682 \r
3683 VOID\r
3684 SquareToPos(int row, int column, int * x, int * y)\r
3685 {\r
3686   if (flipView) {\r
3687     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3688     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3689   } else {\r
3690     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3691     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3692   }\r
3693 }\r
3694 \r
3695 VOID\r
3696 DrawCoordsOnDC(HDC hdc)\r
3697 {\r
3698   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
3699   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
3700   char str[2] = { NULLCHAR, NULLCHAR };\r
3701   int oldMode, oldAlign, x, y, start, i;\r
3702   HFONT oldFont;\r
3703   HBRUSH oldBrush;\r
3704 \r
3705   if (!appData.showCoords)\r
3706     return;\r
3707 \r
3708   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3709 \r
3710   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3711   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3712   oldAlign = GetTextAlign(hdc);\r
3713   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3714 \r
3715   y = boardRect.top + lineGap;\r
3716   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3717 \r
3718   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3719   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3720     str[0] = files[start + i];\r
3721     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3722     y += squareSize + lineGap;\r
3723   }\r
3724 \r
3725   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3726 \r
3727   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3728   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3729     str[0] = ranks[start + i];\r
3730     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3731     x += squareSize + lineGap;\r
3732   }    \r
3733 \r
3734   SelectObject(hdc, oldBrush);\r
3735   SetBkMode(hdc, oldMode);\r
3736   SetTextAlign(hdc, oldAlign);\r
3737   SelectObject(hdc, oldFont);\r
3738 }\r
3739 \r
3740 VOID\r
3741 DrawGridOnDC(HDC hdc)\r
3742 {\r
3743   HPEN oldPen;\r
3744  \r
3745   if (lineGap != 0) {\r
3746     oldPen = SelectObject(hdc, gridPen);\r
3747     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3748     SelectObject(hdc, oldPen);\r
3749   }\r
3750 }\r
3751 \r
3752 #define HIGHLIGHT_PEN 0\r
3753 #define PREMOVE_PEN   1\r
3754 \r
3755 VOID\r
3756 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3757 {\r
3758   int x1, y1;\r
3759   HPEN oldPen, hPen;\r
3760   if (lineGap == 0) return;\r
3761   if (flipView) {\r
3762     x1 = boardRect.left +\r
3763       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3764     y1 = boardRect.top +\r
3765       lineGap/2 + y * (squareSize + lineGap);\r
3766   } else {\r
3767     x1 = boardRect.left +\r
3768       lineGap/2 + x * (squareSize + lineGap);\r
3769     y1 = boardRect.top +\r
3770       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3771   }\r
3772   hPen = pen ? premovePen : highlightPen;\r
3773   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3774   MoveToEx(hdc, x1, y1, NULL);\r
3775   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3776   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3777   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3778   LineTo(hdc, x1, y1);\r
3779   SelectObject(hdc, oldPen);\r
3780 }\r
3781 \r
3782 VOID\r
3783 DrawHighlightsOnDC(HDC hdc)\r
3784 {\r
3785   int i;\r
3786   for (i=0; i<2; i++) {\r
3787     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3788       DrawHighlightOnDC(hdc, TRUE,\r
3789                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3790                         HIGHLIGHT_PEN);\r
3791   }\r
3792   for (i=0; i<2; i++) {\r
3793     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3794         premoveHighlightInfo.sq[i].y >= 0) {\r
3795         DrawHighlightOnDC(hdc, TRUE,\r
3796                           premoveHighlightInfo.sq[i].x, \r
3797                           premoveHighlightInfo.sq[i].y,\r
3798                           PREMOVE_PEN);\r
3799     }\r
3800   }\r
3801 }\r
3802 \r
3803 /* Note: sqcolor is used only in monoMode */\r
3804 /* Note that this code is largely duplicated in woptions.c,\r
3805    function DrawSampleSquare, so that needs to be updated too */\r
3806 VOID\r
3807 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3808 {\r
3809   HBITMAP oldBitmap;\r
3810   HBRUSH oldBrush;\r
3811   int tmpSize;\r
3812 \r
3813   if (appData.blindfold) return;\r
3814 \r
3815   /* [AS] Use font-based pieces if needed */\r
3816   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3817     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3818     CreatePiecesFromFont();\r
3819 \r
3820     if( fontBitmapSquareSize == squareSize ) {\r
3821         int index = TranslatePieceToFontPiece(piece);\r
3822 \r
3823         SelectObject( tmphdc, hPieceMask[ index ] );\r
3824 \r
3825         BitBlt( hdc,\r
3826             x, y,\r
3827             squareSize, squareSize,\r
3828             tmphdc,\r
3829             0, 0,\r
3830             SRCAND );\r
3831 \r
3832         SelectObject( tmphdc, hPieceFace[ index ] );\r
3833 \r
3834         BitBlt( hdc,\r
3835             x, y,\r
3836             squareSize, squareSize,\r
3837             tmphdc,\r
3838             0, 0,\r
3839             SRCPAINT );\r
3840 \r
3841         return;\r
3842     }\r
3843   }\r
3844 \r
3845   if (appData.monoMode) {\r
3846     SelectObject(tmphdc, PieceBitmap(piece, \r
3847       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3848     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3849            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3850   } else {\r
3851     tmpSize = squareSize;\r
3852     if(minorSize &&\r
3853         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3854          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3855       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3856       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3857       x += (squareSize - minorSize)>>1;\r
3858       y += squareSize - minorSize - 2;\r
3859       tmpSize = minorSize;\r
3860     }\r
3861     if (color || appData.allWhite ) {\r
3862       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3863       if( color )\r
3864               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3865       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3866       if(appData.upsideDown && color==flipView)\r
3867         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3868       else\r
3869         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3870 #if 0\r
3871       /* Use black piece color for outline of white pieces */\r
3872       /* Not sure this looks really good (though xboard does it).\r
3873          Maybe better to have another selectable color, default black */\r
3874       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3875       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3876       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3877 #else\r
3878       /* Use black for outline of white pieces */\r
3879       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3880       if(appData.upsideDown && color==flipView)\r
3881         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3882       else\r
3883         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3884 #endif\r
3885     } else {\r
3886 #if 0\r
3887       /* Use white piece color for details of black pieces */\r
3888       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3889          WHITE_PIECE ones aren't always the right shape. */\r
3890       /* Not sure this looks really good (though xboard does it).\r
3891          Maybe better to have another selectable color, default medium gray? */\r
3892       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3893       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3894       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3895       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3896       SelectObject(hdc, blackPieceBrush);\r
3897       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3898 #else\r
3899       /* Use square color for details of black pieces */\r
3900       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3901       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3902       if(appData.upsideDown && !flipView)\r
3903         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3904       else\r
3905         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3906 #endif\r
3907     }\r
3908     SelectObject(hdc, oldBrush);\r
3909     SelectObject(tmphdc, oldBitmap);\r
3910   }\r
3911 }\r
3912 \r
3913 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3914 int GetBackTextureMode( int algo )\r
3915 {\r
3916     int result = BACK_TEXTURE_MODE_DISABLED;\r
3917 \r
3918     switch( algo ) \r
3919     {\r
3920         case BACK_TEXTURE_MODE_PLAIN:\r
3921             result = 1; /* Always use identity map */\r
3922             break;\r
3923         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3924             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3925             break;\r
3926     }\r
3927 \r
3928     return result;\r
3929 }\r
3930 \r
3931 /* \r
3932     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3933     to handle redraws cleanly (as random numbers would always be different).\r
3934 */\r
3935 VOID RebuildTextureSquareInfo()\r
3936 {\r
3937     BITMAP bi;\r
3938     int lite_w = 0;\r
3939     int lite_h = 0;\r
3940     int dark_w = 0;\r
3941     int dark_h = 0;\r
3942     int row;\r
3943     int col;\r
3944 \r
3945     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3946 \r
3947     if( liteBackTexture != NULL ) {\r
3948         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3949             lite_w = bi.bmWidth;\r
3950             lite_h = bi.bmHeight;\r
3951         }\r
3952     }\r
3953 \r
3954     if( darkBackTexture != NULL ) {\r
3955         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3956             dark_w = bi.bmWidth;\r
3957             dark_h = bi.bmHeight;\r
3958         }\r
3959     }\r
3960 \r
3961     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3962         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3963             if( (col + row) & 1 ) {\r
3964                 /* Lite square */\r
3965                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3966                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3967                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3968                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3969                 }\r
3970             }\r
3971             else {\r
3972                 /* Dark square */\r
3973                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3974                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3975                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3976                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3977                 }\r
3978             }\r
3979         }\r
3980     }\r
3981 }\r
3982 \r
3983 /* [AS] Arrow highlighting support */\r
3984 \r
3985 static int A_WIDTH = 5; /* Width of arrow body */\r
3986 \r
3987 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3988 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3989 \r
3990 static double Sqr( double x )\r
3991 {\r
3992     return x*x;\r
3993 }\r
3994 \r
3995 static int Round( double x )\r
3996 {\r
3997     return (int) (x + 0.5);\r
3998 }\r
3999 \r
4000 /* Draw an arrow between two points using current settings */\r
4001 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
4002 {\r
4003     POINT arrow[7];\r
4004     double dx, dy, j, k, x, y;\r
4005 \r
4006     if( d_x == s_x ) {\r
4007         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4008 \r
4009         arrow[0].x = s_x + A_WIDTH;\r
4010         arrow[0].y = s_y;\r
4011 \r
4012         arrow[1].x = s_x + A_WIDTH;\r
4013         arrow[1].y = d_y - h;\r
4014 \r
4015         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
4016         arrow[2].y = d_y - h;\r
4017 \r
4018         arrow[3].x = d_x;\r
4019         arrow[3].y = d_y;\r
4020 \r
4021         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
4022         arrow[4].y = d_y - h;\r
4023 \r
4024         arrow[5].x = s_x - A_WIDTH;\r
4025         arrow[5].y = d_y - h;\r
4026 \r
4027         arrow[6].x = s_x - A_WIDTH;\r
4028         arrow[6].y = s_y;\r
4029     }\r
4030     else if( d_y == s_y ) {\r
4031         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4032 \r
4033         arrow[0].x = s_x;\r
4034         arrow[0].y = s_y + A_WIDTH;\r
4035 \r
4036         arrow[1].x = d_x - w;\r
4037         arrow[1].y = s_y + A_WIDTH;\r
4038 \r
4039         arrow[2].x = d_x - w;\r
4040         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
4041 \r
4042         arrow[3].x = d_x;\r
4043         arrow[3].y = d_y;\r
4044 \r
4045         arrow[4].x = d_x - w;\r
4046         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
4047 \r
4048         arrow[5].x = d_x - w;\r
4049         arrow[5].y = s_y - A_WIDTH;\r
4050 \r
4051         arrow[6].x = s_x;\r
4052         arrow[6].y = s_y - A_WIDTH;\r
4053     }\r
4054     else {\r
4055         /* [AS] Needed a lot of paper for this! :-) */\r
4056         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4057         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4058   \r
4059         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4060 \r
4061         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4062 \r
4063         x = s_x;\r
4064         y = s_y;\r
4065 \r
4066         arrow[0].x = Round(x - j);\r
4067         arrow[0].y = Round(y + j*dx);\r
4068 \r
4069         arrow[1].x = Round(x + j);\r
4070         arrow[1].y = Round(y - j*dx);\r
4071 \r
4072         if( d_x > s_x ) {\r
4073             x = (double) d_x - k;\r
4074             y = (double) d_y - k*dy;\r
4075         }\r
4076         else {\r
4077             x = (double) d_x + k;\r
4078             y = (double) d_y + k*dy;\r
4079         }\r
4080 \r
4081         arrow[2].x = Round(x + j);\r
4082         arrow[2].y = Round(y - j*dx);\r
4083 \r
4084         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4085         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4086 \r
4087         arrow[4].x = d_x;\r
4088         arrow[4].y = d_y;\r
4089 \r
4090         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4091         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4092 \r
4093         arrow[6].x = Round(x - j);\r
4094         arrow[6].y = Round(y + j*dx);\r
4095     }\r
4096 \r
4097     Polygon( hdc, arrow, 7 );\r
4098 }\r
4099 \r
4100 /* [AS] Draw an arrow between two squares */\r
4101 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4102 {\r
4103     int s_x, s_y, d_x, d_y;\r
4104     HPEN hpen;\r
4105     HPEN holdpen;\r
4106     HBRUSH hbrush;\r
4107     HBRUSH holdbrush;\r
4108     LOGBRUSH stLB;\r
4109 \r
4110     if( s_col == d_col && s_row == d_row ) {\r
4111         return;\r
4112     }\r
4113 \r
4114     /* Get source and destination points */\r
4115     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4116     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4117 \r
4118     if( d_y > s_y ) {\r
4119         d_y += squareSize / 4;\r
4120     }\r
4121     else if( d_y < s_y ) {\r
4122         d_y += 3 * squareSize / 4;\r
4123     }\r
4124     else {\r
4125         d_y += squareSize / 2;\r
4126     }\r
4127 \r
4128     if( d_x > s_x ) {\r
4129         d_x += squareSize / 4;\r
4130     }\r
4131     else if( d_x < s_x ) {\r
4132         d_x += 3 * squareSize / 4;\r
4133     }\r
4134     else {\r
4135         d_x += squareSize / 2;\r
4136     }\r
4137 \r
4138     s_x += squareSize / 2;\r
4139     s_y += squareSize / 2;\r
4140 \r
4141     /* Adjust width */\r
4142     A_WIDTH = squareSize / 14;\r
4143 \r
4144     /* Draw */\r
4145     stLB.lbStyle = BS_SOLID;\r
4146     stLB.lbColor = appData.highlightArrowColor;\r
4147     stLB.lbHatch = 0;\r
4148 \r
4149     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4150     holdpen = SelectObject( hdc, hpen );\r
4151     hbrush = CreateBrushIndirect( &stLB );\r
4152     holdbrush = SelectObject( hdc, hbrush );\r
4153 \r
4154     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4155 \r
4156     SelectObject( hdc, holdpen );\r
4157     SelectObject( hdc, holdbrush );\r
4158     DeleteObject( hpen );\r
4159     DeleteObject( hbrush );\r
4160 }\r
4161 \r
4162 BOOL HasHighlightInfo()\r
4163 {\r
4164     BOOL result = FALSE;\r
4165 \r
4166     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4167         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4168     {\r
4169         result = TRUE;\r
4170     }\r
4171 \r
4172     return result;\r
4173 }\r
4174 \r
4175 BOOL IsDrawArrowEnabled()\r
4176 {\r
4177     BOOL result = FALSE;\r
4178 \r
4179     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4180         result = TRUE;\r
4181     }\r
4182 \r
4183     return result;\r
4184 }\r
4185 \r
4186 VOID DrawArrowHighlight( HDC hdc )\r
4187 {\r
4188     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4189         DrawArrowBetweenSquares( hdc,\r
4190             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4191             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4192     }\r
4193 }\r
4194 \r
4195 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4196 {\r
4197     HRGN result = NULL;\r
4198 \r
4199     if( HasHighlightInfo() ) {\r
4200         int x1, y1, x2, y2;\r
4201         int sx, sy, dx, dy;\r
4202 \r
4203         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4204         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4205 \r
4206         sx = MIN( x1, x2 );\r
4207         sy = MIN( y1, y2 );\r
4208         dx = MAX( x1, x2 ) + squareSize;\r
4209         dy = MAX( y1, y2 ) + squareSize;\r
4210 \r
4211         result = CreateRectRgn( sx, sy, dx, dy );\r
4212     }\r
4213 \r
4214     return result;\r
4215 }\r
4216 \r
4217 /*\r
4218     Warning: this function modifies the behavior of several other functions. \r
4219     \r
4220     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4221     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4222     repaint is scattered all over the place, which is not good for features such as\r
4223     "arrow highlighting" that require a full repaint of the board.\r
4224 \r
4225     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4226     user interaction, when speed is not so important) but especially to avoid errors\r
4227     in the displayed graphics.\r
4228 \r
4229     In such patched places, I always try refer to this function so there is a single\r
4230     place to maintain knowledge.\r
4231     \r
4232     To restore the original behavior, just return FALSE unconditionally.\r
4233 */\r
4234 BOOL IsFullRepaintPreferrable()\r
4235 {\r
4236     BOOL result = FALSE;\r
4237 \r
4238     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4239         /* Arrow may appear on the board */\r
4240         result = TRUE;\r
4241     }\r
4242 \r
4243     return result;\r
4244 }\r
4245 \r
4246 /* \r
4247     This function is called by DrawPosition to know whether a full repaint must\r
4248     be forced or not.\r
4249 \r
4250     Only DrawPosition may directly call this function, which makes use of \r
4251     some state information. Other function should call DrawPosition specifying \r
4252     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4253 */\r
4254 BOOL DrawPositionNeedsFullRepaint()\r
4255 {\r
4256     BOOL result = FALSE;\r
4257 \r
4258     /* \r
4259         Probably a slightly better policy would be to trigger a full repaint\r
4260         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4261         but animation is fast enough that it's difficult to notice.\r
4262     */\r
4263     if( animInfo.piece == EmptySquare ) {\r
4264         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4265             result = TRUE;\r
4266         }\r
4267     }\r
4268 \r
4269     return result;\r
4270 }\r
4271 \r
4272 VOID\r
4273 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4274 {\r
4275   int row, column, x, y, square_color, piece_color;\r
4276   ChessSquare piece;\r
4277   HBRUSH oldBrush;\r
4278   HDC texture_hdc = NULL;\r
4279 \r
4280   /* [AS] Initialize background textures if needed */\r
4281   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4282       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4283       if( backTextureSquareSize != squareSize \r
4284        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4285           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4286           backTextureSquareSize = squareSize;\r
4287           RebuildTextureSquareInfo();\r
4288       }\r
4289 \r
4290       texture_hdc = CreateCompatibleDC( hdc );\r
4291   }\r
4292 \r
4293   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4294     for (column = 0; column < BOARD_WIDTH; column++) {\r
4295   \r
4296       SquareToPos(row, column, &x, &y);\r
4297 \r
4298       piece = board[row][column];\r
4299 \r
4300       square_color = ((column + row) % 2) == 1;\r
4301       if( gameInfo.variant == VariantXiangqi ) {\r
4302           square_color = !InPalace(row, column);\r
4303           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4304           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4305       }\r
4306       piece_color = (int) piece < (int) BlackPawn;\r
4307 \r
4308 \r
4309       /* [HGM] holdings file: light square or black */\r
4310       if(column == BOARD_LEFT-2) {\r
4311             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4312                 square_color = 1;\r
4313             else {\r
4314                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4315                 continue;\r
4316             }\r
4317       } else\r
4318       if(column == BOARD_RGHT + 1 ) {\r
4319             if( row < gameInfo.holdingsSize )\r
4320                 square_color = 1;\r
4321             else {\r
4322                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4323                 continue;\r
4324             }\r
4325       }\r
4326       if(column == BOARD_LEFT-1 ) /* left align */\r
4327             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4328       else if( column == BOARD_RGHT) /* right align */\r
4329             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4330       else\r
4331       if (appData.monoMode) {\r
4332         if (piece == EmptySquare) {\r
4333           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4334                  square_color ? WHITENESS : BLACKNESS);\r
4335         } else {\r
4336           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4337         }\r
4338       } \r
4339       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4340           /* [AS] Draw the square using a texture bitmap */\r
4341           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4342           int r = row, c = column; // [HGM] do not flip board in flipView\r
4343           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4344 \r
4345           DrawTile( x, y, \r
4346               squareSize, squareSize, \r
4347               hdc, \r
4348               texture_hdc,\r
4349               backTextureSquareInfo[r][c].mode,\r
4350               backTextureSquareInfo[r][c].x,\r
4351               backTextureSquareInfo[r][c].y );\r
4352 \r
4353           SelectObject( texture_hdc, hbm );\r
4354 \r
4355           if (piece != EmptySquare) {\r
4356               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4357           }\r
4358       }\r
4359       else {\r
4360         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4361 \r
4362         oldBrush = SelectObject(hdc, brush );\r
4363         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4364         SelectObject(hdc, oldBrush);\r
4365         if (piece != EmptySquare)\r
4366           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4367       }\r
4368     }\r
4369   }\r
4370 \r
4371   if( texture_hdc != NULL ) {\r
4372     DeleteDC( texture_hdc );\r
4373   }\r
4374 }\r
4375 \r
4376 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4377 void fputDW(FILE *f, int x)\r
4378 {\r
4379         fputc(x     & 255, f);\r
4380         fputc(x>>8  & 255, f);\r
4381         fputc(x>>16 & 255, f);\r
4382         fputc(x>>24 & 255, f);\r
4383 }\r
4384 \r
4385 #define MAX_CLIPS 200   /* more than enough */\r
4386 \r
4387 VOID\r
4388 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4389 {\r
4390 //  HBITMAP bufferBitmap;\r
4391   BITMAP bi;\r
4392 //  RECT Rect;\r
4393   HDC tmphdc;\r
4394   HBITMAP hbm;\r
4395   int w = 100, h = 50;\r
4396 \r
4397   if(logo == NULL) return;\r
4398 //  GetClientRect(hwndMain, &Rect);\r
4399 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4400 //                                      Rect.bottom-Rect.top+1);\r
4401   tmphdc = CreateCompatibleDC(hdc);\r
4402   hbm = SelectObject(tmphdc, logo);\r
4403   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4404             w = bi.bmWidth;\r
4405             h = bi.bmHeight;\r
4406   }\r
4407   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4408                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4409   SelectObject(tmphdc, hbm);\r
4410   DeleteDC(tmphdc);\r
4411 }\r
4412 \r
4413 VOID\r
4414 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4415 {\r
4416   static Board lastReq, lastDrawn;\r
4417   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4418   static int lastDrawnFlipView = 0;\r
4419   static int lastReqValid = 0, lastDrawnValid = 0;\r
4420   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4421   HDC tmphdc;\r
4422   HDC hdcmem;\r
4423   HBITMAP bufferBitmap;\r
4424   HBITMAP oldBitmap;\r
4425   RECT Rect;\r
4426   HRGN clips[MAX_CLIPS];\r
4427   ChessSquare dragged_piece = EmptySquare;\r
4428 \r
4429   /* I'm undecided on this - this function figures out whether a full\r
4430    * repaint is necessary on its own, so there's no real reason to have the\r
4431    * caller tell it that.  I think this can safely be set to FALSE - but\r
4432    * if we trust the callers not to request full repaints unnessesarily, then\r
4433    * we could skip some clipping work.  In other words, only request a full\r
4434    * redraw when the majority of pieces have changed positions (ie. flip, \r
4435    * gamestart and similar)  --Hawk\r
4436    */\r
4437   Boolean fullrepaint = repaint;\r
4438 \r
4439   if( DrawPositionNeedsFullRepaint() ) {\r
4440       fullrepaint = TRUE;\r
4441   }\r
4442 \r
4443 #if 0\r
4444   if( fullrepaint ) {\r
4445       static int repaint_count = 0;\r
4446       char buf[128];\r
4447 \r
4448       repaint_count++;\r
4449       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4450       OutputDebugString( buf );\r
4451   }\r
4452 #endif\r
4453 \r
4454   if (board == NULL) {\r
4455     if (!lastReqValid) {\r
4456       return;\r
4457     }\r
4458     board = lastReq;\r
4459   } else {\r
4460     CopyBoard(lastReq, board);\r
4461     lastReqValid = 1;\r
4462   }\r
4463 \r
4464   if (doingSizing) {\r
4465     return;\r
4466   }\r
4467 \r
4468   if (IsIconic(hwndMain)) {\r
4469     return;\r
4470   }\r
4471 \r
4472   if (hdc == NULL) {\r
4473     hdc = GetDC(hwndMain);\r
4474     if (!appData.monoMode) {\r
4475       SelectPalette(hdc, hPal, FALSE);\r
4476       RealizePalette(hdc);\r
4477     }\r
4478     releaseDC = TRUE;\r
4479   } else {\r
4480     releaseDC = FALSE;\r
4481   }\r
4482 \r
4483 #if 0\r
4484   fprintf(debugFP, "*******************************\n"\r
4485                    "repaint = %s\n"\r
4486                    "dragInfo.from (%d,%d)\n"\r
4487                    "dragInfo.start (%d,%d)\n"\r
4488                    "dragInfo.pos (%d,%d)\n"\r
4489                    "dragInfo.lastpos (%d,%d)\n", \r
4490                     repaint ? "TRUE" : "FALSE",\r
4491                     dragInfo.from.x, dragInfo.from.y, \r
4492                     dragInfo.start.x, dragInfo.start.y,\r
4493                     dragInfo.pos.x, dragInfo.pos.y,\r
4494                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4495   fprintf(debugFP, "prev:  ");\r
4496   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4497     for (column = 0; column < BOARD_WIDTH; column++) {\r
4498       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4499     }\r
4500   }\r
4501   fprintf(debugFP, "\n");\r
4502   fprintf(debugFP, "board: ");\r
4503   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4504     for (column = 0; column < BOARD_WIDTH; column++) {\r
4505       fprintf(debugFP, "%d ", board[row][column]);\r
4506     }\r
4507   }\r
4508   fprintf(debugFP, "\n");\r
4509   fflush(debugFP);\r
4510 #endif\r
4511 \r
4512   /* Create some work-DCs */\r
4513   hdcmem = CreateCompatibleDC(hdc);\r
4514   tmphdc = CreateCompatibleDC(hdc);\r
4515 \r
4516   /* If dragging is in progress, we temporarely remove the piece */\r
4517   /* [HGM] or temporarily decrease count if stacked              */\r
4518   /*       !! Moved to before board compare !!                   */\r
4519   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4520     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4521     if(dragInfo.from.x == BOARD_LEFT-2 ) {\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     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4526             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4527         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4528     } else \r
4529         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4530   }\r
4531 \r
4532   /* Figure out which squares need updating by comparing the \r
4533    * newest board with the last drawn board and checking if\r
4534    * flipping has changed.\r
4535    */\r
4536   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4537     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4538       for (column = 0; column < BOARD_WIDTH; column++) {\r
4539         if (lastDrawn[row][column] != board[row][column]) {\r
4540           SquareToPos(row, column, &x, &y);\r
4541           clips[num_clips++] =\r
4542             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4543         }\r
4544       }\r
4545     }\r
4546     for (i=0; i<2; i++) {\r
4547       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4548           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4549         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4550             lastDrawnHighlight.sq[i].y >= 0) {\r
4551           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4552                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4553           clips[num_clips++] =\r
4554             CreateRectRgn(x - lineGap, y - lineGap, \r
4555                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4556         }\r
4557         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4558           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4559           clips[num_clips++] =\r
4560             CreateRectRgn(x - lineGap, y - lineGap, \r
4561                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4562         }\r
4563       }\r
4564     }\r
4565     for (i=0; i<2; i++) {\r
4566       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4567           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4568         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4569             lastDrawnPremove.sq[i].y >= 0) {\r
4570           SquareToPos(lastDrawnPremove.sq[i].y,\r
4571                       lastDrawnPremove.sq[i].x, &x, &y);\r
4572           clips[num_clips++] =\r
4573             CreateRectRgn(x - lineGap, y - lineGap, \r
4574                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4575         }\r
4576         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4577             premoveHighlightInfo.sq[i].y >= 0) {\r
4578           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4579                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4580           clips[num_clips++] =\r
4581             CreateRectRgn(x - lineGap, y - lineGap, \r
4582                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4583         }\r
4584       }\r
4585     }\r
4586   } else {\r
4587     fullrepaint = TRUE;\r
4588   }\r
4589 \r
4590   /* Create a buffer bitmap - this is the actual bitmap\r
4591    * being written to.  When all the work is done, we can\r
4592    * copy it to the real DC (the screen).  This avoids\r
4593    * the problems with flickering.\r
4594    */\r
4595   GetClientRect(hwndMain, &Rect);\r
4596   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4597                                         Rect.bottom-Rect.top+1);\r
4598   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4599   if (!appData.monoMode) {\r
4600     SelectPalette(hdcmem, hPal, FALSE);\r
4601   }\r
4602 \r
4603   /* Create clips for dragging */\r
4604   if (!fullrepaint) {\r
4605     if (dragInfo.from.x >= 0) {\r
4606       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4607       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4608     }\r
4609     if (dragInfo.start.x >= 0) {\r
4610       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4611       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4612     }\r
4613     if (dragInfo.pos.x >= 0) {\r
4614       x = dragInfo.pos.x - squareSize / 2;\r
4615       y = dragInfo.pos.y - squareSize / 2;\r
4616       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4617     }\r
4618     if (dragInfo.lastpos.x >= 0) {\r
4619       x = dragInfo.lastpos.x - squareSize / 2;\r
4620       y = dragInfo.lastpos.y - squareSize / 2;\r
4621       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4622     }\r
4623   }\r
4624 \r
4625   /* Are we animating a move?  \r
4626    * If so, \r
4627    *   - remove the piece from the board (temporarely)\r
4628    *   - calculate the clipping region\r
4629    */\r
4630   if (!fullrepaint) {\r
4631     if (animInfo.piece != EmptySquare) {\r
4632       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4633       x = boardRect.left + animInfo.lastpos.x;\r
4634       y = boardRect.top + animInfo.lastpos.y;\r
4635       x2 = boardRect.left + animInfo.pos.x;\r
4636       y2 = boardRect.top + animInfo.pos.y;\r
4637       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4638       /* Slight kludge.  The real problem is that after AnimateMove is\r
4639          done, the position on the screen does not match lastDrawn.\r
4640          This currently causes trouble only on e.p. captures in\r
4641          atomic, where the piece moves to an empty square and then\r
4642          explodes.  The old and new positions both had an empty square\r
4643          at the destination, but animation has drawn a piece there and\r
4644          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4645       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4646     }\r
4647   }\r
4648 \r
4649   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4650   if (num_clips == 0)\r
4651     fullrepaint = TRUE;\r
4652 \r
4653   /* Set clipping on the memory DC */\r
4654   if (!fullrepaint) {\r
4655     SelectClipRgn(hdcmem, clips[0]);\r
4656     for (x = 1; x < num_clips; x++) {\r
4657       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4658         abort();  // this should never ever happen!\r
4659     }\r
4660   }\r
4661 \r
4662   /* Do all the drawing to the memory DC */\r
4663   if(explodeInfo.radius) { // [HGM] atomic\r
4664         HBRUSH oldBrush;\r
4665         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4666         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4667         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4668         x += squareSize/2;\r
4669         y += squareSize/2;\r
4670         if(!fullrepaint) {\r
4671           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4672           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4673         }\r
4674         DrawGridOnDC(hdcmem);\r
4675         DrawHighlightsOnDC(hdcmem);\r
4676         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4677         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4678         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4679         SelectObject(hdcmem, oldBrush);\r
4680   } else {\r
4681     DrawGridOnDC(hdcmem);\r
4682     DrawHighlightsOnDC(hdcmem);\r
4683     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4684   }\r
4685   if(logoHeight) {\r
4686         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4687         if(appData.autoLogo) {\r
4688           \r
4689           switch(gameMode) { // pick logos based on game mode\r
4690             case IcsObserving:\r
4691                 whiteLogo = second.programLogo; // ICS logo\r
4692                 blackLogo = second.programLogo;\r
4693             default:\r
4694                 break;\r
4695             case IcsPlayingWhite:\r
4696                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4697                 blackLogo = second.programLogo; // ICS logo\r
4698                 break;\r
4699             case IcsPlayingBlack:\r
4700                 whiteLogo = second.programLogo; // ICS logo\r
4701                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4702                 break;\r
4703             case TwoMachinesPlay:\r
4704                 if(first.twoMachinesColor[0] == 'b') {\r
4705                     whiteLogo = second.programLogo;\r
4706                     blackLogo = first.programLogo;\r
4707                 }\r
4708                 break;\r
4709             case MachinePlaysWhite:\r
4710                 blackLogo = userLogo;\r
4711                 break;\r
4712             case MachinePlaysBlack:\r
4713                 whiteLogo = userLogo;\r
4714                 blackLogo = first.programLogo;\r
4715           }\r
4716         }\r
4717         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4718         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4719   }\r
4720 \r
4721   if( appData.highlightMoveWithArrow ) {\r
4722     DrawArrowHighlight(hdcmem);\r
4723   }\r
4724 \r
4725   DrawCoordsOnDC(hdcmem);\r
4726 \r
4727   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4728                  /* to make sure lastDrawn contains what is actually drawn */\r
4729 \r
4730   /* Put the dragged piece back into place and draw it (out of place!) */\r
4731     if (dragged_piece != EmptySquare) {\r
4732     /* [HGM] or restack */\r
4733     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4734                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4735     else\r
4736     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4737                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4738     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4739     x = dragInfo.pos.x - squareSize / 2;\r
4740     y = dragInfo.pos.y - squareSize / 2;\r
4741     DrawPieceOnDC(hdcmem, dragged_piece,\r
4742                   ((int) dragged_piece < (int) BlackPawn), \r
4743                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4744   }   \r
4745   \r
4746   /* Put the animated piece back into place and draw it */\r
4747   if (animInfo.piece != EmptySquare) {\r
4748     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4749     x = boardRect.left + animInfo.pos.x;\r
4750     y = boardRect.top + animInfo.pos.y;\r
4751     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4752                   ((int) animInfo.piece < (int) BlackPawn),\r
4753                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4754   }\r
4755 \r
4756   /* Release the bufferBitmap by selecting in the old bitmap \r
4757    * and delete the memory DC\r
4758    */\r
4759   SelectObject(hdcmem, oldBitmap);\r
4760   DeleteDC(hdcmem);\r
4761 \r
4762   /* Set clipping on the target DC */\r
4763   if (!fullrepaint) {\r
4764     SelectClipRgn(hdc, clips[0]);\r
4765     for (x = 1; x < num_clips; x++) {\r
4766       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4767         abort();   // this should never ever happen!\r
4768     } \r
4769   }\r
4770 \r
4771   /* Copy the new bitmap onto the screen in one go.\r
4772    * This way we avoid any flickering\r
4773    */\r
4774   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4775   BitBlt(hdc, boardRect.left, boardRect.top,\r
4776          boardRect.right - boardRect.left,\r
4777          boardRect.bottom - boardRect.top,\r
4778          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4779   if(saveDiagFlag) { \r
4780     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4781     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4782 \r
4783     GetObject(bufferBitmap, sizeof(b), &b);\r
4784     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4785         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4786         bih.biWidth = b.bmWidth;\r
4787         bih.biHeight = b.bmHeight;\r
4788         bih.biPlanes = 1;\r
4789         bih.biBitCount = b.bmBitsPixel;\r
4790         bih.biCompression = 0;\r
4791         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4792         bih.biXPelsPerMeter = 0;\r
4793         bih.biYPelsPerMeter = 0;\r
4794         bih.biClrUsed = 0;\r
4795         bih.biClrImportant = 0;\r
4796 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4797 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4798         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4799 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4800 \r
4801 #if 1\r
4802         wb = b.bmWidthBytes;\r
4803         // count colors\r
4804         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4805                 int k = ((int*) pData)[i];\r
4806                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4807                 if(j >= 16) break;\r
4808                 color[j] = k;\r
4809                 if(j >= nrColors) nrColors = j+1;\r
4810         }\r
4811         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4812                 INT p = 0;\r
4813                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4814                     for(w=0; w<(wb>>2); w+=2) {\r
4815                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4816                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4817                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4818                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4819                         pData[p++] = m | j<<4;\r
4820                     }\r
4821                     while(p&3) pData[p++] = 0;\r
4822                 }\r
4823                 fac = 3;\r
4824                 wb = ((wb+31)>>5)<<2;\r
4825         }\r
4826         // write BITMAPFILEHEADER\r
4827         fprintf(diagFile, "BM");\r
4828         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4829         fputDW(diagFile, 0);\r
4830         fputDW(diagFile, 0x36 + (fac?64:0));\r
4831         // write BITMAPINFOHEADER\r
4832         fputDW(diagFile, 40);\r
4833         fputDW(diagFile, b.bmWidth);\r
4834         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4835         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4836         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4837         fputDW(diagFile, 0);\r
4838         fputDW(diagFile, 0);\r
4839         fputDW(diagFile, 0);\r
4840         fputDW(diagFile, 0);\r
4841         fputDW(diagFile, 0);\r
4842         fputDW(diagFile, 0);\r
4843         // write color table\r
4844         if(fac)\r
4845         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4846         // write bitmap data\r
4847         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4848                 fputc(pData[i], diagFile);\r
4849 #endif\r
4850      }\r
4851   }\r
4852 \r
4853   SelectObject(tmphdc, oldBitmap);\r
4854 \r
4855   /* Massive cleanup */\r
4856   for (x = 0; x < num_clips; x++)\r
4857     DeleteObject(clips[x]);\r
4858 \r
4859   DeleteDC(tmphdc);\r
4860   DeleteObject(bufferBitmap);\r
4861 \r
4862   if (releaseDC) \r
4863     ReleaseDC(hwndMain, hdc);\r
4864   \r
4865   if (lastDrawnFlipView != flipView) {\r
4866     if (flipView)\r
4867       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4868     else\r
4869       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4870   }\r
4871 \r
4872 /*  CopyBoard(lastDrawn, board);*/\r
4873   lastDrawnHighlight = highlightInfo;\r
4874   lastDrawnPremove   = premoveHighlightInfo;\r
4875   lastDrawnFlipView = flipView;\r
4876   lastDrawnValid = 1;\r
4877 }\r
4878 \r
4879 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4880 int\r
4881 SaveDiagram(f)\r
4882      FILE *f;\r
4883 {\r
4884     saveDiagFlag = 1; diagFile = f;\r
4885     HDCDrawPosition(NULL, TRUE, NULL);\r
4886 \r
4887     saveDiagFlag = 0;\r
4888 \r
4889 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4890     \r
4891     fclose(f);\r
4892     return TRUE;\r
4893 }\r
4894 \r
4895 \r
4896 /*---------------------------------------------------------------------------*\\r
4897 | CLIENT PAINT PROCEDURE\r
4898 |   This is the main event-handler for the WM_PAINT message.\r
4899 |\r
4900 \*---------------------------------------------------------------------------*/\r
4901 VOID\r
4902 PaintProc(HWND hwnd)\r
4903 {\r
4904   HDC         hdc;\r
4905   PAINTSTRUCT ps;\r
4906   HFONT       oldFont;\r
4907 \r
4908   if((hdc = BeginPaint(hwnd, &ps))) {\r
4909     if (IsIconic(hwnd)) {\r
4910       DrawIcon(hdc, 2, 2, iconCurrent);\r
4911     } else {\r
4912       if (!appData.monoMode) {\r
4913         SelectPalette(hdc, hPal, FALSE);\r
4914         RealizePalette(hdc);\r
4915       }\r
4916       HDCDrawPosition(hdc, 1, NULL);\r
4917       oldFont =\r
4918         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4919       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4920                  ETO_CLIPPED|ETO_OPAQUE,\r
4921                  &messageRect, messageText, strlen(messageText), NULL);\r
4922       SelectObject(hdc, oldFont);\r
4923       DisplayBothClocks();\r
4924     }\r
4925     EndPaint(hwnd,&ps);\r
4926   }\r
4927 \r
4928   return;\r
4929 }\r
4930 \r
4931 \r
4932 /*\r
4933  * If the user selects on a border boundary, return -1; if off the board,\r
4934  *   return -2.  Otherwise map the event coordinate to the square.\r
4935  * The offset boardRect.left or boardRect.top must already have been\r
4936  *   subtracted from x.\r
4937  */\r
4938 int\r
4939 EventToSquare(int x)\r
4940 {\r
4941   if (x <= 0)\r
4942     return -2;\r
4943   if (x < lineGap)\r
4944     return -1;\r
4945   x -= lineGap;\r
4946   if ((x % (squareSize + lineGap)) >= squareSize)\r
4947     return -1;\r
4948   x /= (squareSize + lineGap);\r
4949   if (x >= BOARD_SIZE)\r
4950     return -2;\r
4951   return x;\r
4952 }\r
4953 \r
4954 typedef struct {\r
4955   char piece;\r
4956   int command;\r
4957   char* name;\r
4958 } DropEnable;\r
4959 \r
4960 DropEnable dropEnables[] = {\r
4961   { 'P', DP_Pawn, "Pawn" },\r
4962   { 'N', DP_Knight, "Knight" },\r
4963   { 'B', DP_Bishop, "Bishop" },\r
4964   { 'R', DP_Rook, "Rook" },\r
4965   { 'Q', DP_Queen, "Queen" },\r
4966 };\r
4967 \r
4968 VOID\r
4969 SetupDropMenu(HMENU hmenu)\r
4970 {\r
4971   int i, count, enable;\r
4972   char *p;\r
4973   extern char white_holding[], black_holding[];\r
4974   char item[MSG_SIZ];\r
4975 \r
4976   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4977     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4978                dropEnables[i].piece);\r
4979     count = 0;\r
4980     while (p && *p++ == dropEnables[i].piece) count++;\r
4981     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4982     enable = count > 0 || !appData.testLegality\r
4983       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4984                       && !appData.icsActive);\r
4985     ModifyMenu(hmenu, dropEnables[i].command,\r
4986                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4987                dropEnables[i].command, item);\r
4988   }\r
4989 }\r
4990 \r
4991 /* Event handler for mouse messages */\r
4992 VOID\r
4993 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4994 {\r
4995   int x, y;\r
4996   POINT pt;\r
4997   static int recursive = 0;\r
4998   HMENU hmenu;\r
4999 //  BOOLEAN needsRedraw = FALSE;\r
5000   BOOLEAN saveAnimate;\r
5001   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
5002   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
5003   ChessMove moveType;\r
5004 \r
5005   if (recursive) {\r
5006     if (message == WM_MBUTTONUP) {\r
5007       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
5008          to the middle button: we simulate pressing the left button too!\r
5009          */\r
5010       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
5011       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
5012     }\r
5013     return;\r
5014   }\r
5015   recursive++;\r
5016   \r
5017   pt.x = LOWORD(lParam);\r
5018   pt.y = HIWORD(lParam);\r
5019   x = EventToSquare(pt.x - boardRect.left);\r
5020   y = EventToSquare(pt.y - boardRect.top);\r
5021   if (!flipView && y >= 0) {\r
5022     y = BOARD_HEIGHT - 1 - y;\r
5023   }\r
5024   if (flipView && x >= 0) {\r
5025     x = BOARD_WIDTH - 1 - x;\r
5026   }\r
5027 \r
5028   switch (message) {\r
5029   case WM_LBUTTONDOWN:\r
5030     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
5031         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
5032         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
5033         if(gameInfo.holdingsWidth && \r
5034                 (WhiteOnMove(currentMove) \r
5035                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
5036                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
5037             // click in right holdings, for determining promotion piece\r
5038             ChessSquare p = boards[currentMove][y][x];\r
5039             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
5040             if(p != EmptySquare) {\r
5041                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
5042                 fromX = fromY = -1;\r
5043                 break;\r
5044             }\r
5045         }\r
5046         DrawPosition(FALSE, boards[currentMove]);\r
5047         break;\r
5048     }\r
5049     ErrorPopDown();\r
5050     sameAgain = FALSE;\r
5051     if (y == -2) {\r
5052       /* Downclick vertically off board; check if on clock */\r
5053       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5054         if (gameMode == EditPosition) {\r
5055           SetWhiteToPlayEvent();\r
5056         } else if (gameMode == IcsPlayingBlack ||\r
5057                    gameMode == MachinePlaysWhite) {\r
5058           CallFlagEvent();\r
5059         } else if (gameMode == EditGame) {\r
5060           AdjustClock(flipClock, -1);\r
5061         }\r
5062       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5063         if (gameMode == EditPosition) {\r
5064           SetBlackToPlayEvent();\r
5065         } else if (gameMode == IcsPlayingWhite ||\r
5066                    gameMode == MachinePlaysBlack) {\r
5067           CallFlagEvent();\r
5068         } else if (gameMode == EditGame) {\r
5069           AdjustClock(!flipClock, -1);\r
5070         }\r
5071       }\r
5072       if (!appData.highlightLastMove) {\r
5073         ClearHighlights();\r
5074         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
5075       }\r
5076       fromX = fromY = -1;\r
5077       dragInfo.start.x = dragInfo.start.y = -1;\r
5078       dragInfo.from = dragInfo.start;\r
5079       break;\r
5080     } else if (x < 0 || y < 0\r
5081       /* [HGM] block clicks between board and holdings */\r
5082               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
5083               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
5084               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
5085         /* EditPosition, empty square, or different color piece;\r
5086            click-click move is possible */\r
5087                                ) {\r
5088       break;\r
5089     } else if (fromX == x && fromY == y) {\r
5090       /* Downclick on same square again */\r
5091       ClearHighlights();\r
5092       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5093       sameAgain = TRUE;  \r
5094     } else if (fromX != -1 &&\r
5095                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5096                                                                         ) {\r
5097       /* Downclick on different square. */\r
5098       /* [HGM] if on holdings file, should count as new first click ! */\r
5099       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5100         toX = x;\r
5101         toY = y;\r
5102         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5103            to make sure move is legal before showing promotion popup */\r
5104         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5105         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5106                 fromX = fromY = -1; \r
5107                 ClearHighlights();\r
5108                 DrawPosition(FALSE, boards[currentMove]);\r
5109                 break; \r
5110         } else \r
5111         if(moveType != ImpossibleMove) {\r
5112           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5113           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5114             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5115               appData.alwaysPromoteToQueen)) {\r
5116                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5117                   if (!appData.highlightLastMove) {\r
5118                       ClearHighlights();\r
5119                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5120                   }\r
5121           } else\r
5122           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5123                   SetHighlights(fromX, fromY, toX, toY);\r
5124                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5125                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5126                      If promotion to Q is legal, all are legal! */\r
5127                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5128                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5129                     // kludge to temporarily execute move on display, wthout promotng yet\r
5130                     promotionChoice = TRUE;\r
5131                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5132                     boards[currentMove][toY][toX] = p;\r
5133                     DrawPosition(FALSE, boards[currentMove]);\r
5134                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5135                     boards[currentMove][toY][toX] = q;\r
5136                   } else\r
5137                   PromotionPopup(hwnd);\r
5138           } else {       /* not a promotion */\r
5139              if (appData.animate || appData.highlightLastMove) {\r
5140                  SetHighlights(fromX, fromY, toX, toY);\r
5141              } else {\r
5142                  ClearHighlights();\r
5143              }\r
5144              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5145              fromX = fromY = -1;\r
5146              if (appData.animate && !appData.highlightLastMove) {\r
5147                   ClearHighlights();\r
5148                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5149              }\r
5150           }\r
5151           break;\r
5152         }\r
5153         if (gotPremove) {\r
5154             /* [HGM] it seemed that braces were missing here */\r
5155             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5156             fromX = fromY = -1;\r
5157             break;\r
5158         }\r
5159       }\r
5160       ClearHighlights();\r
5161       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5162     }\r
5163     /* First downclick, or restart on a square with same color piece */\r
5164     if (!frozen && OKToStartUserMove(x, y)) {\r
5165       fromX = x;\r
5166       fromY = y;\r
5167       dragInfo.lastpos = pt;\r
5168       dragInfo.from.x = fromX;\r
5169       dragInfo.from.y = fromY;\r
5170       dragInfo.start = dragInfo.from;\r
5171       SetCapture(hwndMain);\r
5172     } else {\r
5173       fromX = fromY = -1;\r
5174       dragInfo.start.x = dragInfo.start.y = -1;\r
5175       dragInfo.from = dragInfo.start;\r
5176       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5177     }\r
5178     break;\r
5179 \r
5180   case WM_LBUTTONUP:\r
5181     ReleaseCapture();\r
5182     if (fromX == -1) break;\r
5183     if (x == fromX && y == fromY) {\r
5184       dragInfo.from.x = dragInfo.from.y = -1;\r
5185       /* Upclick on same square */\r
5186       if (sameAgain) {\r
5187         /* Clicked same square twice: abort click-click move */\r
5188         fromX = fromY = -1;\r
5189         gotPremove = 0;\r
5190         ClearPremoveHighlights();\r
5191       } else {\r
5192         /* First square clicked: start click-click move */\r
5193         SetHighlights(fromX, fromY, -1, -1);\r
5194       }\r
5195       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5196     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5197       /* Errant click; ignore */\r
5198       break;\r
5199     } else {\r
5200       /* Finish drag move. */\r
5201     if (appData.debugMode) {\r
5202         fprintf(debugFP, "release\n");\r
5203     }\r
5204       dragInfo.from.x = dragInfo.from.y = -1;\r
5205       toX = x;\r
5206       toY = y;\r
5207       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5208       appData.animate = appData.animate && !appData.animateDragging;\r
5209       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5210       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5211                 fromX = fromY = -1; \r
5212                 ClearHighlights();\r
5213                 DrawPosition(FALSE, boards[currentMove]);\r
5214                 appData.animate = saveAnimate;\r
5215                 break; \r
5216       } else \r
5217       if(moveType != ImpossibleMove) {\r
5218           /* [HGM] use move type to determine if move is promotion.\r
5219              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5220           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5221             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5222               appData.alwaysPromoteToQueen)) \r
5223                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5224           else \r
5225           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5226                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5227                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5228                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5229                     // kludge to temporarily execute move on display, wthout promotng yet\r
5230                     promotionChoice = TRUE;\r
5231                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5232                     boards[currentMove][toY][toX] = p;\r
5233                     DrawPosition(FALSE, boards[currentMove]);\r
5234                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5235                     boards[currentMove][toY][toX] = q;\r
5236                     appData.animate = saveAnimate;\r
5237                     break;\r
5238                   } else\r
5239                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5240           } else {\r
5241             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5242                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5243                                         moveType == WhiteCapturesEnPassant || \r
5244                                         moveType == BlackCapturesEnPassant   ) )\r
5245                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5246             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5247           }\r
5248       }\r
5249       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5250       appData.animate = saveAnimate;\r
5251       fromX = fromY = -1;\r
5252       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5253         ClearHighlights();\r
5254       }\r
5255       if (appData.animate || appData.animateDragging ||\r
5256           appData.highlightDragging || gotPremove) {\r
5257         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5258       }\r
5259     }\r
5260     dragInfo.start.x = dragInfo.start.y = -1; \r
5261     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5262     break;\r
5263 \r
5264   case WM_MOUSEMOVE:\r
5265     if ((appData.animateDragging || appData.highlightDragging)\r
5266         && (wParam & MK_LBUTTON)\r
5267         && dragInfo.from.x >= 0) \r
5268     {\r
5269       BOOL full_repaint = FALSE;\r
5270 \r
5271       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5272       if (appData.animateDragging) {\r
5273         dragInfo.pos = pt;\r
5274       }\r
5275       if (appData.highlightDragging) {\r
5276         SetHighlights(fromX, fromY, x, y);\r
5277         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5278             full_repaint = TRUE;\r
5279         }\r
5280       }\r
5281       \r
5282       DrawPosition( full_repaint, NULL);\r
5283       \r
5284       dragInfo.lastpos = dragInfo.pos;\r
5285     }\r
5286     break;\r
5287 \r
5288   case WM_MOUSEWHEEL: // [DM]\r
5289     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5290        /* Mouse Wheel is being rolled forward\r
5291         * Play moves forward\r
5292         */\r
5293        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5294                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5295        /* Mouse Wheel is being rolled backward\r
5296         * Play moves backward\r
5297         */\r
5298        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5299                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5300     }\r
5301     break;\r
5302 \r
5303   case WM_MBUTTONDOWN:\r
5304   case WM_RBUTTONDOWN:\r
5305     ErrorPopDown();\r
5306     ReleaseCapture();\r
5307     fromX = fromY = -1;\r
5308     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5309     dragInfo.start.x = dragInfo.start.y = -1;\r
5310     dragInfo.from = dragInfo.start;\r
5311     dragInfo.lastpos = dragInfo.pos;\r
5312     if (appData.highlightDragging) {\r
5313       ClearHighlights();\r
5314     }\r
5315     if(y == -2) {\r
5316       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5317       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5318           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5319       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5320           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5321       }\r
5322     }\r
5323     DrawPosition(TRUE, NULL);\r
5324 \r
5325     switch (gameMode) {\r
5326     case EditPosition:\r
5327     case IcsExamining:\r
5328       if (x < 0 || y < 0) break;\r
5329       fromX = x;\r
5330       fromY = y;\r
5331       if (message == WM_MBUTTONDOWN) {\r
5332         buttonCount = 3;  /* even if system didn't think so */\r
5333         if (wParam & MK_SHIFT) \r
5334           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5335         else\r
5336           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5337       } else { /* message == WM_RBUTTONDOWN */\r
5338 #if 0\r
5339         if (buttonCount == 3) {\r
5340           if (wParam & MK_SHIFT) \r
5341             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5342           else\r
5343             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5344         } else {\r
5345           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5346         }\r
5347 #else\r
5348         /* Just have one menu, on the right button.  Windows users don't\r
5349            think to try the middle one, and sometimes other software steals\r
5350            it, or it doesn't really exist. */\r
5351         if(gameInfo.variant != VariantShogi)\r
5352             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5353         else\r
5354             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5355 #endif\r
5356       }\r
5357       break;\r
5358     case IcsPlayingWhite:\r
5359     case IcsPlayingBlack:\r
5360     case EditGame:\r
5361     case MachinePlaysWhite:\r
5362     case MachinePlaysBlack:\r
5363       if (appData.testLegality &&\r
5364           gameInfo.variant != VariantBughouse &&\r
5365           gameInfo.variant != VariantCrazyhouse) break;\r
5366       if (x < 0 || y < 0) break;\r
5367       fromX = x;\r
5368       fromY = y;\r
5369       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5370       SetupDropMenu(hmenu);\r
5371       MenuPopup(hwnd, pt, hmenu, -1);\r
5372       break;\r
5373     default:\r
5374       break;\r
5375     }\r
5376     break;\r
5377   }\r
5378 \r
5379   recursive--;\r
5380 }\r
5381 \r
5382 /* Preprocess messages for buttons in main window */\r
5383 LRESULT CALLBACK\r
5384 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5385 {\r
5386   int id = GetWindowLong(hwnd, GWL_ID);\r
5387   int i, dir;\r
5388 \r
5389   for (i=0; i<N_BUTTONS; i++) {\r
5390     if (buttonDesc[i].id == id) break;\r
5391   }\r
5392   if (i == N_BUTTONS) return 0;\r
5393   switch (message) {\r
5394   case WM_KEYDOWN:\r
5395     switch (wParam) {\r
5396     case VK_LEFT:\r
5397     case VK_RIGHT:\r
5398       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5399       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5400       return TRUE;\r
5401     }\r
5402     break;\r
5403   case WM_CHAR:\r
5404     switch (wParam) {\r
5405     case '\r':\r
5406       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5407       return TRUE;\r
5408     default:\r
5409       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5410         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5411         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5412         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5413         SetFocus(h);\r
5414         SendMessage(h, WM_CHAR, wParam, lParam);\r
5415         return TRUE;\r
5416       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5417         PopUpMoveDialog((char)wParam);\r
5418       }\r
5419       break;\r
5420     }\r
5421     break;\r
5422   }\r
5423   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5424 }\r
5425 \r
5426 /* Process messages for Promotion dialog box */\r
5427 LRESULT CALLBACK\r
5428 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5429 {\r
5430   char promoChar;\r
5431 \r
5432   switch (message) {\r
5433   case WM_INITDIALOG: /* message: initialize dialog box */\r
5434     /* Center the dialog over the application window */\r
5435     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5436     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5437       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5438        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5439                SW_SHOW : SW_HIDE);\r
5440     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5441     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5442        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5443          PieceToChar(WhiteAngel) != '~') ||\r
5444         (PieceToChar(BlackAngel) >= 'A' &&\r
5445          PieceToChar(BlackAngel) != '~')   ) ?\r
5446                SW_SHOW : SW_HIDE);\r
5447     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5448        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5449          PieceToChar(WhiteMarshall) != '~') ||\r
5450         (PieceToChar(BlackMarshall) >= 'A' &&\r
5451          PieceToChar(BlackMarshall) != '~')   ) ?\r
5452                SW_SHOW : SW_HIDE);\r
5453     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5454     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5455        gameInfo.variant != VariantShogi ?\r
5456                SW_SHOW : SW_HIDE);\r
5457     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5458        gameInfo.variant != VariantShogi ?\r
5459                SW_SHOW : SW_HIDE);\r
5460     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5461        gameInfo.variant == VariantShogi ?\r
5462                SW_SHOW : SW_HIDE);\r
5463     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5464        gameInfo.variant == VariantShogi ?\r
5465                SW_SHOW : SW_HIDE);\r
5466     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5467        gameInfo.variant == VariantSuper ?\r
5468                SW_SHOW : SW_HIDE);\r
5469     return TRUE;\r
5470 \r
5471   case WM_COMMAND: /* message: received a command */\r
5472     switch (LOWORD(wParam)) {\r
5473     case IDCANCEL:\r
5474       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5475       ClearHighlights();\r
5476       DrawPosition(FALSE, NULL);\r
5477       return TRUE;\r
5478     case PB_King:\r
5479       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5480       break;\r
5481     case PB_Queen:\r
5482       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5483       break;\r
5484     case PB_Rook:\r
5485       promoChar = PieceToChar(BlackRook);\r
5486       break;\r
5487     case PB_Bishop:\r
5488       promoChar = PieceToChar(BlackBishop);\r
5489       break;\r
5490     case PB_Chancellor:\r
5491       promoChar = PieceToChar(BlackMarshall);\r
5492       break;\r
5493     case PB_Archbishop:\r
5494       promoChar = PieceToChar(BlackAngel);\r
5495       break;\r
5496     case PB_Knight:\r
5497       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5498       break;\r
5499     default:\r
5500       return FALSE;\r
5501     }\r
5502     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5503     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5504        only show the popup when we are already sure the move is valid or\r
5505        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5506        will figure out it is a promotion from the promoChar. */\r
5507     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5508     if (!appData.highlightLastMove) {\r
5509       ClearHighlights();\r
5510       DrawPosition(FALSE, NULL);\r
5511     }\r
5512     return TRUE;\r
5513   }\r
5514   return FALSE;\r
5515 }\r
5516 \r
5517 /* Pop up promotion dialog */\r
5518 VOID\r
5519 PromotionPopup(HWND hwnd)\r
5520 {\r
5521   FARPROC lpProc;\r
5522 \r
5523   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5524   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5525     hwnd, (DLGPROC)lpProc);\r
5526   FreeProcInstance(lpProc);\r
5527 }\r
5528 \r
5529 /* Toggle ShowThinking */\r
5530 VOID\r
5531 ToggleShowThinking()\r
5532 {\r
5533   appData.showThinking = !appData.showThinking;\r
5534   ShowThinkingEvent();\r
5535 }\r
5536 \r
5537 VOID\r
5538 LoadGameDialog(HWND hwnd, char* title)\r
5539 {\r
5540   UINT number = 0;\r
5541   FILE *f;\r
5542   char fileTitle[MSG_SIZ];\r
5543   f = OpenFileDialog(hwnd, "rb", "",\r
5544                      appData.oldSaveStyle ? "gam" : "pgn",\r
5545                      GAME_FILT,\r
5546                      title, &number, fileTitle, NULL);\r
5547   if (f != NULL) {\r
5548     cmailMsgLoaded = FALSE;\r
5549     if (number == 0) {\r
5550       int error = GameListBuild(f);\r
5551       if (error) {\r
5552         DisplayError("Cannot build game list", error);\r
5553       } else if (!ListEmpty(&gameList) &&\r
5554                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5555         GameListPopUp(f, fileTitle);\r
5556         return;\r
5557       }\r
5558       GameListDestroy();\r
5559       number = 1;\r
5560     }\r
5561     LoadGame(f, number, fileTitle, FALSE);\r
5562   }\r
5563 }\r
5564 \r
5565 VOID\r
5566 ChangedConsoleFont()\r
5567 {\r
5568   CHARFORMAT cfmt;\r
5569   CHARRANGE tmpsel, sel;\r
5570   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5571   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5572   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5573   PARAFORMAT paraf;\r
5574 \r
5575   cfmt.cbSize = sizeof(CHARFORMAT);\r
5576   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5577   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5578   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5579    * size.  This was undocumented in the version of MSVC++ that I had\r
5580    * when I wrote the code, but is apparently documented now.\r
5581    */\r
5582   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5583   cfmt.bCharSet = f->lf.lfCharSet;\r
5584   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5585   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5586   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5587   /* Why are the following seemingly needed too? */\r
5588   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5589   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5590   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5591   tmpsel.cpMin = 0;\r
5592   tmpsel.cpMax = -1; /*999999?*/\r
5593   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5594   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5595   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5596    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5597    */\r
5598   paraf.cbSize = sizeof(paraf);\r
5599   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5600   paraf.dxStartIndent = 0;\r
5601   paraf.dxOffset = WRAP_INDENT;\r
5602   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5603   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5604 }\r
5605 \r
5606 /*---------------------------------------------------------------------------*\\r
5607  *\r
5608  * Window Proc for main window\r
5609  *\r
5610 \*---------------------------------------------------------------------------*/\r
5611 \r
5612 /* Process messages for main window, etc. */\r
5613 LRESULT CALLBACK\r
5614 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5615 {\r
5616   FARPROC lpProc;\r
5617   int wmId, wmEvent;\r
5618   char *defName;\r
5619   FILE *f;\r
5620   UINT number;\r
5621   char fileTitle[MSG_SIZ];\r
5622   char buf[MSG_SIZ];\r
5623   static SnapData sd;\r
5624 \r
5625   switch (message) {\r
5626 \r
5627   case WM_PAINT: /* message: repaint portion of window */\r
5628     PaintProc(hwnd);\r
5629     break;\r
5630 \r
5631   case WM_ERASEBKGND:\r
5632     if (IsIconic(hwnd)) {\r
5633       /* Cheat; change the message */\r
5634       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5635     } else {\r
5636       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5637     }\r
5638     break;\r
5639 \r
5640   case WM_LBUTTONDOWN:\r
5641   case WM_MBUTTONDOWN:\r
5642   case WM_RBUTTONDOWN:\r
5643   case WM_LBUTTONUP:\r
5644   case WM_MBUTTONUP:\r
5645   case WM_RBUTTONUP:\r
5646   case WM_MOUSEMOVE:\r
5647   case WM_MOUSEWHEEL:\r
5648     MouseEvent(hwnd, message, wParam, lParam);\r
5649     break;\r
5650 \r
5651   JAWS_KB_NAVIGATION\r
5652 \r
5653   case WM_CHAR:\r
5654     \r
5655     JAWS_ALT_INTERCEPT\r
5656 \r
5657     if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) { \r
5658         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5659         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5660         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5661         SetFocus(h);\r
5662         SendMessage(h, message, wParam, lParam);\r
5663     } else if(lParam != KF_REPEAT) {\r
5664         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5665                 PopUpMoveDialog((char)wParam);\r
5666         }\r
5667     }\r
5668 \r
5669     break;\r
5670 \r
5671   case WM_PALETTECHANGED:\r
5672     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5673       int nnew;\r
5674       HDC hdc = GetDC(hwndMain);\r
5675       SelectPalette(hdc, hPal, TRUE);\r
5676       nnew = RealizePalette(hdc);\r
5677       if (nnew > 0) {\r
5678         paletteChanged = TRUE;\r
5679 #if 0\r
5680         UpdateColors(hdc);\r
5681 #else\r
5682         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5683 #endif\r
5684       }\r
5685       ReleaseDC(hwnd, hdc);\r
5686     }\r
5687     break;\r
5688 \r
5689   case WM_QUERYNEWPALETTE:\r
5690     if (!appData.monoMode /*&& paletteChanged*/) {\r
5691       int nnew;\r
5692       HDC hdc = GetDC(hwndMain);\r
5693       paletteChanged = FALSE;\r
5694       SelectPalette(hdc, hPal, FALSE);\r
5695       nnew = RealizePalette(hdc);\r
5696       if (nnew > 0) {\r
5697         InvalidateRect(hwnd, &boardRect, FALSE);\r
5698       }\r
5699       ReleaseDC(hwnd, hdc);\r
5700       return TRUE;\r
5701     }\r
5702     return FALSE;\r
5703 \r
5704   case WM_COMMAND: /* message: command from application menu */\r
5705     wmId    = LOWORD(wParam);\r
5706     wmEvent = HIWORD(wParam);\r
5707 \r
5708     switch (wmId) {\r
5709     case IDM_NewGame:\r
5710       ResetGameEvent();\r
5711       AnalysisPopDown();\r
5712       SAY("new game enter a move to play against the computer with white");\r
5713       break;\r
5714 \r
5715     case IDM_NewGameFRC:\r
5716       if( NewGameFRC() == 0 ) {\r
5717         ResetGameEvent();\r
5718         AnalysisPopDown();\r
5719       }\r
5720       break;\r
5721 \r
5722     case IDM_NewVariant:\r
5723       NewVariantPopup(hwnd);\r
5724       break;\r
5725 \r
5726     case IDM_LoadGame:\r
5727       LoadGameDialog(hwnd, "Load Game from File");\r
5728       break;\r
5729 \r
5730     case IDM_LoadNextGame:\r
5731       ReloadGame(1);\r
5732       break;\r
5733 \r
5734     case IDM_LoadPrevGame:\r
5735       ReloadGame(-1);\r
5736       break;\r
5737 \r
5738     case IDM_ReloadGame:\r
5739       ReloadGame(0);\r
5740       break;\r
5741 \r
5742     case IDM_LoadPosition:\r
5743       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5744         Reset(FALSE, TRUE);\r
5745       }\r
5746       number = 1;\r
5747       f = OpenFileDialog(hwnd, "rb", "",\r
5748                          appData.oldSaveStyle ? "pos" : "fen",\r
5749                          POSITION_FILT,\r
5750                          "Load Position from File", &number, fileTitle, NULL);\r
5751       if (f != NULL) {\r
5752         LoadPosition(f, number, fileTitle);\r
5753       }\r
5754       break;\r
5755 \r
5756     case IDM_LoadNextPosition:\r
5757       ReloadPosition(1);\r
5758       break;\r
5759 \r
5760     case IDM_LoadPrevPosition:\r
5761       ReloadPosition(-1);\r
5762       break;\r
5763 \r
5764     case IDM_ReloadPosition:\r
5765       ReloadPosition(0);\r
5766       break;\r
5767 \r
5768     case IDM_SaveGame:\r
5769       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5770       f = OpenFileDialog(hwnd, "a", defName,\r
5771                          appData.oldSaveStyle ? "gam" : "pgn",\r
5772                          GAME_FILT,\r
5773                          "Save Game to File", NULL, fileTitle, NULL);\r
5774       if (f != NULL) {\r
5775         SaveGame(f, 0, "");\r
5776       }\r
5777       break;\r
5778 \r
5779     case IDM_SavePosition:\r
5780       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5781       f = OpenFileDialog(hwnd, "a", defName,\r
5782                          appData.oldSaveStyle ? "pos" : "fen",\r
5783                          POSITION_FILT,\r
5784                          "Save Position to File", NULL, fileTitle, NULL);\r
5785       if (f != NULL) {\r
5786         SavePosition(f, 0, "");\r
5787       }\r
5788       break;\r
5789 \r
5790     case IDM_SaveDiagram:\r
5791       defName = "diagram";\r
5792       f = OpenFileDialog(hwnd, "wb", defName,\r
5793                          "bmp",\r
5794                          DIAGRAM_FILT,\r
5795                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5796       if (f != NULL) {\r
5797         SaveDiagram(f);\r
5798       }\r
5799       break;\r
5800 \r
5801     case IDM_CopyGame:\r
5802       CopyGameToClipboard();\r
5803       break;\r
5804 \r
5805     case IDM_PasteGame:\r
5806       PasteGameFromClipboard();\r
5807       break;\r
5808 \r
5809     case IDM_CopyGameListToClipboard:\r
5810       CopyGameListToClipboard();\r
5811       break;\r
5812 \r
5813     /* [AS] Autodetect FEN or PGN data */\r
5814     case IDM_PasteAny:\r
5815       PasteGameOrFENFromClipboard();\r
5816       break;\r
5817 \r
5818     /* [AS] Move history */\r
5819     case IDM_ShowMoveHistory:\r
5820         if( MoveHistoryIsUp() ) {\r
5821             MoveHistoryPopDown();\r
5822         }\r
5823         else {\r
5824             MoveHistoryPopUp();\r
5825         }\r
5826         break;\r
5827 \r
5828     /* [AS] Eval graph */\r
5829     case IDM_ShowEvalGraph:\r
5830         if( EvalGraphIsUp() ) {\r
5831             EvalGraphPopDown();\r
5832         }\r
5833         else {\r
5834             EvalGraphPopUp();\r
5835             SetFocus(hwndMain);\r
5836         }\r
5837         break;\r
5838 \r
5839     /* [AS] Engine output */\r
5840     case IDM_ShowEngineOutput:\r
5841         if( EngineOutputIsUp() ) {\r
5842             EngineOutputPopDown();\r
5843         }\r
5844         else {\r
5845             EngineOutputPopUp();\r
5846         }\r
5847         break;\r
5848 \r
5849     /* [AS] User adjudication */\r
5850     case IDM_UserAdjudication_White:\r
5851         UserAdjudicationEvent( +1 );\r
5852         break;\r
5853 \r
5854     case IDM_UserAdjudication_Black:\r
5855         UserAdjudicationEvent( -1 );\r
5856         break;\r
5857 \r
5858     case IDM_UserAdjudication_Draw:\r
5859         UserAdjudicationEvent( 0 );\r
5860         break;\r
5861 \r
5862     /* [AS] Game list options dialog */\r
5863     case IDM_GameListOptions:\r
5864       GameListOptions();\r
5865       break;\r
5866 \r
5867     case IDM_CopyPosition:\r
5868       CopyFENToClipboard();\r
5869       break;\r
5870 \r
5871     case IDM_PastePosition:\r
5872       PasteFENFromClipboard();\r
5873       break;\r
5874 \r
5875     case IDM_MailMove:\r
5876       MailMoveEvent();\r
5877       break;\r
5878 \r
5879     case IDM_ReloadCMailMsg:\r
5880       Reset(TRUE, TRUE);\r
5881       ReloadCmailMsgEvent(FALSE);\r
5882       break;\r
5883 \r
5884     case IDM_Minimize:\r
5885       ShowWindow(hwnd, SW_MINIMIZE);\r
5886       break;\r
5887 \r
5888     case IDM_Exit:\r
5889       ExitEvent(0);\r
5890       break;\r
5891 \r
5892     case IDM_MachineWhite:\r
5893       MachineWhiteEvent();\r
5894       /*\r
5895        * refresh the tags dialog only if it's visible\r
5896        */\r
5897       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5898           char *tags;\r
5899           tags = PGNTags(&gameInfo);\r
5900           TagsPopUp(tags, CmailMsg());\r
5901           free(tags);\r
5902       }\r
5903       SAY("computer starts playing white");\r
5904       break;\r
5905 \r
5906     case IDM_MachineBlack:\r
5907       MachineBlackEvent();\r
5908       /*\r
5909        * refresh the tags dialog only if it's visible\r
5910        */\r
5911       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5912           char *tags;\r
5913           tags = PGNTags(&gameInfo);\r
5914           TagsPopUp(tags, CmailMsg());\r
5915           free(tags);\r
5916       }\r
5917       SAY("computer starts playing black");\r
5918       break;\r
5919 \r
5920     case IDM_TwoMachines:\r
5921       TwoMachinesEvent();\r
5922       /*\r
5923        * refresh the tags dialog only if it's visible\r
5924        */\r
5925       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5926           char *tags;\r
5927           tags = PGNTags(&gameInfo);\r
5928           TagsPopUp(tags, CmailMsg());\r
5929           free(tags);\r
5930       }\r
5931       SAY("programs start playing each other");\r
5932       break;\r
5933 \r
5934     case IDM_AnalysisMode:\r
5935       if (!first.analysisSupport) {\r
5936         sprintf(buf, "%s does not support analysis", first.tidy);\r
5937         DisplayError(buf, 0);\r
5938       } else {\r
5939         SAY("analyzing current position");\r
5940         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5941         if (appData.icsActive) {\r
5942                if (gameMode != IcsObserving) {\r
5943                        sprintf(buf, "You are not observing a game");\r
5944                        DisplayError(buf, 0);\r
5945                        /* secure check */\r
5946                        if (appData.icsEngineAnalyze) {\r
5947                                if (appData.debugMode) \r
5948                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5949                                ExitAnalyzeMode();\r
5950                                ModeHighlight();\r
5951                                break;\r
5952                        }\r
5953                        break;\r
5954                } else {\r
5955                        /* if enable, user want disable icsEngineAnalyze */\r
5956                        if (appData.icsEngineAnalyze) {\r
5957                                ExitAnalyzeMode();\r
5958                                ModeHighlight();\r
5959                                break;\r
5960                        }\r
5961                        appData.icsEngineAnalyze = TRUE;\r
5962                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5963                }\r
5964         } \r
5965         if (!appData.showThinking) ToggleShowThinking();\r
5966         AnalyzeModeEvent();\r
5967       }\r
5968       break;\r
5969 \r
5970     case IDM_AnalyzeFile:\r
5971       if (!first.analysisSupport) {\r
5972         char buf[MSG_SIZ];\r
5973         sprintf(buf, "%s does not support analysis", first.tidy);\r
5974         DisplayError(buf, 0);\r
5975       } else {\r
5976         if (!appData.showThinking) ToggleShowThinking();\r
5977         AnalyzeFileEvent();\r
5978         LoadGameDialog(hwnd, "Analyze Game from File");\r
5979         AnalysisPeriodicEvent(1);\r
5980       }\r
5981       break;\r
5982 \r
5983     case IDM_IcsClient:\r
5984       IcsClientEvent();\r
5985       break;\r
5986 \r
5987     case IDM_EditGame:\r
5988       EditGameEvent();\r
5989       SAY("edit game");\r
5990       break;\r
5991 \r
5992     case IDM_EditPosition:\r
5993       EditPositionEvent();\r
5994       SAY("to set up a position type a FEN");\r
5995       break;\r
5996 \r
5997     case IDM_Training:\r
5998       TrainingEvent();\r
5999       break;\r
6000 \r
6001     case IDM_ShowGameList:\r
6002       ShowGameListProc();\r
6003       break;\r
6004 \r
6005     case IDM_EditTags:\r
6006       EditTagsProc();\r
6007       break;\r
6008 \r
6009     case IDM_EditComment:\r
6010       if (commentDialogUp && editComment) {\r
6011         CommentPopDown();\r
6012       } else {\r
6013         EditCommentEvent();\r
6014       }\r
6015       break;\r
6016 \r
6017     case IDM_Pause:\r
6018       PauseEvent();\r
6019       break;\r
6020 \r
6021     case IDM_Accept:\r
6022       AcceptEvent();\r
6023       break;\r
6024 \r
6025     case IDM_Decline:\r
6026       DeclineEvent();\r
6027       break;\r
6028 \r
6029     case IDM_Rematch:\r
6030       RematchEvent();\r
6031       break;\r
6032 \r
6033     case IDM_CallFlag:\r
6034       CallFlagEvent();\r
6035       break;\r
6036 \r
6037     case IDM_Draw:\r
6038       DrawEvent();\r
6039       break;\r
6040 \r
6041     case IDM_Adjourn:\r
6042       AdjournEvent();\r
6043       break;\r
6044 \r
6045     case IDM_Abort:\r
6046       AbortEvent();\r
6047       break;\r
6048 \r
6049     case IDM_Resign:\r
6050       ResignEvent();\r
6051       break;\r
6052 \r
6053     case IDM_StopObserving:\r
6054       StopObservingEvent();\r
6055       break;\r
6056 \r
6057     case IDM_StopExamining:\r
6058       StopExaminingEvent();\r
6059       break;\r
6060 \r
6061     case IDM_TypeInMove:\r
6062       PopUpMoveDialog('\000');\r
6063       break;\r
6064 \r
6065     case IDM_TypeInName:\r
6066       PopUpNameDialog('\000');\r
6067       break;\r
6068 \r
6069     case IDM_Backward:\r
6070       BackwardEvent();\r
6071       SetFocus(hwndMain);\r
6072       break;\r
6073 \r
6074     JAWS_MENU_ITEMS\r
6075 \r
6076     case IDM_Forward:\r
6077       ForwardEvent();\r
6078       SetFocus(hwndMain);\r
6079       break;\r
6080 \r
6081     case IDM_ToStart:\r
6082       ToStartEvent();\r
6083       SetFocus(hwndMain);\r
6084       break;\r
6085 \r
6086     case IDM_ToEnd:\r
6087       ToEndEvent();\r
6088       SetFocus(hwndMain);\r
6089       break;\r
6090 \r
6091     case IDM_Revert:\r
6092       RevertEvent();\r
6093       break;\r
6094 \r
6095     case IDM_TruncateGame:\r
6096       TruncateGameEvent();\r
6097       break;\r
6098 \r
6099     case IDM_MoveNow:\r
6100       MoveNowEvent();\r
6101       break;\r
6102 \r
6103     case IDM_RetractMove:\r
6104       RetractMoveEvent();\r
6105       break;\r
6106 \r
6107     case IDM_FlipView:\r
6108       flipView = !flipView;\r
6109       DrawPosition(FALSE, NULL);\r
6110       break;\r
6111 \r
6112     case IDM_FlipClock:\r
6113       flipClock = !flipClock;\r
6114       DisplayBothClocks();\r
6115       DrawPosition(FALSE, NULL);\r
6116       break;\r
6117 \r
6118     case IDM_GeneralOptions:\r
6119       GeneralOptionsPopup(hwnd);\r
6120       DrawPosition(TRUE, NULL);\r
6121       break;\r
6122 \r
6123     case IDM_BoardOptions:\r
6124       BoardOptionsPopup(hwnd);\r
6125       break;\r
6126 \r
6127     case IDM_EnginePlayOptions:\r
6128       EnginePlayOptionsPopup(hwnd);\r
6129       break;\r
6130 \r
6131     case IDM_OptionsUCI:\r
6132       UciOptionsPopup(hwnd);\r
6133       break;\r
6134 \r
6135     case IDM_IcsOptions:\r
6136       IcsOptionsPopup(hwnd);\r
6137       break;\r
6138 \r
6139     case IDM_Fonts:\r
6140       FontsOptionsPopup(hwnd);\r
6141       break;\r
6142 \r
6143     case IDM_Sounds:\r
6144       SoundOptionsPopup(hwnd);\r
6145       break;\r
6146 \r
6147     case IDM_CommPort:\r
6148       CommPortOptionsPopup(hwnd);\r
6149       break;\r
6150 \r
6151     case IDM_LoadOptions:\r
6152       LoadOptionsPopup(hwnd);\r
6153       break;\r
6154 \r
6155     case IDM_SaveOptions:\r
6156       SaveOptionsPopup(hwnd);\r
6157       break;\r
6158 \r
6159     case IDM_TimeControl:\r
6160       TimeControlOptionsPopup(hwnd);\r
6161       break;\r
6162 \r
6163     case IDM_SaveSettings:\r
6164       SaveSettings(settingsFileName);\r
6165       break;\r
6166 \r
6167     case IDM_SaveSettingsOnExit:\r
6168       saveSettingsOnExit = !saveSettingsOnExit;\r
6169       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6170                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6171                                          MF_CHECKED : MF_UNCHECKED));\r
6172       break;\r
6173 \r
6174     case IDM_Hint:\r
6175       HintEvent();\r
6176       break;\r
6177 \r
6178     case IDM_Book:\r
6179       BookEvent();\r
6180       break;\r
6181 \r
6182     case IDM_AboutGame:\r
6183       AboutGameEvent();\r
6184       break;\r
6185 \r
6186     case IDM_Debug:\r
6187       appData.debugMode = !appData.debugMode;\r
6188       if (appData.debugMode) {\r
6189         char dir[MSG_SIZ];\r
6190         GetCurrentDirectory(MSG_SIZ, dir);\r
6191         SetCurrentDirectory(installDir);\r
6192         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6193         SetCurrentDirectory(dir);\r
6194         setbuf(debugFP, NULL);\r
6195       } else {\r
6196         fclose(debugFP);\r
6197         debugFP = NULL;\r
6198       }\r
6199       break;\r
6200 \r
6201     case IDM_HELPCONTENTS:\r
6202       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6203           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6204           MessageBox (GetFocus(),\r
6205                     "Unable to activate help",\r
6206                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6207       }\r
6208       break;\r
6209 \r
6210     case IDM_HELPSEARCH:\r
6211         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6212             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6213         MessageBox (GetFocus(),\r
6214                     "Unable to activate help",\r
6215                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6216       }\r
6217       break;\r
6218 \r
6219     case IDM_HELPHELP:\r
6220       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6221         MessageBox (GetFocus(),\r
6222                     "Unable to activate help",\r
6223                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6224       }\r
6225       break;\r
6226 \r
6227     case IDM_ABOUT:\r
6228       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6229       DialogBox(hInst, \r
6230         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6231         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6232       FreeProcInstance(lpProc);\r
6233       break;\r
6234 \r
6235     case IDM_DirectCommand1:\r
6236       AskQuestionEvent("Direct Command",\r
6237                        "Send to chess program:", "", "1");\r
6238       break;\r
6239     case IDM_DirectCommand2:\r
6240       AskQuestionEvent("Direct Command",\r
6241                        "Send to second chess program:", "", "2");\r
6242       break;\r
6243 \r
6244     case EP_WhitePawn:\r
6245       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6246       fromX = fromY = -1;\r
6247       break;\r
6248 \r
6249     case EP_WhiteKnight:\r
6250       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6251       fromX = fromY = -1;\r
6252       break;\r
6253 \r
6254     case EP_WhiteBishop:\r
6255       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6256       fromX = fromY = -1;\r
6257       break;\r
6258 \r
6259     case EP_WhiteRook:\r
6260       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6261       fromX = fromY = -1;\r
6262       break;\r
6263 \r
6264     case EP_WhiteQueen:\r
6265       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6266       fromX = fromY = -1;\r
6267       break;\r
6268 \r
6269     case EP_WhiteFerz:\r
6270       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6271       fromX = fromY = -1;\r
6272       break;\r
6273 \r
6274     case EP_WhiteWazir:\r
6275       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6276       fromX = fromY = -1;\r
6277       break;\r
6278 \r
6279     case EP_WhiteAlfil:\r
6280       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6281       fromX = fromY = -1;\r
6282       break;\r
6283 \r
6284     case EP_WhiteCannon:\r
6285       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6286       fromX = fromY = -1;\r
6287       break;\r
6288 \r
6289     case EP_WhiteCardinal:\r
6290       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6291       fromX = fromY = -1;\r
6292       break;\r
6293 \r
6294     case EP_WhiteMarshall:\r
6295       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6296       fromX = fromY = -1;\r
6297       break;\r
6298 \r
6299     case EP_WhiteKing:\r
6300       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6301       fromX = fromY = -1;\r
6302       break;\r
6303 \r
6304     case EP_BlackPawn:\r
6305       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6306       fromX = fromY = -1;\r
6307       break;\r
6308 \r
6309     case EP_BlackKnight:\r
6310       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6311       fromX = fromY = -1;\r
6312       break;\r
6313 \r
6314     case EP_BlackBishop:\r
6315       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6316       fromX = fromY = -1;\r
6317       break;\r
6318 \r
6319     case EP_BlackRook:\r
6320       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6321       fromX = fromY = -1;\r
6322       break;\r
6323 \r
6324     case EP_BlackQueen:\r
6325       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6326       fromX = fromY = -1;\r
6327       break;\r
6328 \r
6329     case EP_BlackFerz:\r
6330       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6331       fromX = fromY = -1;\r
6332       break;\r
6333 \r
6334     case EP_BlackWazir:\r
6335       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6336       fromX = fromY = -1;\r
6337       break;\r
6338 \r
6339     case EP_BlackAlfil:\r
6340       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6341       fromX = fromY = -1;\r
6342       break;\r
6343 \r
6344     case EP_BlackCannon:\r
6345       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6346       fromX = fromY = -1;\r
6347       break;\r
6348 \r
6349     case EP_BlackCardinal:\r
6350       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6351       fromX = fromY = -1;\r
6352       break;\r
6353 \r
6354     case EP_BlackMarshall:\r
6355       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6356       fromX = fromY = -1;\r
6357       break;\r
6358 \r
6359     case EP_BlackKing:\r
6360       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6361       fromX = fromY = -1;\r
6362       break;\r
6363 \r
6364     case EP_EmptySquare:\r
6365       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6366       fromX = fromY = -1;\r
6367       break;\r
6368 \r
6369     case EP_ClearBoard:\r
6370       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6371       fromX = fromY = -1;\r
6372       break;\r
6373 \r
6374     case EP_White:\r
6375       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6376       fromX = fromY = -1;\r
6377       break;\r
6378 \r
6379     case EP_Black:\r
6380       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6381       fromX = fromY = -1;\r
6382       break;\r
6383 \r
6384     case EP_Promote:\r
6385       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6386       fromX = fromY = -1;\r
6387       break;\r
6388 \r
6389     case EP_Demote:\r
6390       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6391       fromX = fromY = -1;\r
6392       break;\r
6393 \r
6394     case DP_Pawn:\r
6395       DropMenuEvent(WhitePawn, fromX, fromY);\r
6396       fromX = fromY = -1;\r
6397       break;\r
6398 \r
6399     case DP_Knight:\r
6400       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6401       fromX = fromY = -1;\r
6402       break;\r
6403 \r
6404     case DP_Bishop:\r
6405       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6406       fromX = fromY = -1;\r
6407       break;\r
6408 \r
6409     case DP_Rook:\r
6410       DropMenuEvent(WhiteRook, fromX, fromY);\r
6411       fromX = fromY = -1;\r
6412       break;\r
6413 \r
6414     case DP_Queen:\r
6415       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6416       fromX = fromY = -1;\r
6417       break;\r
6418 \r
6419     default:\r
6420       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6421     }\r
6422     break;\r
6423 \r
6424   case WM_TIMER:\r
6425     switch (wParam) {\r
6426     case CLOCK_TIMER_ID:\r
6427       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6428       clockTimerEvent = 0;\r
6429       DecrementClocks(); /* call into back end */\r
6430       break;\r
6431     case LOAD_GAME_TIMER_ID:\r
6432       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6433       loadGameTimerEvent = 0;\r
6434       AutoPlayGameLoop(); /* call into back end */\r
6435       break;\r
6436     case ANALYSIS_TIMER_ID:\r
6437       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6438                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6439         AnalysisPeriodicEvent(0);\r
6440       } else {\r
6441         KillTimer(hwnd, analysisTimerEvent);\r
6442         analysisTimerEvent = 0;\r
6443       }\r
6444       break;\r
6445     case DELAYED_TIMER_ID:\r
6446       KillTimer(hwnd, delayedTimerEvent);\r
6447       delayedTimerEvent = 0;\r
6448       delayedTimerCallback();\r
6449       break;\r
6450     }\r
6451     break;\r
6452 \r
6453   case WM_USER_Input:\r
6454     InputEvent(hwnd, message, wParam, lParam);\r
6455     break;\r
6456 \r
6457   /* [AS] Also move "attached" child windows */\r
6458   case WM_WINDOWPOSCHANGING:\r
6459 \r
6460     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6461         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6462 \r
6463         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6464             /* Window is moving */\r
6465             RECT rcMain;\r
6466 \r
6467 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6468             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6469             rcMain.right  = boardX + winWidth;\r
6470             rcMain.top    = boardY;\r
6471             rcMain.bottom = boardY + winHeight;\r
6472             \r
6473             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6474             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6475             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6476             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6477             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6478             boardX = lpwp->x;\r
6479             boardY = lpwp->y;\r
6480         }\r
6481     }\r
6482     break;\r
6483 \r
6484   /* [AS] Snapping */\r
6485   case WM_ENTERSIZEMOVE:\r
6486     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6487     if (hwnd == hwndMain) {\r
6488       doingSizing = TRUE;\r
6489       lastSizing = 0;\r
6490     }\r
6491     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6492     break;\r
6493 \r
6494   case WM_SIZING:\r
6495     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6496     if (hwnd == hwndMain) {\r
6497       lastSizing = wParam;\r
6498     }\r
6499     break;\r
6500 \r
6501   case WM_MOVING:\r
6502     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6503       return OnMoving( &sd, hwnd, wParam, lParam );\r
6504 \r
6505   case WM_EXITSIZEMOVE:\r
6506     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6507     if (hwnd == hwndMain) {\r
6508       RECT client;\r
6509       doingSizing = FALSE;\r
6510       InvalidateRect(hwnd, &boardRect, FALSE);\r
6511       GetClientRect(hwnd, &client);\r
6512       ResizeBoard(client.right, client.bottom, lastSizing);\r
6513       lastSizing = 0;\r
6514       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6515     }\r
6516     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6517     break;\r
6518 \r
6519   case WM_DESTROY: /* message: window being destroyed */\r
6520     PostQuitMessage(0);\r
6521     break;\r
6522 \r
6523   case WM_CLOSE:\r
6524     if (hwnd == hwndMain) {\r
6525       ExitEvent(0);\r
6526     }\r
6527     break;\r
6528 \r
6529   default:      /* Passes it on if unprocessed */\r
6530     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6531   }\r
6532   return 0;\r
6533 }\r
6534 \r
6535 /*---------------------------------------------------------------------------*\\r
6536  *\r
6537  * Misc utility routines\r
6538  *\r
6539 \*---------------------------------------------------------------------------*/\r
6540 \r
6541 /*\r
6542  * Decent random number generator, at least not as bad as Windows\r
6543  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6544  */\r
6545 unsigned int randstate;\r
6546 \r
6547 int\r
6548 myrandom(void)\r
6549 {\r
6550   randstate = randstate * 1664525 + 1013904223;\r
6551   return (int) randstate & 0x7fffffff;\r
6552 }\r
6553 \r
6554 void\r
6555 mysrandom(unsigned int seed)\r
6556 {\r
6557   randstate = seed;\r
6558 }\r
6559 \r
6560 \r
6561 /* \r
6562  * returns TRUE if user selects a different color, FALSE otherwise \r
6563  */\r
6564 \r
6565 BOOL\r
6566 ChangeColor(HWND hwnd, COLORREF *which)\r
6567 {\r
6568   static BOOL firstTime = TRUE;\r
6569   static DWORD customColors[16];\r
6570   CHOOSECOLOR cc;\r
6571   COLORREF newcolor;\r
6572   int i;\r
6573   ColorClass ccl;\r
6574 \r
6575   if (firstTime) {\r
6576     /* Make initial colors in use available as custom colors */\r
6577     /* Should we put the compiled-in defaults here instead? */\r
6578     i = 0;\r
6579     customColors[i++] = lightSquareColor & 0xffffff;\r
6580     customColors[i++] = darkSquareColor & 0xffffff;\r
6581     customColors[i++] = whitePieceColor & 0xffffff;\r
6582     customColors[i++] = blackPieceColor & 0xffffff;\r
6583     customColors[i++] = highlightSquareColor & 0xffffff;\r
6584     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6585 \r
6586     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6587       customColors[i++] = textAttribs[ccl].color;\r
6588     }\r
6589     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6590     firstTime = FALSE;\r
6591   }\r
6592 \r
6593   cc.lStructSize = sizeof(cc);\r
6594   cc.hwndOwner = hwnd;\r
6595   cc.hInstance = NULL;\r
6596   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6597   cc.lpCustColors = (LPDWORD) customColors;\r
6598   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6599 \r
6600   if (!ChooseColor(&cc)) return FALSE;\r
6601 \r
6602   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6603   if (newcolor == *which) return FALSE;\r
6604   *which = newcolor;\r
6605   return TRUE;\r
6606 \r
6607   /*\r
6608   InitDrawingColors();\r
6609   InvalidateRect(hwnd, &boardRect, FALSE);\r
6610   */\r
6611 }\r
6612 \r
6613 BOOLEAN\r
6614 MyLoadSound(MySound *ms)\r
6615 {\r
6616   BOOL ok = FALSE;\r
6617   struct stat st;\r
6618   FILE *f;\r
6619 \r
6620   if (ms->data) free(ms->data);\r
6621   ms->data = NULL;\r
6622 \r
6623   switch (ms->name[0]) {\r
6624   case NULLCHAR:\r
6625     /* Silence */\r
6626     ok = TRUE;\r
6627     break;\r
6628   case '$':\r
6629     /* System sound from Control Panel.  Don't preload here. */\r
6630     ok = TRUE;\r
6631     break;\r
6632   case '!':\r
6633     if (ms->name[1] == NULLCHAR) {\r
6634       /* "!" alone = silence */\r
6635       ok = TRUE;\r
6636     } else {\r
6637       /* Builtin wave resource.  Error if not found. */\r
6638       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6639       if (h == NULL) break;\r
6640       ms->data = (void *)LoadResource(hInst, h);\r
6641       if (h == NULL) break;\r
6642       ok = TRUE;\r
6643     }\r
6644     break;\r
6645   default:\r
6646     /* .wav file.  Error if not found. */\r
6647     f = fopen(ms->name, "rb");\r
6648     if (f == NULL) break;\r
6649     if (fstat(fileno(f), &st) < 0) break;\r
6650     ms->data = malloc(st.st_size);\r
6651     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6652     fclose(f);\r
6653     ok = TRUE;\r
6654     break;\r
6655   }\r
6656   if (!ok) {\r
6657     char buf[MSG_SIZ];\r
6658     sprintf(buf, "Error loading sound %s", ms->name);\r
6659     DisplayError(buf, GetLastError());\r
6660   }\r
6661   return ok;\r
6662 }\r
6663 \r
6664 BOOLEAN\r
6665 MyPlaySound(MySound *ms)\r
6666 {\r
6667   BOOLEAN ok = FALSE;\r
6668         if(appData.debugMode) fprintf(debugFP, "make sound %s %x %d\n", ms->name, ms, ms->name[0]);\r
6669   switch (ms->name[0]) {\r
6670   case NULLCHAR:\r
6671         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6672     /* Silence */\r
6673     ok = TRUE;\r
6674     break;\r
6675   case '$':\r
6676     /* System sound from Control Panel (deprecated feature).\r
6677        "$" alone or an unset sound name gets default beep (still in use). */\r
6678     if (ms->name[1]) {\r
6679       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6680     }\r
6681     if (!ok) ok = MessageBeep(MB_OK);\r
6682     break; \r
6683   case '!':\r
6684     /* Builtin wave resource, or "!" alone for silence */\r
6685     if (ms->name[1]) {\r
6686       if (ms->data == NULL) return FALSE;\r
6687       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6688     } else {\r
6689       ok = TRUE;\r
6690     }\r
6691     break;\r
6692   default:\r
6693     /* .wav file.  Error if not found. */\r
6694     if (ms->data == NULL) return FALSE;\r
6695     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6696     break;\r
6697   }\r
6698   /* Don't print an error: this can happen innocently if the sound driver\r
6699      is busy; for instance, if another instance of WinBoard is playing\r
6700      a sound at about the same time. */\r
6701 #if 0\r
6702   if (!ok) {\r
6703     char buf[MSG_SIZ];\r
6704     sprintf(buf, "Error playing sound %s", ms->name);\r
6705     DisplayError(buf, GetLastError());\r
6706   }\r
6707 #endif\r
6708   return ok;\r
6709 }\r
6710 \r
6711 \r
6712 LRESULT CALLBACK\r
6713 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6714 {\r
6715   BOOL ok;\r
6716   OPENFILENAME *ofn;\r
6717   static UINT *number; /* gross that this is static */\r
6718 \r
6719   switch (message) {\r
6720   case WM_INITDIALOG: /* message: initialize dialog box */\r
6721     /* Center the dialog over the application window */\r
6722     ofn = (OPENFILENAME *) lParam;\r
6723     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6724       number = (UINT *) ofn->lCustData;\r
6725       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6726     } else {\r
6727       number = NULL;\r
6728     }\r
6729     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6730     return FALSE;  /* Allow for further processing */\r
6731 \r
6732   case WM_COMMAND:\r
6733     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6734       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6735     }\r
6736     return FALSE;  /* Allow for further processing */\r
6737   }\r
6738   return FALSE;\r
6739 }\r
6740 \r
6741 UINT APIENTRY\r
6742 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6743 {\r
6744   static UINT *number;\r
6745   OPENFILENAME *ofname;\r
6746   OFNOTIFY *ofnot;\r
6747   switch (uiMsg) {\r
6748   case WM_INITDIALOG:\r
6749     ofname = (OPENFILENAME *)lParam;\r
6750     number = (UINT *)(ofname->lCustData);\r
6751     break;\r
6752   case WM_NOTIFY:\r
6753     ofnot = (OFNOTIFY *)lParam;\r
6754     if (ofnot->hdr.code == CDN_FILEOK) {\r
6755       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6756     }\r
6757     break;\r
6758   }\r
6759   return 0;\r
6760 }\r
6761 \r
6762 \r
6763 FILE *\r
6764 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6765                char *nameFilt, char *dlgTitle, UINT *number,\r
6766                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6767 {\r
6768   OPENFILENAME openFileName;\r
6769   char buf1[MSG_SIZ];\r
6770   FILE *f;\r
6771 \r
6772   if (fileName == NULL) fileName = buf1;\r
6773   if (defName == NULL) {\r
6774     strcpy(fileName, "*.");\r
6775     strcat(fileName, defExt);\r
6776   } else {\r
6777     strcpy(fileName, defName);\r
6778   }\r
6779   if (fileTitle) strcpy(fileTitle, "");\r
6780   if (number) *number = 0;\r
6781 \r
6782   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6783   openFileName.hwndOwner         = hwnd;\r
6784   openFileName.hInstance         = (HANDLE) hInst;\r
6785   openFileName.lpstrFilter       = nameFilt;\r
6786   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6787   openFileName.nMaxCustFilter    = 0L;\r
6788   openFileName.nFilterIndex      = 1L;\r
6789   openFileName.lpstrFile         = fileName;\r
6790   openFileName.nMaxFile          = MSG_SIZ;\r
6791   openFileName.lpstrFileTitle    = fileTitle;\r
6792   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6793   openFileName.lpstrInitialDir   = NULL;\r
6794   openFileName.lpstrTitle        = dlgTitle;\r
6795   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6796     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6797     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6798     | (oldDialog ? 0 : OFN_EXPLORER);\r
6799   openFileName.nFileOffset       = 0;\r
6800   openFileName.nFileExtension    = 0;\r
6801   openFileName.lpstrDefExt       = defExt;\r
6802   openFileName.lCustData         = (LONG) number;\r
6803   openFileName.lpfnHook          = oldDialog ?\r
6804     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6805   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6806 \r
6807   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6808                         GetOpenFileName(&openFileName)) {\r
6809     /* open the file */\r
6810     f = fopen(openFileName.lpstrFile, write);\r
6811     if (f == NULL) {\r
6812       MessageBox(hwnd, "File open failed", NULL,\r
6813                  MB_OK|MB_ICONEXCLAMATION);\r
6814       return NULL;\r
6815     }\r
6816   } else {\r
6817     int err = CommDlgExtendedError();\r
6818     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6819     return FALSE;\r
6820   }\r
6821   return f;\r
6822 }\r
6823 \r
6824 \r
6825 \r
6826 VOID APIENTRY\r
6827 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6828 {\r
6829   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6830 \r
6831   /*\r
6832    * Get the first pop-up menu in the menu template. This is the\r
6833    * menu that TrackPopupMenu displays.\r
6834    */\r
6835   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6836 \r
6837   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6838 \r
6839   /*\r
6840    * TrackPopup uses screen coordinates, so convert the\r
6841    * coordinates of the mouse click to screen coordinates.\r
6842    */\r
6843   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6844 \r
6845   /* Draw and track the floating pop-up menu. */\r
6846   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6847                  pt.x, pt.y, 0, hwnd, NULL);\r
6848 \r
6849   /* Destroy the menu.*/\r
6850   DestroyMenu(hmenu);\r
6851 }\r
6852    \r
6853 typedef struct {\r
6854   HWND hDlg, hText;\r
6855   int sizeX, sizeY, newSizeX, newSizeY;\r
6856   HDWP hdwp;\r
6857 } ResizeEditPlusButtonsClosure;\r
6858 \r
6859 BOOL CALLBACK\r
6860 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6861 {\r
6862   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6863   RECT rect;\r
6864   POINT pt;\r
6865 \r
6866   if (hChild == cl->hText) return TRUE;\r
6867   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6868   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6869   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6870   ScreenToClient(cl->hDlg, &pt);\r
6871   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6872     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6873   return TRUE;\r
6874 }\r
6875 \r
6876 /* Resize a dialog that has a (rich) edit field filling most of\r
6877    the top, with a row of buttons below */\r
6878 VOID\r
6879 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6880 {\r
6881   RECT rectText;\r
6882   int newTextHeight, newTextWidth;\r
6883   ResizeEditPlusButtonsClosure cl;\r
6884   \r
6885   /*if (IsIconic(hDlg)) return;*/\r
6886   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6887   \r
6888   cl.hdwp = BeginDeferWindowPos(8);\r
6889 \r
6890   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6891   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6892   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6893   if (newTextHeight < 0) {\r
6894     newSizeY += -newTextHeight;\r
6895     newTextHeight = 0;\r
6896   }\r
6897   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6898     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6899 \r
6900   cl.hDlg = hDlg;\r
6901   cl.hText = hText;\r
6902   cl.sizeX = sizeX;\r
6903   cl.sizeY = sizeY;\r
6904   cl.newSizeX = newSizeX;\r
6905   cl.newSizeY = newSizeY;\r
6906   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6907 \r
6908   EndDeferWindowPos(cl.hdwp);\r
6909 }\r
6910 \r
6911 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6912 {\r
6913     RECT    rChild, rParent;\r
6914     int     wChild, hChild, wParent, hParent;\r
6915     int     wScreen, hScreen, xNew, yNew;\r
6916     HDC     hdc;\r
6917 \r
6918     /* Get the Height and Width of the child window */\r
6919     GetWindowRect (hwndChild, &rChild);\r
6920     wChild = rChild.right - rChild.left;\r
6921     hChild = rChild.bottom - rChild.top;\r
6922 \r
6923     /* Get the Height and Width of the parent window */\r
6924     GetWindowRect (hwndParent, &rParent);\r
6925     wParent = rParent.right - rParent.left;\r
6926     hParent = rParent.bottom - rParent.top;\r
6927 \r
6928     /* Get the display limits */\r
6929     hdc = GetDC (hwndChild);\r
6930     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6931     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6932     ReleaseDC(hwndChild, hdc);\r
6933 \r
6934     /* Calculate new X position, then adjust for screen */\r
6935     xNew = rParent.left + ((wParent - wChild) /2);\r
6936     if (xNew < 0) {\r
6937         xNew = 0;\r
6938     } else if ((xNew+wChild) > wScreen) {\r
6939         xNew = wScreen - wChild;\r
6940     }\r
6941 \r
6942     /* Calculate new Y position, then adjust for screen */\r
6943     if( mode == 0 ) {\r
6944         yNew = rParent.top  + ((hParent - hChild) /2);\r
6945     }\r
6946     else {\r
6947         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6948     }\r
6949 \r
6950     if (yNew < 0) {\r
6951         yNew = 0;\r
6952     } else if ((yNew+hChild) > hScreen) {\r
6953         yNew = hScreen - hChild;\r
6954     }\r
6955 \r
6956     /* Set it, and return */\r
6957     return SetWindowPos (hwndChild, NULL,\r
6958                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6959 }\r
6960 \r
6961 /* Center one window over another */\r
6962 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6963 {\r
6964     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6965 }\r
6966 \r
6967 /*---------------------------------------------------------------------------*\\r
6968  *\r
6969  * Startup Dialog functions\r
6970  *\r
6971 \*---------------------------------------------------------------------------*/\r
6972 void\r
6973 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6974 {\r
6975   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6976 \r
6977   while (*cd != NULL) {\r
6978     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6979     cd++;\r
6980   }\r
6981 }\r
6982 \r
6983 void\r
6984 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6985 {\r
6986   char buf1[ARG_MAX];\r
6987   int len;\r
6988 \r
6989   if (str[0] == '@') {\r
6990     FILE* f = fopen(str + 1, "r");\r
6991     if (f == NULL) {\r
6992       DisplayFatalError(str + 1, errno, 2);\r
6993       return;\r
6994     }\r
6995     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6996     fclose(f);\r
6997     buf1[len] = NULLCHAR;\r
6998     str = buf1;\r
6999   }\r
7000 \r
7001   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7002 \r
7003   for (;;) {\r
7004     char buf[MSG_SIZ];\r
7005     char *end = strchr(str, '\n');\r
7006     if (end == NULL) return;\r
7007     memcpy(buf, str, end - str);\r
7008     buf[end - str] = NULLCHAR;\r
7009     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
7010     str = end + 1;\r
7011   }\r
7012 }\r
7013 \r
7014 void\r
7015 SetStartupDialogEnables(HWND hDlg)\r
7016 {\r
7017   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
7018     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7019     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
7020   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7021     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
7022   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
7023     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
7024   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
7025     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
7026   EnableWindow(GetDlgItem(hDlg, IDOK),\r
7027     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7028     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
7029     IsDlgButtonChecked(hDlg, OPT_View));\r
7030 }\r
7031 \r
7032 char *\r
7033 QuoteForFilename(char *filename)\r
7034 {\r
7035   int dquote, space;\r
7036   dquote = strchr(filename, '"') != NULL;\r
7037   space = strchr(filename, ' ') != NULL;\r
7038   if (dquote || space) {\r
7039     if (dquote) {\r
7040       return "'";\r
7041     } else {\r
7042       return "\"";\r
7043     }\r
7044   } else {\r
7045     return "";\r
7046   }\r
7047 }\r
7048 \r
7049 VOID\r
7050 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
7051 {\r
7052   char buf[MSG_SIZ];\r
7053   char *q;\r
7054 \r
7055   InitComboStringsFromOption(hwndCombo, nthnames);\r
7056   q = QuoteForFilename(nthcp);\r
7057   sprintf(buf, "%s%s%s", q, nthcp, q);\r
7058   if (*nthdir != NULLCHAR) {\r
7059     q = QuoteForFilename(nthdir);\r
7060     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
7061   }\r
7062   if (*nthcp == NULLCHAR) {\r
7063     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7064   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7065     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7066     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7067   }\r
7068 }\r
7069 \r
7070 LRESULT CALLBACK\r
7071 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7072 {\r
7073   char buf[MSG_SIZ];\r
7074   HANDLE hwndCombo;\r
7075   char *p;\r
7076 \r
7077   switch (message) {\r
7078   case WM_INITDIALOG:\r
7079     /* Center the dialog */\r
7080     CenterWindow (hDlg, GetDesktopWindow());\r
7081     /* Initialize the dialog items */\r
7082     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7083                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7084                   firstChessProgramNames);\r
7085     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7086                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7087                   secondChessProgramNames);\r
7088     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7089     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7090     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7091     if (*appData.icsHelper != NULLCHAR) {\r
7092       char *q = QuoteForFilename(appData.icsHelper);\r
7093       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7094     }\r
7095     if (*appData.icsHost == NULLCHAR) {\r
7096       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7097       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7098     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7099       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7100       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7101     }\r
7102 \r
7103     if (appData.icsActive) {\r
7104       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7105     }\r
7106     else if (appData.noChessProgram) {\r
7107       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7108     }\r
7109     else {\r
7110       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7111     }\r
7112 \r
7113     SetStartupDialogEnables(hDlg);\r
7114     return TRUE;\r
7115 \r
7116   case WM_COMMAND:\r
7117     switch (LOWORD(wParam)) {\r
7118     case IDOK:\r
7119       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7120         strcpy(buf, "/fcp=");\r
7121         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7122         p = buf;\r
7123         ParseArgs(StringGet, &p);\r
7124         strcpy(buf, "/scp=");\r
7125         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7126         p = buf;\r
7127         ParseArgs(StringGet, &p);\r
7128         appData.noChessProgram = FALSE;\r
7129         appData.icsActive = FALSE;\r
7130       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7131         strcpy(buf, "/ics /icshost=");\r
7132         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7133         p = buf;\r
7134         ParseArgs(StringGet, &p);\r
7135         if (appData.zippyPlay) {\r
7136           strcpy(buf, "/fcp=");\r
7137           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7138           p = buf;\r
7139           ParseArgs(StringGet, &p);\r
7140         }\r
7141       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7142         appData.noChessProgram = TRUE;\r
7143         appData.icsActive = FALSE;\r
7144       } else {\r
7145         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7146                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7147         return TRUE;\r
7148       }\r
7149       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7150         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7151         p = buf;\r
7152         ParseArgs(StringGet, &p);\r
7153       }\r
7154       EndDialog(hDlg, TRUE);\r
7155       return TRUE;\r
7156 \r
7157     case IDCANCEL:\r
7158       ExitEvent(0);\r
7159       return TRUE;\r
7160 \r
7161     case IDM_HELPCONTENTS:\r
7162       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7163         MessageBox (GetFocus(),\r
7164                     "Unable to activate help",\r
7165                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7166       }\r
7167       break;\r
7168 \r
7169     default:\r
7170       SetStartupDialogEnables(hDlg);\r
7171       break;\r
7172     }\r
7173     break;\r
7174   }\r
7175   return FALSE;\r
7176 }\r
7177 \r
7178 /*---------------------------------------------------------------------------*\\r
7179  *\r
7180  * About box dialog functions\r
7181  *\r
7182 \*---------------------------------------------------------------------------*/\r
7183 \r
7184 /* Process messages for "About" dialog box */\r
7185 LRESULT CALLBACK\r
7186 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7187 {\r
7188   switch (message) {\r
7189   case WM_INITDIALOG: /* message: initialize dialog box */\r
7190     /* Center the dialog over the application window */\r
7191     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7192     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7193     return (TRUE);\r
7194 \r
7195   case WM_COMMAND: /* message: received a command */\r
7196     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7197         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7198       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7199       return (TRUE);\r
7200     }\r
7201     break;\r
7202   }\r
7203   return (FALSE);\r
7204 }\r
7205 \r
7206 /*---------------------------------------------------------------------------*\\r
7207  *\r
7208  * Comment Dialog functions\r
7209  *\r
7210 \*---------------------------------------------------------------------------*/\r
7211 \r
7212 LRESULT CALLBACK\r
7213 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7214 {\r
7215   static HANDLE hwndText = NULL;\r
7216   int len, newSizeX, newSizeY, flags;\r
7217   static int sizeX, sizeY;\r
7218   char *str;\r
7219   RECT rect;\r
7220   MINMAXINFO *mmi;\r
7221 \r
7222   switch (message) {\r
7223   case WM_INITDIALOG: /* message: initialize dialog box */\r
7224     /* Initialize the dialog items */\r
7225     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7226     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7227     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7228     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7229     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7230     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7231     SetWindowText(hDlg, commentTitle);\r
7232     if (editComment) {\r
7233       SetFocus(hwndText);\r
7234     } else {\r
7235       SetFocus(GetDlgItem(hDlg, IDOK));\r
7236     }\r
7237     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7238                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7239                 MAKELPARAM(FALSE, 0));\r
7240     /* Size and position the dialog */\r
7241     if (!commentDialog) {\r
7242       commentDialog = hDlg;\r
7243       flags = SWP_NOZORDER;\r
7244       GetClientRect(hDlg, &rect);\r
7245       sizeX = rect.right;\r
7246       sizeY = rect.bottom;\r
7247       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7248           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7249         WINDOWPLACEMENT wp;\r
7250         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7251         wp.length = sizeof(WINDOWPLACEMENT);\r
7252         wp.flags = 0;\r
7253         wp.showCmd = SW_SHOW;\r
7254         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7255         wp.rcNormalPosition.left = commentX;\r
7256         wp.rcNormalPosition.right = commentX + commentW;\r
7257         wp.rcNormalPosition.top = commentY;\r
7258         wp.rcNormalPosition.bottom = commentY + commentH;\r
7259         SetWindowPlacement(hDlg, &wp);\r
7260 \r
7261         GetClientRect(hDlg, &rect);\r
7262         newSizeX = rect.right;\r
7263         newSizeY = rect.bottom;\r
7264         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7265                               newSizeX, newSizeY);\r
7266         sizeX = newSizeX;\r
7267         sizeY = newSizeY;\r
7268       }\r
7269     }\r
7270     return FALSE;\r
7271 \r
7272   case WM_COMMAND: /* message: received a command */\r
7273     switch (LOWORD(wParam)) {\r
7274     case IDOK:\r
7275       if (editComment) {\r
7276         char *p, *q;\r
7277         /* Read changed options from the dialog box */\r
7278         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7279         len = GetWindowTextLength(hwndText);\r
7280         str = (char *) malloc(len + 1);\r
7281         GetWindowText(hwndText, str, len + 1);\r
7282         p = q = str;\r
7283         while (*q) {\r
7284           if (*q == '\r')\r
7285             q++;\r
7286           else\r
7287             *p++ = *q++;\r
7288         }\r
7289         *p = NULLCHAR;\r
7290         ReplaceComment(commentIndex, str);\r
7291         free(str);\r
7292       }\r
7293       CommentPopDown();\r
7294       return TRUE;\r
7295 \r
7296     case IDCANCEL:\r
7297     case OPT_CancelComment:\r
7298       CommentPopDown();\r
7299       return TRUE;\r
7300 \r
7301     case OPT_ClearComment:\r
7302       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7303       break;\r
7304 \r
7305     case OPT_EditComment:\r
7306       EditCommentEvent();\r
7307       return TRUE;\r
7308 \r
7309     default:\r
7310       break;\r
7311     }\r
7312     break;\r
7313 \r
7314   case WM_SIZE:\r
7315     newSizeX = LOWORD(lParam);\r
7316     newSizeY = HIWORD(lParam);\r
7317     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7318     sizeX = newSizeX;\r
7319     sizeY = newSizeY;\r
7320     break;\r
7321 \r
7322   case WM_GETMINMAXINFO:\r
7323     /* Prevent resizing window too small */\r
7324     mmi = (MINMAXINFO *) lParam;\r
7325     mmi->ptMinTrackSize.x = 100;\r
7326     mmi->ptMinTrackSize.y = 100;\r
7327     break;\r
7328   }\r
7329   return FALSE;\r
7330 }\r
7331 \r
7332 VOID\r
7333 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7334 {\r
7335   FARPROC lpProc;\r
7336   char *p, *q;\r
7337 \r
7338   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7339 \r
7340   if (str == NULL) str = "";\r
7341   p = (char *) malloc(2 * strlen(str) + 2);\r
7342   q = p;\r
7343   while (*str) {\r
7344     if (*str == '\n') *q++ = '\r';\r
7345     *q++ = *str++;\r
7346   }\r
7347   *q = NULLCHAR;\r
7348   if (commentText != NULL) free(commentText);\r
7349 \r
7350   commentIndex = index;\r
7351   commentTitle = title;\r
7352   commentText = p;\r
7353   editComment = edit;\r
7354 \r
7355   if (commentDialog) {\r
7356     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7357     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7358   } else {\r
7359     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7360     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7361                  hwndMain, (DLGPROC)lpProc);\r
7362     FreeProcInstance(lpProc);\r
7363   }\r
7364   commentDialogUp = TRUE;\r
7365 }\r
7366 \r
7367 \r
7368 /*---------------------------------------------------------------------------*\\r
7369  *\r
7370  * Type-in move dialog functions\r
7371  * \r
7372 \*---------------------------------------------------------------------------*/\r
7373 \r
7374 LRESULT CALLBACK\r
7375 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7376 {\r
7377   char move[MSG_SIZ];\r
7378   HWND hInput;\r
7379   ChessMove moveType;\r
7380   int fromX, fromY, toX, toY;\r
7381   char promoChar;\r
7382 \r
7383   switch (message) {\r
7384   case WM_INITDIALOG:\r
7385     move[0] = (char) lParam;\r
7386     move[1] = NULLCHAR;\r
7387     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7388     hInput = GetDlgItem(hDlg, OPT_Move);\r
7389     SetWindowText(hInput, move);\r
7390     SetFocus(hInput);\r
7391     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7392     return FALSE;\r
7393 \r
7394   case WM_COMMAND:\r
7395     switch (LOWORD(wParam)) {\r
7396     case IDOK:\r
7397       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7398       { int n; Board board;\r
7399         // [HGM] FENedit\r
7400         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7401                 EditPositionPasteFEN(move);\r
7402                 EndDialog(hDlg, TRUE);\r
7403                 return TRUE;\r
7404         }\r
7405         // [HGM] movenum: allow move number to be typed in any mode\r
7406         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7407           currentMove = 2*n-1;\r
7408           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7409           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7410           EndDialog(hDlg, TRUE);\r
7411           DrawPosition(TRUE, boards[currentMove]);\r
7412           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7413           else DisplayMessage("", "");\r
7414           return TRUE;\r
7415         }\r
7416       }\r
7417       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7418         gameMode != Training) {\r
7419         DisplayMoveError("Displayed move is not current");\r
7420       } else {\r
7421 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7422         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7423           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7424         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7425         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7426           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7427           if (gameMode != Training)\r
7428               forwardMostMove = currentMove;\r
7429           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7430         } else {\r
7431           DisplayMoveError("Could not parse move");\r
7432         }\r
7433       }\r
7434       EndDialog(hDlg, TRUE);\r
7435       return TRUE;\r
7436     case IDCANCEL:\r
7437       EndDialog(hDlg, FALSE);\r
7438       return TRUE;\r
7439     default:\r
7440       break;\r
7441     }\r
7442     break;\r
7443   }\r
7444   return FALSE;\r
7445 }\r
7446 \r
7447 VOID\r
7448 PopUpMoveDialog(char firstchar)\r
7449 {\r
7450     FARPROC lpProc;\r
7451     \r
7452     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7453         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7454         gameMode == AnalyzeMode || gameMode == EditGame || \r
7455         gameMode == EditPosition || gameMode == IcsExamining ||\r
7456         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7457         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7458                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7459                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7460         gameMode == Training) {\r
7461       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7462       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7463         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7464       FreeProcInstance(lpProc);\r
7465     }\r
7466 }\r
7467 \r
7468 /*---------------------------------------------------------------------------*\\r
7469  *\r
7470  * Type-in name dialog functions\r
7471  * \r
7472 \*---------------------------------------------------------------------------*/\r
7473 \r
7474 LRESULT CALLBACK\r
7475 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7476 {\r
7477   char move[MSG_SIZ];\r
7478   HWND hInput;\r
7479 \r
7480   switch (message) {\r
7481   case WM_INITDIALOG:\r
7482     move[0] = (char) lParam;\r
7483     move[1] = NULLCHAR;\r
7484     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7485     hInput = GetDlgItem(hDlg, OPT_Name);\r
7486     SetWindowText(hInput, move);\r
7487     SetFocus(hInput);\r
7488     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7489     return FALSE;\r
7490 \r
7491   case WM_COMMAND:\r
7492     switch (LOWORD(wParam)) {\r
7493     case IDOK:\r
7494       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7495       appData.userName = strdup(move);\r
7496       SetUserLogo();\r
7497 \r
7498       EndDialog(hDlg, TRUE);\r
7499       return TRUE;\r
7500     case IDCANCEL:\r
7501       EndDialog(hDlg, FALSE);\r
7502       return TRUE;\r
7503     default:\r
7504       break;\r
7505     }\r
7506     break;\r
7507   }\r
7508   return FALSE;\r
7509 }\r
7510 \r
7511 VOID\r
7512 PopUpNameDialog(char firstchar)\r
7513 {\r
7514     FARPROC lpProc;\r
7515     \r
7516       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7517       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7518         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7519       FreeProcInstance(lpProc);\r
7520 }\r
7521 \r
7522 /*---------------------------------------------------------------------------*\\r
7523  *\r
7524  *  Error dialogs\r
7525  * \r
7526 \*---------------------------------------------------------------------------*/\r
7527 \r
7528 /* Nonmodal error box */\r
7529 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7530                              WPARAM wParam, LPARAM lParam);\r
7531 \r
7532 VOID\r
7533 ErrorPopUp(char *title, char *content)\r
7534 {\r
7535   FARPROC lpProc;\r
7536   char *p, *q;\r
7537   BOOLEAN modal = hwndMain == NULL;\r
7538 \r
7539   p = content;\r
7540   q = errorMessage;\r
7541   while (*p) {\r
7542     if (*p == '\n') {\r
7543       if (modal) {\r
7544         *q++ = ' ';\r
7545         p++;\r
7546       } else {\r
7547         *q++ = '\r';\r
7548         *q++ = *p++;\r
7549       }\r
7550     } else {\r
7551       *q++ = *p++;\r
7552     }\r
7553   }\r
7554   *q = NULLCHAR;\r
7555   strncpy(errorTitle, title, sizeof(errorTitle));\r
7556   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7557   \r
7558   if (modal) {\r
7559     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7560   } else {\r
7561     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7562     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7563                  hwndMain, (DLGPROC)lpProc);\r
7564     FreeProcInstance(lpProc);\r
7565   }\r
7566 }\r
7567 \r
7568 VOID\r
7569 ErrorPopDown()\r
7570 {\r
7571   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7572   if (errorDialog == NULL) return;\r
7573   DestroyWindow(errorDialog);\r
7574   errorDialog = NULL;\r
7575 }\r
7576 \r
7577 LRESULT CALLBACK\r
7578 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7579 {\r
7580   HANDLE hwndText;\r
7581   RECT rChild;\r
7582 \r
7583   switch (message) {\r
7584   case WM_INITDIALOG:\r
7585     GetWindowRect(hDlg, &rChild);\r
7586 \r
7587     /*\r
7588     SetWindowPos(hDlg, NULL, rChild.left,\r
7589       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7590       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7591     */\r
7592 \r
7593     /* \r
7594         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7595         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7596         and it doesn't work when you resize the dialog.\r
7597         For now, just give it a default position.\r
7598     */\r
7599     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7600 \r
7601     errorDialog = hDlg;\r
7602     SetWindowText(hDlg, errorTitle);\r
7603     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7604     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7605     return FALSE;\r
7606 \r
7607   case WM_COMMAND:\r
7608     switch (LOWORD(wParam)) {\r
7609     case IDOK:\r
7610     case IDCANCEL:\r
7611       if (errorDialog == hDlg) errorDialog = NULL;\r
7612       DestroyWindow(hDlg);\r
7613       return TRUE;\r
7614 \r
7615     default:\r
7616       break;\r
7617     }\r
7618     break;\r
7619   }\r
7620   return FALSE;\r
7621 }\r
7622 \r
7623 #ifdef GOTHIC\r
7624 HWND gothicDialog = NULL;\r
7625 \r
7626 LRESULT CALLBACK\r
7627 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7628 {\r
7629   HANDLE hwndText;\r
7630   RECT rChild;\r
7631   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7632 \r
7633   switch (message) {\r
7634   case WM_INITDIALOG:\r
7635     GetWindowRect(hDlg, &rChild);\r
7636 \r
7637     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7638                                                              SWP_NOZORDER);\r
7639 \r
7640     /* \r
7641         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7642         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7643         and it doesn't work when you resize the dialog.\r
7644         For now, just give it a default position.\r
7645     */\r
7646     gothicDialog = hDlg;\r
7647     SetWindowText(hDlg, errorTitle);\r
7648     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7649     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7650     return FALSE;\r
7651 \r
7652   case WM_COMMAND:\r
7653     switch (LOWORD(wParam)) {\r
7654     case IDOK:\r
7655     case IDCANCEL:\r
7656       if (errorDialog == hDlg) errorDialog = NULL;\r
7657       DestroyWindow(hDlg);\r
7658       return TRUE;\r
7659 \r
7660     default:\r
7661       break;\r
7662     }\r
7663     break;\r
7664   }\r
7665   return FALSE;\r
7666 }\r
7667 \r
7668 VOID\r
7669 GothicPopUp(char *title, VariantClass variant)\r
7670 {\r
7671   FARPROC lpProc;\r
7672   static char *lastTitle;\r
7673 \r
7674   strncpy(errorTitle, title, sizeof(errorTitle));\r
7675   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7676 \r
7677   if(lastTitle != title && gothicDialog != NULL) {\r
7678     DestroyWindow(gothicDialog);\r
7679     gothicDialog = NULL;\r
7680   }\r
7681   if(variant != VariantNormal && gothicDialog == NULL) {\r
7682     title = lastTitle;\r
7683     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7684     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7685                  hwndMain, (DLGPROC)lpProc);\r
7686     FreeProcInstance(lpProc);\r
7687   }\r
7688 }\r
7689 #endif\r
7690 \r
7691 /*---------------------------------------------------------------------------*\\r
7692  *\r
7693  *  Ics Interaction console functions\r
7694  *\r
7695 \*---------------------------------------------------------------------------*/\r
7696 \r
7697 #define HISTORY_SIZE 64\r
7698 static char *history[HISTORY_SIZE];\r
7699 int histIn = 0, histP = 0;\r
7700 \r
7701 VOID\r
7702 SaveInHistory(char *cmd)\r
7703 {\r
7704   if (history[histIn] != NULL) {\r
7705     free(history[histIn]);\r
7706     history[histIn] = NULL;\r
7707   }\r
7708   if (*cmd == NULLCHAR) return;\r
7709   history[histIn] = StrSave(cmd);\r
7710   histIn = (histIn + 1) % HISTORY_SIZE;\r
7711   if (history[histIn] != NULL) {\r
7712     free(history[histIn]);\r
7713     history[histIn] = NULL;\r
7714   }\r
7715   histP = histIn;\r
7716 }\r
7717 \r
7718 char *\r
7719 PrevInHistory(char *cmd)\r
7720 {\r
7721   int newhp;\r
7722   if (histP == histIn) {\r
7723     if (history[histIn] != NULL) free(history[histIn]);\r
7724     history[histIn] = StrSave(cmd);\r
7725   }\r
7726   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7727   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7728   histP = newhp;\r
7729   return history[histP];\r
7730 }\r
7731 \r
7732 char *\r
7733 NextInHistory()\r
7734 {\r
7735   if (histP == histIn) return NULL;\r
7736   histP = (histP + 1) % HISTORY_SIZE;\r
7737   return history[histP];\r
7738 }\r
7739 \r
7740 typedef struct {\r
7741   char *item;\r
7742   char *command;\r
7743   BOOLEAN getname;\r
7744   BOOLEAN immediate;\r
7745 } IcsTextMenuEntry;\r
7746 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7747 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7748 \r
7749 void\r
7750 ParseIcsTextMenu(char *icsTextMenuString)\r
7751 {\r
7752 //  int flags = 0;\r
7753   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7754   char *p = icsTextMenuString;\r
7755   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7756     free(e->item);\r
7757     e->item = NULL;\r
7758     if (e->command != NULL) {\r
7759       free(e->command);\r
7760       e->command = NULL;\r
7761     }\r
7762     e++;\r
7763   }\r
7764   e = icsTextMenuEntry;\r
7765   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7766     if (*p == ';' || *p == '\n') {\r
7767       e->item = strdup("-");\r
7768       e->command = NULL;\r
7769       p++;\r
7770     } else if (*p == '-') {\r
7771       e->item = strdup("-");\r
7772       e->command = NULL;\r
7773       p++;\r
7774       if (*p) p++;\r
7775     } else {\r
7776       char *q, *r, *s, *t;\r
7777       char c;\r
7778       q = strchr(p, ',');\r
7779       if (q == NULL) break;\r
7780       *q = NULLCHAR;\r
7781       r = strchr(q + 1, ',');\r
7782       if (r == NULL) break;\r
7783       *r = NULLCHAR;\r
7784       s = strchr(r + 1, ',');\r
7785       if (s == NULL) break;\r
7786       *s = NULLCHAR;\r
7787       c = ';';\r
7788       t = strchr(s + 1, c);\r
7789       if (t == NULL) {\r
7790         c = '\n';\r
7791         t = strchr(s + 1, c);\r
7792       }\r
7793       if (t != NULL) *t = NULLCHAR;\r
7794       e->item = strdup(p);\r
7795       e->command = strdup(q + 1);\r
7796       e->getname = *(r + 1) != '0';\r
7797       e->immediate = *(s + 1) != '0';\r
7798       *q = ',';\r
7799       *r = ',';\r
7800       *s = ',';\r
7801       if (t == NULL) break;\r
7802       *t = c;\r
7803       p = t + 1;\r
7804     }\r
7805     e++;\r
7806   } \r
7807 }\r
7808 \r
7809 HMENU\r
7810 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7811 {\r
7812   HMENU hmenu, h;\r
7813   int i = 0;\r
7814   hmenu = LoadMenu(hInst, "TextMenu");\r
7815   h = GetSubMenu(hmenu, 0);\r
7816   while (e->item) {\r
7817     if (strcmp(e->item, "-") == 0) {\r
7818       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7819     } else {\r
7820       if (e->item[0] == '|') {\r
7821         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7822                    IDM_CommandX + i, &e->item[1]);\r
7823       } else {\r
7824         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7825       }\r
7826     }\r
7827     e++;\r
7828     i++;\r
7829   } \r
7830   return hmenu;\r
7831 }\r
7832 \r
7833 WNDPROC consoleTextWindowProc;\r
7834 \r
7835 void\r
7836 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7837 {\r
7838   char buf[MSG_SIZ], name[MSG_SIZ];\r
7839   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7840   CHARRANGE sel;\r
7841 \r
7842   if (!getname) {\r
7843     SetWindowText(hInput, command);\r
7844     if (immediate) {\r
7845       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7846     } else {\r
7847       sel.cpMin = 999999;\r
7848       sel.cpMax = 999999;\r
7849       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7850       SetFocus(hInput);\r
7851     }\r
7852     return;\r
7853   }    \r
7854   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7855   if (sel.cpMin == sel.cpMax) {\r
7856     /* Expand to surrounding word */\r
7857     TEXTRANGE tr;\r
7858     do {\r
7859       tr.chrg.cpMax = sel.cpMin;\r
7860       tr.chrg.cpMin = --sel.cpMin;\r
7861       if (sel.cpMin < 0) break;\r
7862       tr.lpstrText = name;\r
7863       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7864     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7865     sel.cpMin++;\r
7866 \r
7867     do {\r
7868       tr.chrg.cpMin = sel.cpMax;\r
7869       tr.chrg.cpMax = ++sel.cpMax;\r
7870       tr.lpstrText = name;\r
7871       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7872     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7873     sel.cpMax--;\r
7874 \r
7875     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7876       MessageBeep(MB_ICONEXCLAMATION);\r
7877       return;\r
7878     }\r
7879     tr.chrg = sel;\r
7880     tr.lpstrText = name;\r
7881     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7882   } else {\r
7883     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7884       MessageBeep(MB_ICONEXCLAMATION);\r
7885       return;\r
7886     }\r
7887     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7888   }\r
7889   if (immediate) {\r
7890     sprintf(buf, "%s %s", command, name);\r
7891     SetWindowText(hInput, buf);\r
7892     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7893   } else {\r
7894     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7895     SetWindowText(hInput, buf);\r
7896     sel.cpMin = 999999;\r
7897     sel.cpMax = 999999;\r
7898     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7899     SetFocus(hInput);\r
7900   }\r
7901 }\r
7902 \r
7903 LRESULT CALLBACK \r
7904 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7905 {\r
7906   HWND hInput;\r
7907   CHARRANGE sel;\r
7908 \r
7909   switch (message) {\r
7910   case WM_KEYDOWN:\r
7911     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7912     switch (wParam) {\r
7913     case VK_PRIOR:\r
7914       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7915       return 0;\r
7916     case VK_NEXT:\r
7917       sel.cpMin = 999999;\r
7918       sel.cpMax = 999999;\r
7919       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7920       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7921       return 0;\r
7922     }\r
7923     break;\r
7924   case WM_CHAR:\r
7925    if(wParam != '\022') {\r
7926     if (wParam == '\t') {\r
7927       if (GetKeyState(VK_SHIFT) < 0) {\r
7928         /* shifted */\r
7929         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7930         if (buttonDesc[0].hwnd) {\r
7931           SetFocus(buttonDesc[0].hwnd);\r
7932         } else {\r
7933           SetFocus(hwndMain);\r
7934         }\r
7935       } else {\r
7936         /* unshifted */\r
7937         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7938       }\r
7939     } else {\r
7940       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7941       JAWS_DELETE( SetFocus(hInput); )\r
7942       SendMessage(hInput, message, wParam, lParam);\r
7943     }\r
7944     return 0;\r
7945    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7946   case WM_RBUTTONUP:\r
7947     if (GetKeyState(VK_SHIFT) & ~1) {\r
7948       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7949         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7950     } else {\r
7951       POINT pt;\r
7952       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7953       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7954       if (sel.cpMin == sel.cpMax) {\r
7955         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7956         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7957       }\r
7958       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7959         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7960       }\r
7961       pt.x = LOWORD(lParam);\r
7962       pt.y = HIWORD(lParam);\r
7963       MenuPopup(hwnd, pt, hmenu, -1);\r
7964     }\r
7965     return 0;\r
7966   case WM_PASTE:\r
7967     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7968     SetFocus(hInput);\r
7969     return SendMessage(hInput, message, wParam, lParam);\r
7970   case WM_MBUTTONDOWN:\r
7971     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7972   case WM_RBUTTONDOWN:\r
7973     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7974       /* Move selection here if it was empty */\r
7975       POINT pt;\r
7976       pt.x = LOWORD(lParam);\r
7977       pt.y = HIWORD(lParam);\r
7978       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7979       if (sel.cpMin == sel.cpMax) {\r
7980         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7981         sel.cpMax = sel.cpMin;\r
7982         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7983       }\r
7984       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7985     }\r
7986     return 0;\r
7987   case WM_COMMAND:\r
7988     switch (LOWORD(wParam)) {\r
7989     case IDM_QuickPaste:\r
7990       {\r
7991         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7992         if (sel.cpMin == sel.cpMax) {\r
7993           MessageBeep(MB_ICONEXCLAMATION);\r
7994           return 0;\r
7995         }\r
7996         SendMessage(hwnd, WM_COPY, 0, 0);\r
7997         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7998         SendMessage(hInput, WM_PASTE, 0, 0);\r
7999         SetFocus(hInput);\r
8000         return 0;\r
8001       }\r
8002     case IDM_Cut:\r
8003       SendMessage(hwnd, WM_CUT, 0, 0);\r
8004       return 0;\r
8005     case IDM_Paste:\r
8006       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8007       return 0;\r
8008     case IDM_Copy:\r
8009       SendMessage(hwnd, WM_COPY, 0, 0);\r
8010       return 0;\r
8011     default:\r
8012       {\r
8013         int i = LOWORD(wParam) - IDM_CommandX;\r
8014         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
8015             icsTextMenuEntry[i].command != NULL) {\r
8016           CommandX(hwnd, icsTextMenuEntry[i].command,\r
8017                    icsTextMenuEntry[i].getname,\r
8018                    icsTextMenuEntry[i].immediate);\r
8019           return 0;\r
8020         }\r
8021       }\r
8022       break;\r
8023     }\r
8024     break;\r
8025   }\r
8026   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
8027 }\r
8028 \r
8029 WNDPROC consoleInputWindowProc;\r
8030 \r
8031 LRESULT CALLBACK\r
8032 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8033 {\r
8034   char buf[MSG_SIZ];\r
8035   char *p;\r
8036   static BOOL sendNextChar = FALSE;\r
8037   static BOOL quoteNextChar = FALSE;\r
8038   InputSource *is = consoleInputSource;\r
8039   CHARFORMAT cf;\r
8040   CHARRANGE sel;\r
8041 \r
8042   switch (message) {\r
8043   case WM_CHAR:\r
8044     if (!appData.localLineEditing || sendNextChar) {\r
8045       is->buf[0] = (CHAR) wParam;\r
8046       is->count = 1;\r
8047       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8048       sendNextChar = FALSE;\r
8049       return 0;\r
8050     }\r
8051     if (quoteNextChar) {\r
8052       buf[0] = (char) wParam;\r
8053       buf[1] = NULLCHAR;\r
8054       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
8055       quoteNextChar = FALSE;\r
8056       return 0;\r
8057     }\r
8058     switch (wParam) {\r
8059     case '\r':   /* Enter key */\r
8060       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
8061       if (consoleEcho) SaveInHistory(is->buf);\r
8062       is->buf[is->count++] = '\n';\r
8063       is->buf[is->count] = NULLCHAR;\r
8064       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8065       if (consoleEcho) {\r
8066         ConsoleOutput(is->buf, is->count, TRUE);\r
8067       } else if (appData.localLineEditing) {\r
8068         ConsoleOutput("\n", 1, TRUE);\r
8069       }\r
8070       /* fall thru */\r
8071     case '\033': /* Escape key */\r
8072       SetWindowText(hwnd, "");\r
8073       cf.cbSize = sizeof(CHARFORMAT);\r
8074       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8075       if (consoleEcho) {\r
8076         cf.crTextColor = textAttribs[ColorNormal].color;\r
8077       } else {\r
8078         cf.crTextColor = COLOR_ECHOOFF;\r
8079       }\r
8080       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8081       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8082       return 0;\r
8083     case '\t':   /* Tab key */\r
8084       if (GetKeyState(VK_SHIFT) < 0) {\r
8085         /* shifted */\r
8086         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8087       } else {\r
8088         /* unshifted */\r
8089         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8090         if (buttonDesc[0].hwnd) {\r
8091           SetFocus(buttonDesc[0].hwnd);\r
8092         } else {\r
8093           SetFocus(hwndMain);\r
8094         }\r
8095       }\r
8096       return 0;\r
8097     case '\023': /* Ctrl+S */\r
8098       sendNextChar = TRUE;\r
8099       return 0;\r
8100     case '\021': /* Ctrl+Q */\r
8101       quoteNextChar = TRUE;\r
8102       return 0;\r
8103     JAWS_REPLAY\r
8104     default:\r
8105       break;\r
8106     }\r
8107     break;\r
8108   case WM_KEYDOWN:\r
8109     switch (wParam) {\r
8110     case VK_UP:\r
8111       GetWindowText(hwnd, buf, MSG_SIZ);\r
8112       p = PrevInHistory(buf);\r
8113       if (p != NULL) {\r
8114         SetWindowText(hwnd, p);\r
8115         sel.cpMin = 999999;\r
8116         sel.cpMax = 999999;\r
8117         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8118         return 0;\r
8119       }\r
8120       break;\r
8121     case VK_DOWN:\r
8122       p = NextInHistory();\r
8123       if (p != NULL) {\r
8124         SetWindowText(hwnd, p);\r
8125         sel.cpMin = 999999;\r
8126         sel.cpMax = 999999;\r
8127         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8128         return 0;\r
8129       }\r
8130       break;\r
8131     case VK_HOME:\r
8132     case VK_END:\r
8133       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8134       /* fall thru */\r
8135     case VK_PRIOR:\r
8136     case VK_NEXT:\r
8137       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8138       return 0;\r
8139     }\r
8140     break;\r
8141   case WM_MBUTTONDOWN:\r
8142     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8143       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8144     break;\r
8145   case WM_RBUTTONUP:\r
8146     if (GetKeyState(VK_SHIFT) & ~1) {\r
8147       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8148         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8149     } else {\r
8150       POINT pt;\r
8151       HMENU hmenu;\r
8152       hmenu = LoadMenu(hInst, "InputMenu");\r
8153       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8154       if (sel.cpMin == sel.cpMax) {\r
8155         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8156         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8157       }\r
8158       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8159         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8160       }\r
8161       pt.x = LOWORD(lParam);\r
8162       pt.y = HIWORD(lParam);\r
8163       MenuPopup(hwnd, pt, hmenu, -1);\r
8164     }\r
8165     return 0;\r
8166   case WM_COMMAND:\r
8167     switch (LOWORD(wParam)) { \r
8168     case IDM_Undo:\r
8169       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8170       return 0;\r
8171     case IDM_SelectAll:\r
8172       sel.cpMin = 0;\r
8173       sel.cpMax = -1; /*999999?*/\r
8174       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8175       return 0;\r
8176     case IDM_Cut:\r
8177       SendMessage(hwnd, WM_CUT, 0, 0);\r
8178       return 0;\r
8179     case IDM_Paste:\r
8180       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8181       return 0;\r
8182     case IDM_Copy:\r
8183       SendMessage(hwnd, WM_COPY, 0, 0);\r
8184       return 0;\r
8185     }\r
8186     break;\r
8187   }\r
8188   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8189 }\r
8190 \r
8191 #define CO_MAX  100000\r
8192 #define CO_TRIM   1000\r
8193 \r
8194 LRESULT CALLBACK\r
8195 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8196 {\r
8197   static SnapData sd;\r
8198   static HWND hText, hInput /*, hFocus*/;\r
8199 //  InputSource *is = consoleInputSource;\r
8200   RECT rect;\r
8201   static int sizeX, sizeY;\r
8202   int newSizeX, newSizeY;\r
8203   MINMAXINFO *mmi;\r
8204 \r
8205   switch (message) {\r
8206   case WM_INITDIALOG: /* message: initialize dialog box */\r
8207     hwndConsole = hDlg;\r
8208     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8209     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8210     SetFocus(hInput);\r
8211     consoleTextWindowProc = (WNDPROC)\r
8212       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8213     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8214     consoleInputWindowProc = (WNDPROC)\r
8215       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8216     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8217     Colorize(ColorNormal, TRUE);\r
8218     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8219     ChangedConsoleFont();\r
8220     GetClientRect(hDlg, &rect);\r
8221     sizeX = rect.right;\r
8222     sizeY = rect.bottom;\r
8223     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8224         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8225       WINDOWPLACEMENT wp;\r
8226       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8227       wp.length = sizeof(WINDOWPLACEMENT);\r
8228       wp.flags = 0;\r
8229       wp.showCmd = SW_SHOW;\r
8230       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8231       wp.rcNormalPosition.left = wpConsole.x;\r
8232       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8233       wp.rcNormalPosition.top = wpConsole.y;\r
8234       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8235       SetWindowPlacement(hDlg, &wp);\r
8236     }\r
8237 #if 1\r
8238    // [HGM] Chessknight's change 2004-07-13\r
8239    else { /* Determine Defaults */\r
8240        WINDOWPLACEMENT wp;\r
8241        wpConsole.x = winWidth + 1;\r
8242        wpConsole.y = boardY;\r
8243        wpConsole.width = screenWidth -  winWidth;\r
8244        wpConsole.height = winHeight;\r
8245        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8246        wp.length = sizeof(WINDOWPLACEMENT);\r
8247        wp.flags = 0;\r
8248        wp.showCmd = SW_SHOW;\r
8249        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8250        wp.rcNormalPosition.left = wpConsole.x;\r
8251        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8252        wp.rcNormalPosition.top = wpConsole.y;\r
8253        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8254        SetWindowPlacement(hDlg, &wp);\r
8255     }\r
8256 #endif\r
8257     return FALSE;\r
8258 \r
8259   case WM_SETFOCUS:\r
8260     SetFocus(hInput);\r
8261     return 0;\r
8262 \r
8263   case WM_CLOSE:\r
8264     ExitEvent(0);\r
8265     /* not reached */\r
8266     break;\r
8267 \r
8268   case WM_SIZE:\r
8269     if (IsIconic(hDlg)) break;\r
8270     newSizeX = LOWORD(lParam);\r
8271     newSizeY = HIWORD(lParam);\r
8272     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8273       RECT rectText, rectInput;\r
8274       POINT pt;\r
8275       int newTextHeight, newTextWidth;\r
8276       GetWindowRect(hText, &rectText);\r
8277       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8278       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8279       if (newTextHeight < 0) {\r
8280         newSizeY += -newTextHeight;\r
8281         newTextHeight = 0;\r
8282       }\r
8283       SetWindowPos(hText, NULL, 0, 0,\r
8284         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8285       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8286       pt.x = rectInput.left;\r
8287       pt.y = rectInput.top + newSizeY - sizeY;\r
8288       ScreenToClient(hDlg, &pt);\r
8289       SetWindowPos(hInput, NULL, \r
8290         pt.x, pt.y, /* needs client coords */   \r
8291         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8292         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8293     }\r
8294     sizeX = newSizeX;\r
8295     sizeY = newSizeY;\r
8296     break;\r
8297 \r
8298   case WM_GETMINMAXINFO:\r
8299     /* Prevent resizing window too small */\r
8300     mmi = (MINMAXINFO *) lParam;\r
8301     mmi->ptMinTrackSize.x = 100;\r
8302     mmi->ptMinTrackSize.y = 100;\r
8303     break;\r
8304 \r
8305   /* [AS] Snapping */\r
8306   case WM_ENTERSIZEMOVE:\r
8307     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8308 \r
8309   case WM_SIZING:\r
8310     return OnSizing( &sd, hDlg, wParam, lParam );\r
8311 \r
8312   case WM_MOVING:\r
8313     return OnMoving( &sd, hDlg, wParam, lParam );\r
8314 \r
8315   case WM_EXITSIZEMOVE:\r
8316     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8317   }\r
8318 \r
8319   return DefWindowProc(hDlg, message, wParam, lParam);\r
8320 }\r
8321 \r
8322 \r
8323 VOID\r
8324 ConsoleCreate()\r
8325 {\r
8326   HWND hCons;\r
8327   if (hwndConsole) return;\r
8328   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8329   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8330 }\r
8331 \r
8332 \r
8333 VOID\r
8334 ConsoleOutput(char* data, int length, int forceVisible)\r
8335 {\r
8336   HWND hText;\r
8337   int trim, exlen;\r
8338   char *p, *q;\r
8339   char buf[CO_MAX+1];\r
8340   POINT pEnd;\r
8341   RECT rect;\r
8342   static int delayLF = 0;\r
8343   CHARRANGE savesel, sel;\r
8344 \r
8345   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8346   p = data;\r
8347   q = buf;\r
8348   if (delayLF) {\r
8349     *q++ = '\r';\r
8350     *q++ = '\n';\r
8351     delayLF = 0;\r
8352   }\r
8353   while (length--) {\r
8354     if (*p == '\n') {\r
8355       if (*++p) {\r
8356         *q++ = '\r';\r
8357         *q++ = '\n';\r
8358       } else {\r
8359         delayLF = 1;\r
8360       }\r
8361     } else if (*p == '\007') {\r
8362        MyPlaySound(&sounds[(int)SoundBell]);\r
8363        p++;\r
8364     } else {\r
8365       *q++ = *p++;\r
8366     }\r
8367   }\r
8368   *q = NULLCHAR;\r
8369   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8370   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8371   /* Save current selection */\r
8372   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8373   exlen = GetWindowTextLength(hText);\r
8374   /* Find out whether current end of text is visible */\r
8375   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8376   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8377   /* Trim existing text if it's too long */\r
8378   if (exlen + (q - buf) > CO_MAX) {\r
8379     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8380     sel.cpMin = 0;\r
8381     sel.cpMax = trim;\r
8382     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8383     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8384     exlen -= trim;\r
8385     savesel.cpMin -= trim;\r
8386     savesel.cpMax -= trim;\r
8387     if (exlen < 0) exlen = 0;\r
8388     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8389     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8390   }\r
8391   /* Append the new text */\r
8392   sel.cpMin = exlen;\r
8393   sel.cpMax = exlen;\r
8394   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8395   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8396   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8397   if (forceVisible || exlen == 0 ||\r
8398       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8399        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8400     /* Scroll to make new end of text visible if old end of text\r
8401        was visible or new text is an echo of user typein */\r
8402     sel.cpMin = 9999999;\r
8403     sel.cpMax = 9999999;\r
8404     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8405     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8406     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8407     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8408   }\r
8409   if (savesel.cpMax == exlen || forceVisible) {\r
8410     /* Move insert point to new end of text if it was at the old\r
8411        end of text or if the new text is an echo of user typein */\r
8412     sel.cpMin = 9999999;\r
8413     sel.cpMax = 9999999;\r
8414     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8415   } else {\r
8416     /* Restore previous selection */\r
8417     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8418   }\r
8419   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8420 }\r
8421 \r
8422 /*---------*/\r
8423 \r
8424 \r
8425 void\r
8426 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8427 {\r
8428   char buf[100];\r
8429   char *str;\r
8430   COLORREF oldFg, oldBg;\r
8431   HFONT oldFont;\r
8432   RECT rect;\r
8433 \r
8434   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8435 \r
8436   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8437   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8438   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8439 \r
8440   rect.left = x;\r
8441   rect.right = x + squareSize;\r
8442   rect.top  = y;\r
8443   rect.bottom = y + squareSize;\r
8444   str = buf;\r
8445 \r
8446   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8447                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8448              y, ETO_CLIPPED|ETO_OPAQUE,\r
8449              &rect, str, strlen(str), NULL);\r
8450 \r
8451   (void) SetTextColor(hdc, oldFg);\r
8452   (void) SetBkColor(hdc, oldBg);\r
8453   (void) SelectObject(hdc, oldFont);\r
8454 }\r
8455 \r
8456 void\r
8457 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8458               RECT *rect, char *color, char *flagFell)\r
8459 {\r
8460   char buf[100];\r
8461   char *str;\r
8462   COLORREF oldFg, oldBg;\r
8463   HFONT oldFont;\r
8464 \r
8465   if (appData.clockMode) {\r
8466     if (tinyLayout)\r
8467       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8468     else\r
8469       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8470     str = buf;\r
8471   } else {\r
8472     str = color;\r
8473   }\r
8474 \r
8475   if (highlight) {\r
8476     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8477     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8478   } else {\r
8479     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8480     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8481   }\r
8482   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8483 \r
8484   JAWS_SILENCE\r
8485 \r
8486   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8487              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8488              rect, str, strlen(str), NULL);\r
8489   if(logoHeight > 0 && appData.clockMode) {\r
8490       RECT r;\r
8491       sprintf(buf, "%s %s", buf+7, flagFell);\r
8492       r.top = rect->top + logoHeight/2;\r
8493       r.left = rect->left;\r
8494       r.right = rect->right;\r
8495       r.bottom = rect->bottom;\r
8496       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8497                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8498                  &r, str, strlen(str), NULL);\r
8499   }\r
8500   (void) SetTextColor(hdc, oldFg);\r
8501   (void) SetBkColor(hdc, oldBg);\r
8502   (void) SelectObject(hdc, oldFont);\r
8503 }\r
8504 \r
8505 \r
8506 int\r
8507 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8508            OVERLAPPED *ovl)\r
8509 {\r
8510   int ok, err;\r
8511 \r
8512   /* [AS]  */\r
8513   if( count <= 0 ) {\r
8514     if (appData.debugMode) {\r
8515       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8516     }\r
8517 \r
8518     return ERROR_INVALID_USER_BUFFER;\r
8519   }\r
8520 \r
8521   ResetEvent(ovl->hEvent);\r
8522   ovl->Offset = ovl->OffsetHigh = 0;\r
8523   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8524   if (ok) {\r
8525     err = NO_ERROR;\r
8526   } else {\r
8527     err = GetLastError();\r
8528     if (err == ERROR_IO_PENDING) {\r
8529       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8530       if (ok)\r
8531         err = NO_ERROR;\r
8532       else\r
8533         err = GetLastError();\r
8534     }\r
8535   }\r
8536   return err;\r
8537 }\r
8538 \r
8539 int\r
8540 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8541             OVERLAPPED *ovl)\r
8542 {\r
8543   int ok, err;\r
8544 \r
8545   ResetEvent(ovl->hEvent);\r
8546   ovl->Offset = ovl->OffsetHigh = 0;\r
8547   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8548   if (ok) {\r
8549     err = NO_ERROR;\r
8550   } else {\r
8551     err = GetLastError();\r
8552     if (err == ERROR_IO_PENDING) {\r
8553       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8554       if (ok)\r
8555         err = NO_ERROR;\r
8556       else\r
8557         err = GetLastError();\r
8558     }\r
8559   }\r
8560   return err;\r
8561 }\r
8562 \r
8563 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8564 void CheckForInputBufferFull( InputSource * is )\r
8565 {\r
8566     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8567         /* Look for end of line */\r
8568         char * p = is->buf;\r
8569         \r
8570         while( p < is->next && *p != '\n' ) {\r
8571             p++;\r
8572         }\r
8573 \r
8574         if( p >= is->next ) {\r
8575             if (appData.debugMode) {\r
8576                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8577             }\r
8578 \r
8579             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8580             is->count = (DWORD) -1;\r
8581             is->next = is->buf;\r
8582         }\r
8583     }\r
8584 }\r
8585 \r
8586 DWORD\r
8587 InputThread(LPVOID arg)\r
8588 {\r
8589   InputSource *is;\r
8590   OVERLAPPED ovl;\r
8591 \r
8592   is = (InputSource *) arg;\r
8593   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8594   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8595   while (is->hThread != NULL) {\r
8596     is->error = DoReadFile(is->hFile, is->next,\r
8597                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8598                            &is->count, &ovl);\r
8599     if (is->error == NO_ERROR) {\r
8600       is->next += is->count;\r
8601     } else {\r
8602       if (is->error == ERROR_BROKEN_PIPE) {\r
8603         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8604         is->count = 0;\r
8605       } else {\r
8606         is->count = (DWORD) -1;\r
8607         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8608         break; \r
8609       }\r
8610     }\r
8611 \r
8612     CheckForInputBufferFull( is );\r
8613 \r
8614     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8615 \r
8616     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8617 \r
8618     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8619   }\r
8620 \r
8621   CloseHandle(ovl.hEvent);\r
8622   CloseHandle(is->hFile);\r
8623 \r
8624   if (appData.debugMode) {\r
8625     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8626   }\r
8627 \r
8628   return 0;\r
8629 }\r
8630 \r
8631 \r
8632 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8633 DWORD\r
8634 NonOvlInputThread(LPVOID arg)\r
8635 {\r
8636   InputSource *is;\r
8637   char *p, *q;\r
8638   int i;\r
8639   char prev;\r
8640 \r
8641   is = (InputSource *) arg;\r
8642   while (is->hThread != NULL) {\r
8643     is->error = ReadFile(is->hFile, is->next,\r
8644                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8645                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8646     if (is->error == NO_ERROR) {\r
8647       /* Change CRLF to LF */\r
8648       if (is->next > is->buf) {\r
8649         p = is->next - 1;\r
8650         i = is->count + 1;\r
8651       } else {\r
8652         p = is->next;\r
8653         i = is->count;\r
8654       }\r
8655       q = p;\r
8656       prev = NULLCHAR;\r
8657       while (i > 0) {\r
8658         if (prev == '\r' && *p == '\n') {\r
8659           *(q-1) = '\n';\r
8660           is->count--;\r
8661         } else { \r
8662           *q++ = *p;\r
8663         }\r
8664         prev = *p++;\r
8665         i--;\r
8666       }\r
8667       *q = NULLCHAR;\r
8668       is->next = q;\r
8669     } else {\r
8670       if (is->error == ERROR_BROKEN_PIPE) {\r
8671         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8672         is->count = 0; \r
8673       } else {\r
8674         is->count = (DWORD) -1;\r
8675       }\r
8676     }\r
8677 \r
8678     CheckForInputBufferFull( is );\r
8679 \r
8680     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8681 \r
8682     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8683 \r
8684     if (is->count < 0) break;  /* Quit on error */\r
8685   }\r
8686   CloseHandle(is->hFile);\r
8687   return 0;\r
8688 }\r
8689 \r
8690 DWORD\r
8691 SocketInputThread(LPVOID arg)\r
8692 {\r
8693   InputSource *is;\r
8694 \r
8695   is = (InputSource *) arg;\r
8696   while (is->hThread != NULL) {\r
8697     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8698     if ((int)is->count == SOCKET_ERROR) {\r
8699       is->count = (DWORD) -1;\r
8700       is->error = WSAGetLastError();\r
8701     } else {\r
8702       is->error = NO_ERROR;\r
8703       is->next += is->count;\r
8704       if (is->count == 0 && is->second == is) {\r
8705         /* End of file on stderr; quit with no message */\r
8706         break;\r
8707       }\r
8708     }\r
8709     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8710 \r
8711     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8712 \r
8713     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8714   }\r
8715   return 0;\r
8716 }\r
8717 \r
8718 VOID\r
8719 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8720 {\r
8721   InputSource *is;\r
8722 \r
8723   is = (InputSource *) lParam;\r
8724   if (is->lineByLine) {\r
8725     /* Feed in lines one by one */\r
8726     char *p = is->buf;\r
8727     char *q = p;\r
8728     while (q < is->next) {\r
8729       if (*q++ == '\n') {\r
8730         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8731         p = q;\r
8732       }\r
8733     }\r
8734     \r
8735     /* Move any partial line to the start of the buffer */\r
8736     q = is->buf;\r
8737     while (p < is->next) {\r
8738       *q++ = *p++;\r
8739     }\r
8740     is->next = q;\r
8741 \r
8742     if (is->error != NO_ERROR || is->count == 0) {\r
8743       /* Notify backend of the error.  Note: If there was a partial\r
8744          line at the end, it is not flushed through. */\r
8745       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8746     }\r
8747   } else {\r
8748     /* Feed in the whole chunk of input at once */\r
8749     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8750     is->next = is->buf;\r
8751   }\r
8752 }\r
8753 \r
8754 /*---------------------------------------------------------------------------*\\r
8755  *\r
8756  *  Menu enables. Used when setting various modes.\r
8757  *\r
8758 \*---------------------------------------------------------------------------*/\r
8759 \r
8760 typedef struct {\r
8761   int item;\r
8762   int flags;\r
8763 } Enables;\r
8764 \r
8765 VOID\r
8766 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8767 {\r
8768   while (enab->item > 0) {\r
8769     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8770     enab++;\r
8771   }\r
8772 }\r
8773 \r
8774 Enables gnuEnables[] = {\r
8775   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8776   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8777   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8778   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8779   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8780   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8781   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8782   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8783   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8784   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8785   { -1, -1 }\r
8786 };\r
8787 \r
8788 Enables icsEnables[] = {\r
8789   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8790   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8791   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8792   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8793   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8794   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8795   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8796   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8797   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8798   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8799   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8800   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8801   { -1, -1 }\r
8802 };\r
8803 \r
8804 #ifdef ZIPPY\r
8805 Enables zippyEnables[] = {\r
8806   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8807   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8808   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8809   { -1, -1 }\r
8810 };\r
8811 #endif\r
8812 \r
8813 Enables ncpEnables[] = {\r
8814   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8815   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8816   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8817   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8818   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8819   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8820   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8821   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8822   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8823   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8824   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8825   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8826   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8827   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8828   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8829   { -1, -1 }\r
8830 };\r
8831 \r
8832 Enables trainingOnEnables[] = {\r
8833   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8834   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8835   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8836   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8837   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8838   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8839   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8840   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8841   { -1, -1 }\r
8842 };\r
8843 \r
8844 Enables trainingOffEnables[] = {\r
8845   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8846   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8847   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8848   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8849   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8850   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8851   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8852   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8853   { -1, -1 }\r
8854 };\r
8855 \r
8856 /* These modify either ncpEnables or gnuEnables */\r
8857 Enables cmailEnables[] = {\r
8858   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8859   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8860   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8861   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8862   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8863   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8864   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8865   { -1, -1 }\r
8866 };\r
8867 \r
8868 Enables machineThinkingEnables[] = {\r
8869   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8870   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8871   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8872   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8873   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8874   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8875   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8876   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8877   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8878   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8879   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8880   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8881   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8882   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8883   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8884   { -1, -1 }\r
8885 };\r
8886 \r
8887 Enables userThinkingEnables[] = {\r
8888   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8889   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8890   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8891   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8892   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8893   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8894   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8895   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8896   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8897   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8898   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8899   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8900   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8901   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8902   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8903   { -1, -1 }\r
8904 };\r
8905 \r
8906 /*---------------------------------------------------------------------------*\\r
8907  *\r
8908  *  Front-end interface functions exported by XBoard.\r
8909  *  Functions appear in same order as prototypes in frontend.h.\r
8910  * \r
8911 \*---------------------------------------------------------------------------*/\r
8912 VOID\r
8913 ModeHighlight()\r
8914 {\r
8915   static UINT prevChecked = 0;\r
8916   static int prevPausing = 0;\r
8917   UINT nowChecked;\r
8918 \r
8919   if (pausing != prevPausing) {\r
8920     prevPausing = pausing;\r
8921     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8922                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8923     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8924   }\r
8925 \r
8926   switch (gameMode) {\r
8927   case BeginningOfGame:\r
8928     if (appData.icsActive)\r
8929       nowChecked = IDM_IcsClient;\r
8930     else if (appData.noChessProgram)\r
8931       nowChecked = IDM_EditGame;\r
8932     else\r
8933       nowChecked = IDM_MachineBlack;\r
8934     break;\r
8935   case MachinePlaysBlack:\r
8936     nowChecked = IDM_MachineBlack;\r
8937     break;\r
8938   case MachinePlaysWhite:\r
8939     nowChecked = IDM_MachineWhite;\r
8940     break;\r
8941   case TwoMachinesPlay:\r
8942     nowChecked = IDM_TwoMachines;\r
8943     break;\r
8944   case AnalyzeMode:\r
8945     nowChecked = IDM_AnalysisMode;\r
8946     break;\r
8947   case AnalyzeFile:\r
8948     nowChecked = IDM_AnalyzeFile;\r
8949     break;\r
8950   case EditGame:\r
8951     nowChecked = IDM_EditGame;\r
8952     break;\r
8953   case PlayFromGameFile:\r
8954     nowChecked = IDM_LoadGame;\r
8955     break;\r
8956   case EditPosition:\r
8957     nowChecked = IDM_EditPosition;\r
8958     break;\r
8959   case Training:\r
8960     nowChecked = IDM_Training;\r
8961     break;\r
8962   case IcsPlayingWhite:\r
8963   case IcsPlayingBlack:\r
8964   case IcsObserving:\r
8965   case IcsIdle:\r
8966     nowChecked = IDM_IcsClient;\r
8967     break;\r
8968   default:\r
8969   case EndOfGame:\r
8970     nowChecked = 0;\r
8971     break;\r
8972   }\r
8973   if (prevChecked != 0)\r
8974     (void) CheckMenuItem(GetMenu(hwndMain),\r
8975                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8976   if (nowChecked != 0)\r
8977     (void) CheckMenuItem(GetMenu(hwndMain),\r
8978                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8979 \r
8980   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8981     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8982                           MF_BYCOMMAND|MF_ENABLED);\r
8983   } else {\r
8984     (void) EnableMenuItem(GetMenu(hwndMain), \r
8985                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8986   }\r
8987 \r
8988   prevChecked = nowChecked;\r
8989 \r
8990   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8991   if (appData.icsActive) {\r
8992        if (appData.icsEngineAnalyze) {\r
8993                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8994                        MF_BYCOMMAND|MF_CHECKED);\r
8995        } else {\r
8996                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8997                        MF_BYCOMMAND|MF_UNCHECKED);\r
8998        }\r
8999   }\r
9000 }\r
9001 \r
9002 VOID\r
9003 SetICSMode()\r
9004 {\r
9005   HMENU hmenu = GetMenu(hwndMain);\r
9006   SetMenuEnables(hmenu, icsEnables);\r
9007   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
9008     MF_BYPOSITION|MF_ENABLED);\r
9009 #ifdef ZIPPY\r
9010   if (appData.zippyPlay) {\r
9011     SetMenuEnables(hmenu, zippyEnables);\r
9012     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
9013          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9014           MF_BYCOMMAND|MF_ENABLED);\r
9015   }\r
9016 #endif\r
9017 }\r
9018 \r
9019 VOID\r
9020 SetGNUMode()\r
9021 {\r
9022   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
9023 }\r
9024 \r
9025 VOID\r
9026 SetNCPMode()\r
9027 {\r
9028   HMENU hmenu = GetMenu(hwndMain);\r
9029   SetMenuEnables(hmenu, ncpEnables);\r
9030   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
9031     MF_BYPOSITION|MF_GRAYED);\r
9032     DrawMenuBar(hwndMain);\r
9033 }\r
9034 \r
9035 VOID\r
9036 SetCmailMode()\r
9037 {\r
9038   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9039 }\r
9040 \r
9041 VOID \r
9042 SetTrainingModeOn()\r
9043 {\r
9044   int i;\r
9045   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9046   for (i = 0; i < N_BUTTONS; i++) {\r
9047     if (buttonDesc[i].hwnd != NULL)\r
9048       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9049   }\r
9050   CommentPopDown();\r
9051 }\r
9052 \r
9053 VOID SetTrainingModeOff()\r
9054 {\r
9055   int i;\r
9056   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9057   for (i = 0; i < N_BUTTONS; i++) {\r
9058     if (buttonDesc[i].hwnd != NULL)\r
9059       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9060   }\r
9061 }\r
9062 \r
9063 \r
9064 VOID\r
9065 SetUserThinkingEnables()\r
9066 {\r
9067   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9068 }\r
9069 \r
9070 VOID\r
9071 SetMachineThinkingEnables()\r
9072 {\r
9073   HMENU hMenu = GetMenu(hwndMain);\r
9074   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9075 \r
9076   SetMenuEnables(hMenu, machineThinkingEnables);\r
9077 \r
9078   if (gameMode == MachinePlaysBlack) {\r
9079     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9080   } else if (gameMode == MachinePlaysWhite) {\r
9081     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9082   } else if (gameMode == TwoMachinesPlay) {\r
9083     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9084   }\r
9085 }\r
9086 \r
9087 \r
9088 VOID\r
9089 DisplayTitle(char *str)\r
9090 {\r
9091   char title[MSG_SIZ], *host;\r
9092   if (str[0] != NULLCHAR) {\r
9093     strcpy(title, str);\r
9094   } else if (appData.icsActive) {\r
9095     if (appData.icsCommPort[0] != NULLCHAR)\r
9096       host = "ICS";\r
9097     else \r
9098       host = appData.icsHost;\r
9099     sprintf(title, "%s: %s", szTitle, host);\r
9100   } else if (appData.noChessProgram) {\r
9101     strcpy(title, szTitle);\r
9102   } else {\r
9103     strcpy(title, szTitle);\r
9104     strcat(title, ": ");\r
9105     strcat(title, first.tidy);\r
9106   }\r
9107   SetWindowText(hwndMain, title);\r
9108 }\r
9109 \r
9110 \r
9111 VOID\r
9112 DisplayMessage(char *str1, char *str2)\r
9113 {\r
9114   HDC hdc;\r
9115   HFONT oldFont;\r
9116   int remain = MESSAGE_TEXT_MAX - 1;\r
9117   int len;\r
9118 \r
9119   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9120   messageText[0] = NULLCHAR;\r
9121   if (*str1) {\r
9122     len = strlen(str1);\r
9123     if (len > remain) len = remain;\r
9124     strncpy(messageText, str1, len);\r
9125     messageText[len] = NULLCHAR;\r
9126     remain -= len;\r
9127   }\r
9128   if (*str2 && remain >= 2) {\r
9129     if (*str1) {\r
9130       strcat(messageText, "  ");\r
9131       remain -= 2;\r
9132     }\r
9133     len = strlen(str2);\r
9134     if (len > remain) len = remain;\r
9135     strncat(messageText, str2, len);\r
9136   }\r
9137   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9138 \r
9139   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9140 \r
9141   SAYMACHINEMOVE();\r
9142 \r
9143   hdc = GetDC(hwndMain);\r
9144   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9145   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9146              &messageRect, messageText, strlen(messageText), NULL);\r
9147   (void) SelectObject(hdc, oldFont);\r
9148   (void) ReleaseDC(hwndMain, hdc);\r
9149 }\r
9150 \r
9151 VOID\r
9152 DisplayError(char *str, int error)\r
9153 {\r
9154   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9155   int len;\r
9156 \r
9157   if (error == 0) {\r
9158     strcpy(buf, str);\r
9159   } else {\r
9160     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9161                         NULL, error, LANG_NEUTRAL,\r
9162                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9163     if (len > 0) {\r
9164       sprintf(buf, "%s:\n%s", str, buf2);\r
9165     } else {\r
9166       ErrorMap *em = errmap;\r
9167       while (em->err != 0 && em->err != error) em++;\r
9168       if (em->err != 0) {\r
9169         sprintf(buf, "%s:\n%s", str, em->msg);\r
9170       } else {\r
9171         sprintf(buf, "%s:\nError code %d", str, error);\r
9172       }\r
9173     }\r
9174   }\r
9175   \r
9176   ErrorPopUp("Error", buf);\r
9177 }\r
9178 \r
9179 \r
9180 VOID\r
9181 DisplayMoveError(char *str)\r
9182 {\r
9183   fromX = fromY = -1;\r
9184   ClearHighlights();\r
9185   DrawPosition(FALSE, NULL);\r
9186   if (appData.popupMoveErrors) {\r
9187     ErrorPopUp("Error", str);\r
9188   } else {\r
9189     DisplayMessage(str, "");\r
9190     moveErrorMessageUp = TRUE;\r
9191   }\r
9192 }\r
9193 \r
9194 VOID\r
9195 DisplayFatalError(char *str, int error, int exitStatus)\r
9196 {\r
9197   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9198   int len;\r
9199   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9200 \r
9201   if (error != 0) {\r
9202     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9203                         NULL, error, LANG_NEUTRAL,\r
9204                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9205     if (len > 0) {\r
9206       sprintf(buf, "%s:\n%s", str, buf2);\r
9207     } else {\r
9208       ErrorMap *em = errmap;\r
9209       while (em->err != 0 && em->err != error) em++;\r
9210       if (em->err != 0) {\r
9211         sprintf(buf, "%s:\n%s", str, em->msg);\r
9212       } else {\r
9213         sprintf(buf, "%s:\nError code %d", str, error);\r
9214       }\r
9215     }\r
9216     str = buf;\r
9217   }\r
9218   if (appData.debugMode) {\r
9219     fprintf(debugFP, "%s: %s\n", label, str);\r
9220   }\r
9221   if (appData.popupExitMessage) {\r
9222     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9223                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9224   }\r
9225   ExitEvent(exitStatus);\r
9226 }\r
9227 \r
9228 \r
9229 VOID\r
9230 DisplayInformation(char *str)\r
9231 {\r
9232   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9233 }\r
9234 \r
9235 \r
9236 VOID\r
9237 DisplayNote(char *str)\r
9238 {\r
9239   ErrorPopUp("Note", str);\r
9240 }\r
9241 \r
9242 \r
9243 typedef struct {\r
9244   char *title, *question, *replyPrefix;\r
9245   ProcRef pr;\r
9246 } QuestionParams;\r
9247 \r
9248 LRESULT CALLBACK\r
9249 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9250 {\r
9251   static QuestionParams *qp;\r
9252   char reply[MSG_SIZ];\r
9253   int len, err;\r
9254 \r
9255   switch (message) {\r
9256   case WM_INITDIALOG:\r
9257     qp = (QuestionParams *) lParam;\r
9258     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9259     SetWindowText(hDlg, qp->title);\r
9260     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9261     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9262     return FALSE;\r
9263 \r
9264   case WM_COMMAND:\r
9265     switch (LOWORD(wParam)) {\r
9266     case IDOK:\r
9267       strcpy(reply, qp->replyPrefix);\r
9268       if (*reply) strcat(reply, " ");\r
9269       len = strlen(reply);\r
9270       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9271       strcat(reply, "\n");\r
9272       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9273       EndDialog(hDlg, TRUE);\r
9274       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9275       return TRUE;\r
9276     case IDCANCEL:\r
9277       EndDialog(hDlg, FALSE);\r
9278       return TRUE;\r
9279     default:\r
9280       break;\r
9281     }\r
9282     break;\r
9283   }\r
9284   return FALSE;\r
9285 }\r
9286 \r
9287 VOID\r
9288 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9289 {\r
9290     QuestionParams qp;\r
9291     FARPROC lpProc;\r
9292     \r
9293     qp.title = title;\r
9294     qp.question = question;\r
9295     qp.replyPrefix = replyPrefix;\r
9296     qp.pr = pr;\r
9297     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9298     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9299       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9300     FreeProcInstance(lpProc);\r
9301 }\r
9302 \r
9303 /* [AS] Pick FRC position */\r
9304 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9305 {\r
9306     static int * lpIndexFRC;\r
9307     BOOL index_is_ok;\r
9308     char buf[16];\r
9309 \r
9310     switch( message )\r
9311     {\r
9312     case WM_INITDIALOG:\r
9313         lpIndexFRC = (int *) lParam;\r
9314 \r
9315         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9316 \r
9317         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9318         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9319         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9320         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9321 \r
9322         break;\r
9323 \r
9324     case WM_COMMAND:\r
9325         switch( LOWORD(wParam) ) {\r
9326         case IDOK:\r
9327             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9328             EndDialog( hDlg, 0 );\r
9329             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9330             return TRUE;\r
9331         case IDCANCEL:\r
9332             EndDialog( hDlg, 1 );   \r
9333             return TRUE;\r
9334         case IDC_NFG_Edit:\r
9335             if( HIWORD(wParam) == EN_CHANGE ) {\r
9336                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9337 \r
9338                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9339             }\r
9340             return TRUE;\r
9341         case IDC_NFG_Random:\r
9342             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9343             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9344             return TRUE;\r
9345         }\r
9346 \r
9347         break;\r
9348     }\r
9349 \r
9350     return FALSE;\r
9351 }\r
9352 \r
9353 int NewGameFRC()\r
9354 {\r
9355     int result;\r
9356     int index = appData.defaultFrcPosition;\r
9357     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9358 \r
9359     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9360 \r
9361     if( result == 0 ) {\r
9362         appData.defaultFrcPosition = index;\r
9363     }\r
9364 \r
9365     return result;\r
9366 }\r
9367 \r
9368 /* [AS] Game list options */\r
9369 typedef struct {\r
9370     char id;\r
9371     char * name;\r
9372 } GLT_Item;\r
9373 \r
9374 static GLT_Item GLT_ItemInfo[] = {\r
9375     { GLT_EVENT,      "Event" },\r
9376     { GLT_SITE,       "Site" },\r
9377     { GLT_DATE,       "Date" },\r
9378     { GLT_ROUND,      "Round" },\r
9379     { GLT_PLAYERS,    "Players" },\r
9380     { GLT_RESULT,     "Result" },\r
9381     { GLT_WHITE_ELO,  "White Rating" },\r
9382     { GLT_BLACK_ELO,  "Black Rating" },\r
9383     { GLT_TIME_CONTROL,"Time Control" },\r
9384     { GLT_VARIANT,    "Variant" },\r
9385     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9386     { 0, 0 }\r
9387 };\r
9388 \r
9389 const char * GLT_FindItem( char id )\r
9390 {\r
9391     const char * result = 0;\r
9392 \r
9393     GLT_Item * list = GLT_ItemInfo;\r
9394 \r
9395     while( list->id != 0 ) {\r
9396         if( list->id == id ) {\r
9397             result = list->name;\r
9398             break;\r
9399         }\r
9400 \r
9401         list++;\r
9402     }\r
9403 \r
9404     return result;\r
9405 }\r
9406 \r
9407 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9408 {\r
9409     const char * name = GLT_FindItem( id );\r
9410 \r
9411     if( name != 0 ) {\r
9412         if( index >= 0 ) {\r
9413             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9414         }\r
9415         else {\r
9416             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9417         }\r
9418     }\r
9419 }\r
9420 \r
9421 void GLT_TagsToList( HWND hDlg, char * tags )\r
9422 {\r
9423     char * pc = tags;\r
9424 \r
9425     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9426 \r
9427     while( *pc ) {\r
9428         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9429         pc++;\r
9430     }\r
9431 \r
9432     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9433 \r
9434     pc = GLT_ALL_TAGS;\r
9435 \r
9436     while( *pc ) {\r
9437         if( strchr( tags, *pc ) == 0 ) {\r
9438             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9439         }\r
9440         pc++;\r
9441     }\r
9442 \r
9443     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9444 }\r
9445 \r
9446 char GLT_ListItemToTag( HWND hDlg, int index )\r
9447 {\r
9448     char result = '\0';\r
9449     char name[128];\r
9450 \r
9451     GLT_Item * list = GLT_ItemInfo;\r
9452 \r
9453     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9454         while( list->id != 0 ) {\r
9455             if( strcmp( list->name, name ) == 0 ) {\r
9456                 result = list->id;\r
9457                 break;\r
9458             }\r
9459 \r
9460             list++;\r
9461         }\r
9462     }\r
9463 \r
9464     return result;\r
9465 }\r
9466 \r
9467 void GLT_MoveSelection( HWND hDlg, int delta )\r
9468 {\r
9469     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9470     int idx2 = idx1 + delta;\r
9471     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9472 \r
9473     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9474         char buf[128];\r
9475 \r
9476         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9477         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9478         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9479         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9480     }\r
9481 }\r
9482 \r
9483 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9484 {\r
9485     static char glt[64];\r
9486     static char * lpUserGLT;\r
9487 \r
9488     switch( message )\r
9489     {\r
9490     case WM_INITDIALOG:\r
9491         lpUserGLT = (char *) lParam;\r
9492         \r
9493         strcpy( glt, lpUserGLT );\r
9494 \r
9495         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9496 \r
9497         /* Initialize list */\r
9498         GLT_TagsToList( hDlg, glt );\r
9499 \r
9500         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9501 \r
9502         break;\r
9503 \r
9504     case WM_COMMAND:\r
9505         switch( LOWORD(wParam) ) {\r
9506         case IDOK:\r
9507             {\r
9508                 char * pc = lpUserGLT;\r
9509                 int idx = 0;\r
9510 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9511                 char id;\r
9512 \r
9513                 do {\r
9514                     id = GLT_ListItemToTag( hDlg, idx );\r
9515 \r
9516                     *pc++ = id;\r
9517                     idx++;\r
9518                 } while( id != '\0' );\r
9519             }\r
9520             EndDialog( hDlg, 0 );\r
9521             return TRUE;\r
9522         case IDCANCEL:\r
9523             EndDialog( hDlg, 1 );\r
9524             return TRUE;\r
9525 \r
9526         case IDC_GLT_Default:\r
9527             strcpy( glt, GLT_DEFAULT_TAGS );\r
9528             GLT_TagsToList( hDlg, glt );\r
9529             return TRUE;\r
9530 \r
9531         case IDC_GLT_Restore:\r
9532             strcpy( glt, lpUserGLT );\r
9533             GLT_TagsToList( hDlg, glt );\r
9534             return TRUE;\r
9535 \r
9536         case IDC_GLT_Up:\r
9537             GLT_MoveSelection( hDlg, -1 );\r
9538             return TRUE;\r
9539 \r
9540         case IDC_GLT_Down:\r
9541             GLT_MoveSelection( hDlg, +1 );\r
9542             return TRUE;\r
9543         }\r
9544 \r
9545         break;\r
9546     }\r
9547 \r
9548     return FALSE;\r
9549 }\r
9550 \r
9551 int GameListOptions()\r
9552 {\r
9553     char glt[64];\r
9554     int result;\r
9555     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9556 \r
9557     strcpy( glt, appData.gameListTags );\r
9558 \r
9559     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9560 \r
9561     if( result == 0 ) {\r
9562         /* [AS] Memory leak here! */\r
9563         appData.gameListTags = strdup( glt ); \r
9564     }\r
9565 \r
9566     return result;\r
9567 }\r
9568 \r
9569 \r
9570 VOID\r
9571 DisplayIcsInteractionTitle(char *str)\r
9572 {\r
9573   char consoleTitle[MSG_SIZ];\r
9574 \r
9575   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9576   SetWindowText(hwndConsole, consoleTitle);\r
9577 }\r
9578 \r
9579 void\r
9580 DrawPosition(int fullRedraw, Board board)\r
9581 {\r
9582   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9583 }\r
9584 \r
9585 \r
9586 VOID\r
9587 ResetFrontEnd()\r
9588 {\r
9589   fromX = fromY = -1;\r
9590   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9591     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9592     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9593     dragInfo.lastpos = dragInfo.pos;\r
9594     dragInfo.start.x = dragInfo.start.y = -1;\r
9595     dragInfo.from = dragInfo.start;\r
9596     ReleaseCapture();\r
9597     DrawPosition(TRUE, NULL);\r
9598   }\r
9599 }\r
9600 \r
9601 \r
9602 VOID\r
9603 CommentPopUp(char *title, char *str)\r
9604 {\r
9605   HWND hwnd = GetActiveWindow();\r
9606   EitherCommentPopUp(0, title, str, FALSE);\r
9607   SetActiveWindow(hwnd);\r
9608 }\r
9609 \r
9610 VOID\r
9611 CommentPopDown(void)\r
9612 {\r
9613   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9614   if (commentDialog) {\r
9615     ShowWindow(commentDialog, SW_HIDE);\r
9616   }\r
9617   commentDialogUp = FALSE;\r
9618 }\r
9619 \r
9620 VOID\r
9621 EditCommentPopUp(int index, char *title, char *str)\r
9622 {\r
9623   EitherCommentPopUp(index, title, str, TRUE);\r
9624 }\r
9625 \r
9626 \r
9627 VOID\r
9628 RingBell()\r
9629 {\r
9630   MyPlaySound(&sounds[(int)SoundMove]);\r
9631 }\r
9632 \r
9633 VOID PlayIcsWinSound()\r
9634 {\r
9635   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9636 }\r
9637 \r
9638 VOID PlayIcsLossSound()\r
9639 {\r
9640   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9641 }\r
9642 \r
9643 VOID PlayIcsDrawSound()\r
9644 {\r
9645   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9646 }\r
9647 \r
9648 VOID PlayIcsUnfinishedSound()\r
9649 {\r
9650   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9651 }\r
9652 \r
9653 VOID\r
9654 PlayAlarmSound()\r
9655 {\r
9656   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9657 }\r
9658 \r
9659 \r
9660 VOID\r
9661 EchoOn()\r
9662 {\r
9663   HWND hInput;\r
9664   consoleEcho = TRUE;\r
9665   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9666   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9667   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9668 }\r
9669 \r
9670 \r
9671 VOID\r
9672 EchoOff()\r
9673 {\r
9674   CHARFORMAT cf;\r
9675   HWND hInput;\r
9676   consoleEcho = FALSE;\r
9677   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9678   /* This works OK: set text and background both to the same color */\r
9679   cf = consoleCF;\r
9680   cf.crTextColor = COLOR_ECHOOFF;\r
9681   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9682   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9683 }\r
9684 \r
9685 /* No Raw()...? */\r
9686 \r
9687 void Colorize(ColorClass cc, int continuation)\r
9688 {\r
9689   currentColorClass = cc;\r
9690   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9691   consoleCF.crTextColor = textAttribs[cc].color;\r
9692   consoleCF.dwEffects = textAttribs[cc].effects;\r
9693   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9694 }\r
9695 \r
9696 char *\r
9697 UserName()\r
9698 {\r
9699   static char buf[MSG_SIZ];\r
9700   DWORD bufsiz = MSG_SIZ;\r
9701 \r
9702   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9703         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9704   }\r
9705   if (!GetUserName(buf, &bufsiz)) {\r
9706     /*DisplayError("Error getting user name", GetLastError());*/\r
9707     strcpy(buf, "User");\r
9708   }\r
9709   return buf;\r
9710 }\r
9711 \r
9712 char *\r
9713 HostName()\r
9714 {\r
9715   static char buf[MSG_SIZ];\r
9716   DWORD bufsiz = MSG_SIZ;\r
9717 \r
9718   if (!GetComputerName(buf, &bufsiz)) {\r
9719     /*DisplayError("Error getting host name", GetLastError());*/\r
9720     strcpy(buf, "Unknown");\r
9721   }\r
9722   return buf;\r
9723 }\r
9724 \r
9725 \r
9726 int\r
9727 ClockTimerRunning()\r
9728 {\r
9729   return clockTimerEvent != 0;\r
9730 }\r
9731 \r
9732 int\r
9733 StopClockTimer()\r
9734 {\r
9735   if (clockTimerEvent == 0) return FALSE;\r
9736   KillTimer(hwndMain, clockTimerEvent);\r
9737   clockTimerEvent = 0;\r
9738   return TRUE;\r
9739 }\r
9740 \r
9741 void\r
9742 StartClockTimer(long millisec)\r
9743 {\r
9744   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9745                              (UINT) millisec, NULL);\r
9746 }\r
9747 \r
9748 void\r
9749 DisplayWhiteClock(long timeRemaining, int highlight)\r
9750 {\r
9751   HDC hdc;\r
9752   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9753 \r
9754   if(appData.noGUI) return;\r
9755   hdc = GetDC(hwndMain);\r
9756   if (!IsIconic(hwndMain)) {\r
9757     DisplayAClock(hdc, timeRemaining, highlight, \r
9758                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9759   }\r
9760   if (highlight && iconCurrent == iconBlack) {\r
9761     iconCurrent = iconWhite;\r
9762     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9763     if (IsIconic(hwndMain)) {\r
9764       DrawIcon(hdc, 2, 2, iconCurrent);\r
9765     }\r
9766   }\r
9767   (void) ReleaseDC(hwndMain, hdc);\r
9768   if (hwndConsole)\r
9769     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9770 }\r
9771 \r
9772 void\r
9773 DisplayBlackClock(long timeRemaining, int highlight)\r
9774 {\r
9775   HDC hdc;\r
9776   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9777 \r
9778   if(appData.noGUI) return;\r
9779   hdc = GetDC(hwndMain);\r
9780   if (!IsIconic(hwndMain)) {\r
9781     DisplayAClock(hdc, timeRemaining, highlight, \r
9782                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9783   }\r
9784   if (highlight && iconCurrent == iconWhite) {\r
9785     iconCurrent = iconBlack;\r
9786     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9787     if (IsIconic(hwndMain)) {\r
9788       DrawIcon(hdc, 2, 2, iconCurrent);\r
9789     }\r
9790   }\r
9791   (void) ReleaseDC(hwndMain, hdc);\r
9792   if (hwndConsole)\r
9793     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9794 }\r
9795 \r
9796 \r
9797 int\r
9798 LoadGameTimerRunning()\r
9799 {\r
9800   return loadGameTimerEvent != 0;\r
9801 }\r
9802 \r
9803 int\r
9804 StopLoadGameTimer()\r
9805 {\r
9806   if (loadGameTimerEvent == 0) return FALSE;\r
9807   KillTimer(hwndMain, loadGameTimerEvent);\r
9808   loadGameTimerEvent = 0;\r
9809   return TRUE;\r
9810 }\r
9811 \r
9812 void\r
9813 StartLoadGameTimer(long millisec)\r
9814 {\r
9815   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9816                                 (UINT) millisec, NULL);\r
9817 }\r
9818 \r
9819 void\r
9820 AutoSaveGame()\r
9821 {\r
9822   char *defName;\r
9823   FILE *f;\r
9824   char fileTitle[MSG_SIZ];\r
9825 \r
9826   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9827   f = OpenFileDialog(hwndMain, "a", defName,\r
9828                      appData.oldSaveStyle ? "gam" : "pgn",\r
9829                      GAME_FILT, \r
9830                      "Save Game to File", NULL, fileTitle, NULL);\r
9831   if (f != NULL) {\r
9832     SaveGame(f, 0, "");\r
9833     fclose(f);\r
9834   }\r
9835 }\r
9836 \r
9837 \r
9838 void\r
9839 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9840 {\r
9841   if (delayedTimerEvent != 0) {\r
9842     if (appData.debugMode) {\r
9843       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9844     }\r
9845     KillTimer(hwndMain, delayedTimerEvent);\r
9846     delayedTimerEvent = 0;\r
9847     delayedTimerCallback();\r
9848   }\r
9849   delayedTimerCallback = cb;\r
9850   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9851                                 (UINT) millisec, NULL);\r
9852 }\r
9853 \r
9854 DelayedEventCallback\r
9855 GetDelayedEvent()\r
9856 {\r
9857   if (delayedTimerEvent) {\r
9858     return delayedTimerCallback;\r
9859   } else {\r
9860     return NULL;\r
9861   }\r
9862 }\r
9863 \r
9864 void\r
9865 CancelDelayedEvent()\r
9866 {\r
9867   if (delayedTimerEvent) {\r
9868     KillTimer(hwndMain, delayedTimerEvent);\r
9869     delayedTimerEvent = 0;\r
9870   }\r
9871 }\r
9872 \r
9873 DWORD GetWin32Priority(int nice)\r
9874 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9875 /*\r
9876 REALTIME_PRIORITY_CLASS     0x00000100\r
9877 HIGH_PRIORITY_CLASS         0x00000080\r
9878 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9879 NORMAL_PRIORITY_CLASS       0x00000020\r
9880 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9881 IDLE_PRIORITY_CLASS         0x00000040\r
9882 */\r
9883         if (nice < -15) return 0x00000080;\r
9884         if (nice < 0)   return 0x00008000;\r
9885         if (nice == 0)  return 0x00000020;\r
9886         if (nice < 15)  return 0x00004000;\r
9887         return 0x00000040;\r
9888 }\r
9889 \r
9890 /* Start a child process running the given program.\r
9891    The process's standard output can be read from "from", and its\r
9892    standard input can be written to "to".\r
9893    Exit with fatal error if anything goes wrong.\r
9894    Returns an opaque pointer that can be used to destroy the process\r
9895    later.\r
9896 */\r
9897 int\r
9898 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9899 {\r
9900 #define BUFSIZE 4096\r
9901 \r
9902   HANDLE hChildStdinRd, hChildStdinWr,\r
9903     hChildStdoutRd, hChildStdoutWr;\r
9904   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9905   SECURITY_ATTRIBUTES saAttr;\r
9906   BOOL fSuccess;\r
9907   PROCESS_INFORMATION piProcInfo;\r
9908   STARTUPINFO siStartInfo;\r
9909   ChildProc *cp;\r
9910   char buf[MSG_SIZ];\r
9911   DWORD err;\r
9912 \r
9913   if (appData.debugMode) {\r
9914     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9915   }\r
9916 \r
9917   *pr = NoProc;\r
9918 \r
9919   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9920   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9921   saAttr.bInheritHandle = TRUE;\r
9922   saAttr.lpSecurityDescriptor = NULL;\r
9923 \r
9924   /*\r
9925    * The steps for redirecting child's STDOUT:\r
9926    *     1. Create anonymous pipe to be STDOUT for child.\r
9927    *     2. Create a noninheritable duplicate of read handle,\r
9928    *         and close the inheritable read handle.\r
9929    */\r
9930 \r
9931   /* Create a pipe for the child's STDOUT. */\r
9932   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9933     return GetLastError();\r
9934   }\r
9935 \r
9936   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9937   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9938                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9939                              FALSE,     /* not inherited */\r
9940                              DUPLICATE_SAME_ACCESS);\r
9941   if (! fSuccess) {\r
9942     return GetLastError();\r
9943   }\r
9944   CloseHandle(hChildStdoutRd);\r
9945 \r
9946   /*\r
9947    * The steps for redirecting child's STDIN:\r
9948    *     1. Create anonymous pipe to be STDIN for child.\r
9949    *     2. Create a noninheritable duplicate of write handle,\r
9950    *         and close the inheritable write handle.\r
9951    */\r
9952 \r
9953   /* Create a pipe for the child's STDIN. */\r
9954   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9955     return GetLastError();\r
9956   }\r
9957 \r
9958   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9959   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9960                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9961                              FALSE,     /* not inherited */\r
9962                              DUPLICATE_SAME_ACCESS);\r
9963   if (! fSuccess) {\r
9964     return GetLastError();\r
9965   }\r
9966   CloseHandle(hChildStdinWr);\r
9967 \r
9968   /* Arrange to (1) look in dir for the child .exe file, and\r
9969    * (2) have dir be the child's working directory.  Interpret\r
9970    * dir relative to the directory WinBoard loaded from. */\r
9971   GetCurrentDirectory(MSG_SIZ, buf);\r
9972   SetCurrentDirectory(installDir);\r
9973   SetCurrentDirectory(dir);\r
9974 \r
9975   /* Now create the child process. */\r
9976 \r
9977   siStartInfo.cb = sizeof(STARTUPINFO);\r
9978   siStartInfo.lpReserved = NULL;\r
9979   siStartInfo.lpDesktop = NULL;\r
9980   siStartInfo.lpTitle = NULL;\r
9981   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9982   siStartInfo.cbReserved2 = 0;\r
9983   siStartInfo.lpReserved2 = NULL;\r
9984   siStartInfo.hStdInput = hChildStdinRd;\r
9985   siStartInfo.hStdOutput = hChildStdoutWr;\r
9986   siStartInfo.hStdError = hChildStdoutWr;\r
9987 \r
9988   fSuccess = CreateProcess(NULL,\r
9989                            cmdLine,        /* command line */\r
9990                            NULL,           /* process security attributes */\r
9991                            NULL,           /* primary thread security attrs */\r
9992                            TRUE,           /* handles are inherited */\r
9993                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9994                            NULL,           /* use parent's environment */\r
9995                            NULL,\r
9996                            &siStartInfo, /* STARTUPINFO pointer */\r
9997                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9998 \r
9999   err = GetLastError();\r
10000   SetCurrentDirectory(buf); /* return to prev directory */\r
10001   if (! fSuccess) {\r
10002     return err;\r
10003   }\r
10004 \r
10005   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
10006     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
10007     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
10008   }\r
10009 \r
10010   /* Close the handles we don't need in the parent */\r
10011   CloseHandle(piProcInfo.hThread);\r
10012   CloseHandle(hChildStdinRd);\r
10013   CloseHandle(hChildStdoutWr);\r
10014 \r
10015   /* Prepare return value */\r
10016   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10017   cp->kind = CPReal;\r
10018   cp->hProcess = piProcInfo.hProcess;\r
10019   cp->pid = piProcInfo.dwProcessId;\r
10020   cp->hFrom = hChildStdoutRdDup;\r
10021   cp->hTo = hChildStdinWrDup;\r
10022 \r
10023   *pr = (void *) cp;\r
10024 \r
10025   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10026      2000 where engines sometimes don't see the initial command(s)\r
10027      from WinBoard and hang.  I don't understand how that can happen,\r
10028      but the Sleep is harmless, so I've put it in.  Others have also\r
10029      reported what may be the same problem, so hopefully this will fix\r
10030      it for them too.  */\r
10031   Sleep(500);\r
10032 \r
10033   return NO_ERROR;\r
10034 }\r
10035 \r
10036 \r
10037 void\r
10038 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10039 {\r
10040   ChildProc *cp; int result;\r
10041 \r
10042   cp = (ChildProc *) pr;\r
10043   if (cp == NULL) return;\r
10044 \r
10045   switch (cp->kind) {\r
10046   case CPReal:\r
10047     /* TerminateProcess is considered harmful, so... */\r
10048     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10049     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10050     /* The following doesn't work because the chess program\r
10051        doesn't "have the same console" as WinBoard.  Maybe\r
10052        we could arrange for this even though neither WinBoard\r
10053        nor the chess program uses a console for stdio? */\r
10054     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10055 \r
10056     /* [AS] Special termination modes for misbehaving programs... */\r
10057     if( signal == 9 ) { \r
10058         result = TerminateProcess( cp->hProcess, 0 );\r
10059 \r
10060         if ( appData.debugMode) {\r
10061             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10062         }\r
10063     }\r
10064     else if( signal == 10 ) {\r
10065         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10066 \r
10067         if( dw != WAIT_OBJECT_0 ) {\r
10068             result = TerminateProcess( cp->hProcess, 0 );\r
10069 \r
10070             if ( appData.debugMode) {\r
10071                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10072             }\r
10073 \r
10074         }\r
10075     }\r
10076 \r
10077     CloseHandle(cp->hProcess);\r
10078     break;\r
10079 \r
10080   case CPComm:\r
10081     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10082     break;\r
10083 \r
10084   case CPSock:\r
10085     closesocket(cp->sock);\r
10086     WSACleanup();\r
10087     break;\r
10088 \r
10089   case CPRcmd:\r
10090     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10091     closesocket(cp->sock);\r
10092     closesocket(cp->sock2);\r
10093     WSACleanup();\r
10094     break;\r
10095   }\r
10096   free(cp);\r
10097 }\r
10098 \r
10099 void\r
10100 InterruptChildProcess(ProcRef pr)\r
10101 {\r
10102   ChildProc *cp;\r
10103 \r
10104   cp = (ChildProc *) pr;\r
10105   if (cp == NULL) return;\r
10106   switch (cp->kind) {\r
10107   case CPReal:\r
10108     /* The following doesn't work because the chess program\r
10109        doesn't "have the same console" as WinBoard.  Maybe\r
10110        we could arrange for this even though neither WinBoard\r
10111        nor the chess program uses a console for stdio */\r
10112     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10113     break;\r
10114 \r
10115   case CPComm:\r
10116   case CPSock:\r
10117     /* Can't interrupt */\r
10118     break;\r
10119 \r
10120   case CPRcmd:\r
10121     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10122     break;\r
10123   }\r
10124 }\r
10125 \r
10126 \r
10127 int\r
10128 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10129 {\r
10130   char cmdLine[MSG_SIZ];\r
10131 \r
10132   if (port[0] == NULLCHAR) {\r
10133     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10134   } else {\r
10135     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10136   }\r
10137   return StartChildProcess(cmdLine, "", pr);\r
10138 }\r
10139 \r
10140 \r
10141 /* Code to open TCP sockets */\r
10142 \r
10143 int\r
10144 OpenTCP(char *host, char *port, ProcRef *pr)\r
10145 {\r
10146   ChildProc *cp;\r
10147   int err;\r
10148   SOCKET s;\r
10149   struct sockaddr_in sa, mysa;\r
10150   struct hostent FAR *hp;\r
10151   unsigned short uport;\r
10152   WORD wVersionRequested;\r
10153   WSADATA wsaData;\r
10154 \r
10155   /* Initialize socket DLL */\r
10156   wVersionRequested = MAKEWORD(1, 1);\r
10157   err = WSAStartup(wVersionRequested, &wsaData);\r
10158   if (err != 0) return err;\r
10159 \r
10160   /* Make socket */\r
10161   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10162     err = WSAGetLastError();\r
10163     WSACleanup();\r
10164     return err;\r
10165   }\r
10166 \r
10167   /* Bind local address using (mostly) don't-care values.\r
10168    */\r
10169   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10170   mysa.sin_family = AF_INET;\r
10171   mysa.sin_addr.s_addr = INADDR_ANY;\r
10172   uport = (unsigned short) 0;\r
10173   mysa.sin_port = htons(uport);\r
10174   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10175       == SOCKET_ERROR) {\r
10176     err = WSAGetLastError();\r
10177     WSACleanup();\r
10178     return err;\r
10179   }\r
10180 \r
10181   /* Resolve remote host name */\r
10182   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10183   if (!(hp = gethostbyname(host))) {\r
10184     unsigned int b0, b1, b2, b3;\r
10185 \r
10186     err = WSAGetLastError();\r
10187 \r
10188     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10189       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10190       hp->h_addrtype = AF_INET;\r
10191       hp->h_length = 4;\r
10192       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10193       hp->h_addr_list[0] = (char *) malloc(4);\r
10194       hp->h_addr_list[0][0] = (char) b0;\r
10195       hp->h_addr_list[0][1] = (char) b1;\r
10196       hp->h_addr_list[0][2] = (char) b2;\r
10197       hp->h_addr_list[0][3] = (char) b3;\r
10198     } else {\r
10199       WSACleanup();\r
10200       return err;\r
10201     }\r
10202   }\r
10203   sa.sin_family = hp->h_addrtype;\r
10204   uport = (unsigned short) atoi(port);\r
10205   sa.sin_port = htons(uport);\r
10206   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10207 \r
10208   /* Make connection */\r
10209   if (connect(s, (struct sockaddr *) &sa,\r
10210               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10211     err = WSAGetLastError();\r
10212     WSACleanup();\r
10213     return err;\r
10214   }\r
10215 \r
10216   /* Prepare return value */\r
10217   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10218   cp->kind = CPSock;\r
10219   cp->sock = s;\r
10220   *pr = (ProcRef *) cp;\r
10221 \r
10222   return NO_ERROR;\r
10223 }\r
10224 \r
10225 int\r
10226 OpenCommPort(char *name, ProcRef *pr)\r
10227 {\r
10228   HANDLE h;\r
10229   COMMTIMEOUTS ct;\r
10230   ChildProc *cp;\r
10231   char fullname[MSG_SIZ];\r
10232 \r
10233   if (*name != '\\')\r
10234     sprintf(fullname, "\\\\.\\%s", name);\r
10235   else\r
10236     strcpy(fullname, name);\r
10237 \r
10238   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10239                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10240   if (h == (HANDLE) -1) {\r
10241     return GetLastError();\r
10242   }\r
10243   hCommPort = h;\r
10244 \r
10245   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10246 \r
10247   /* Accumulate characters until a 100ms pause, then parse */\r
10248   ct.ReadIntervalTimeout = 100;\r
10249   ct.ReadTotalTimeoutMultiplier = 0;\r
10250   ct.ReadTotalTimeoutConstant = 0;\r
10251   ct.WriteTotalTimeoutMultiplier = 0;\r
10252   ct.WriteTotalTimeoutConstant = 0;\r
10253   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10254 \r
10255   /* Prepare return value */\r
10256   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10257   cp->kind = CPComm;\r
10258   cp->hFrom = h;\r
10259   cp->hTo = h;\r
10260   *pr = (ProcRef *) cp;\r
10261 \r
10262   return NO_ERROR;\r
10263 }\r
10264 \r
10265 int\r
10266 OpenLoopback(ProcRef *pr)\r
10267 {\r
10268   DisplayFatalError("Not implemented", 0, 1);\r
10269   return NO_ERROR;\r
10270 }\r
10271 \r
10272 \r
10273 int\r
10274 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10275 {\r
10276   ChildProc *cp;\r
10277   int err;\r
10278   SOCKET s, s2, s3;\r
10279   struct sockaddr_in sa, mysa;\r
10280   struct hostent FAR *hp;\r
10281   unsigned short uport;\r
10282   WORD wVersionRequested;\r
10283   WSADATA wsaData;\r
10284   int fromPort;\r
10285   char stderrPortStr[MSG_SIZ];\r
10286 \r
10287   /* Initialize socket DLL */\r
10288   wVersionRequested = MAKEWORD(1, 1);\r
10289   err = WSAStartup(wVersionRequested, &wsaData);\r
10290   if (err != 0) return err;\r
10291 \r
10292   /* Resolve remote host name */\r
10293   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10294   if (!(hp = gethostbyname(host))) {\r
10295     unsigned int b0, b1, b2, b3;\r
10296 \r
10297     err = WSAGetLastError();\r
10298 \r
10299     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10300       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10301       hp->h_addrtype = AF_INET;\r
10302       hp->h_length = 4;\r
10303       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10304       hp->h_addr_list[0] = (char *) malloc(4);\r
10305       hp->h_addr_list[0][0] = (char) b0;\r
10306       hp->h_addr_list[0][1] = (char) b1;\r
10307       hp->h_addr_list[0][2] = (char) b2;\r
10308       hp->h_addr_list[0][3] = (char) b3;\r
10309     } else {\r
10310       WSACleanup();\r
10311       return err;\r
10312     }\r
10313   }\r
10314   sa.sin_family = hp->h_addrtype;\r
10315   uport = (unsigned short) 514;\r
10316   sa.sin_port = htons(uport);\r
10317   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10318 \r
10319   /* Bind local socket to unused "privileged" port address\r
10320    */\r
10321   s = INVALID_SOCKET;\r
10322   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10323   mysa.sin_family = AF_INET;\r
10324   mysa.sin_addr.s_addr = INADDR_ANY;\r
10325   for (fromPort = 1023;; fromPort--) {\r
10326     if (fromPort < 0) {\r
10327       WSACleanup();\r
10328       return WSAEADDRINUSE;\r
10329     }\r
10330     if (s == INVALID_SOCKET) {\r
10331       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10332         err = WSAGetLastError();\r
10333         WSACleanup();\r
10334         return err;\r
10335       }\r
10336     }\r
10337     uport = (unsigned short) fromPort;\r
10338     mysa.sin_port = htons(uport);\r
10339     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10340         == SOCKET_ERROR) {\r
10341       err = WSAGetLastError();\r
10342       if (err == WSAEADDRINUSE) continue;\r
10343       WSACleanup();\r
10344       return err;\r
10345     }\r
10346     if (connect(s, (struct sockaddr *) &sa,\r
10347       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10348       err = WSAGetLastError();\r
10349       if (err == WSAEADDRINUSE) {\r
10350         closesocket(s);\r
10351         s = -1;\r
10352         continue;\r
10353       }\r
10354       WSACleanup();\r
10355       return err;\r
10356     }\r
10357     break;\r
10358   }\r
10359 \r
10360   /* Bind stderr local socket to unused "privileged" port address\r
10361    */\r
10362   s2 = INVALID_SOCKET;\r
10363   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10364   mysa.sin_family = AF_INET;\r
10365   mysa.sin_addr.s_addr = INADDR_ANY;\r
10366   for (fromPort = 1023;; fromPort--) {\r
10367     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10368     if (fromPort < 0) {\r
10369       (void) closesocket(s);\r
10370       WSACleanup();\r
10371       return WSAEADDRINUSE;\r
10372     }\r
10373     if (s2 == INVALID_SOCKET) {\r
10374       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10375         err = WSAGetLastError();\r
10376         closesocket(s);\r
10377         WSACleanup();\r
10378         return err;\r
10379       }\r
10380     }\r
10381     uport = (unsigned short) fromPort;\r
10382     mysa.sin_port = htons(uport);\r
10383     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10384         == SOCKET_ERROR) {\r
10385       err = WSAGetLastError();\r
10386       if (err == WSAEADDRINUSE) continue;\r
10387       (void) closesocket(s);\r
10388       WSACleanup();\r
10389       return err;\r
10390     }\r
10391     if (listen(s2, 1) == SOCKET_ERROR) {\r
10392       err = WSAGetLastError();\r
10393       if (err == WSAEADDRINUSE) {\r
10394         closesocket(s2);\r
10395         s2 = INVALID_SOCKET;\r
10396         continue;\r
10397       }\r
10398       (void) closesocket(s);\r
10399       (void) closesocket(s2);\r
10400       WSACleanup();\r
10401       return err;\r
10402     }\r
10403     break;\r
10404   }\r
10405   prevStderrPort = fromPort; // remember port used\r
10406   sprintf(stderrPortStr, "%d", fromPort);\r
10407 \r
10408   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10409     err = WSAGetLastError();\r
10410     (void) closesocket(s);\r
10411     (void) closesocket(s2);\r
10412     WSACleanup();\r
10413     return err;\r
10414   }\r
10415 \r
10416   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10417     err = WSAGetLastError();\r
10418     (void) closesocket(s);\r
10419     (void) closesocket(s2);\r
10420     WSACleanup();\r
10421     return err;\r
10422   }\r
10423   if (*user == NULLCHAR) user = UserName();\r
10424   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10425     err = WSAGetLastError();\r
10426     (void) closesocket(s);\r
10427     (void) closesocket(s2);\r
10428     WSACleanup();\r
10429     return err;\r
10430   }\r
10431   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10432     err = WSAGetLastError();\r
10433     (void) closesocket(s);\r
10434     (void) closesocket(s2);\r
10435     WSACleanup();\r
10436     return err;\r
10437   }\r
10438 \r
10439   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10440     err = WSAGetLastError();\r
10441     (void) closesocket(s);\r
10442     (void) closesocket(s2);\r
10443     WSACleanup();\r
10444     return err;\r
10445   }\r
10446   (void) closesocket(s2);  /* Stop listening */\r
10447 \r
10448   /* Prepare return value */\r
10449   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10450   cp->kind = CPRcmd;\r
10451   cp->sock = s;\r
10452   cp->sock2 = s3;\r
10453   *pr = (ProcRef *) cp;\r
10454 \r
10455   return NO_ERROR;\r
10456 }\r
10457 \r
10458 \r
10459 InputSourceRef\r
10460 AddInputSource(ProcRef pr, int lineByLine,\r
10461                InputCallback func, VOIDSTAR closure)\r
10462 {\r
10463   InputSource *is, *is2 = NULL;\r
10464   ChildProc *cp = (ChildProc *) pr;\r
10465 \r
10466   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10467   is->lineByLine = lineByLine;\r
10468   is->func = func;\r
10469   is->closure = closure;\r
10470   is->second = NULL;\r
10471   is->next = is->buf;\r
10472   if (pr == NoProc) {\r
10473     is->kind = CPReal;\r
10474     consoleInputSource = is;\r
10475   } else {\r
10476     is->kind = cp->kind;\r
10477     /* \r
10478         [AS] Try to avoid a race condition if the thread is given control too early:\r
10479         we create all threads suspended so that the is->hThread variable can be\r
10480         safely assigned, then let the threads start with ResumeThread.\r
10481     */\r
10482     switch (cp->kind) {\r
10483     case CPReal:\r
10484       is->hFile = cp->hFrom;\r
10485       cp->hFrom = NULL; /* now owned by InputThread */\r
10486       is->hThread =\r
10487         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10488                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10489       break;\r
10490 \r
10491     case CPComm:\r
10492       is->hFile = cp->hFrom;\r
10493       cp->hFrom = NULL; /* now owned by InputThread */\r
10494       is->hThread =\r
10495         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10496                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10497       break;\r
10498 \r
10499     case CPSock:\r
10500       is->sock = cp->sock;\r
10501       is->hThread =\r
10502         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10503                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10504       break;\r
10505 \r
10506     case CPRcmd:\r
10507       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10508       *is2 = *is;\r
10509       is->sock = cp->sock;\r
10510       is->second = is2;\r
10511       is2->sock = cp->sock2;\r
10512       is2->second = is2;\r
10513       is->hThread =\r
10514         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10515                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10516       is2->hThread =\r
10517         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10518                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10519       break;\r
10520     }\r
10521 \r
10522     if( is->hThread != NULL ) {\r
10523         ResumeThread( is->hThread );\r
10524     }\r
10525 \r
10526     if( is2 != NULL && is2->hThread != NULL ) {\r
10527         ResumeThread( is2->hThread );\r
10528     }\r
10529   }\r
10530 \r
10531   return (InputSourceRef) is;\r
10532 }\r
10533 \r
10534 void\r
10535 RemoveInputSource(InputSourceRef isr)\r
10536 {\r
10537   InputSource *is;\r
10538 \r
10539   is = (InputSource *) isr;\r
10540   is->hThread = NULL;  /* tell thread to stop */\r
10541   CloseHandle(is->hThread);\r
10542   if (is->second != NULL) {\r
10543     is->second->hThread = NULL;\r
10544     CloseHandle(is->second->hThread);\r
10545   }\r
10546 }\r
10547 \r
10548 \r
10549 int\r
10550 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10551 {\r
10552   DWORD dOutCount;\r
10553   int outCount = SOCKET_ERROR;\r
10554   ChildProc *cp = (ChildProc *) pr;\r
10555   static OVERLAPPED ovl;\r
10556 \r
10557   if (pr == NoProc) {\r
10558     ConsoleOutput(message, count, FALSE);\r
10559     return count;\r
10560   } \r
10561 \r
10562   if (ovl.hEvent == NULL) {\r
10563     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10564   }\r
10565   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10566 \r
10567   switch (cp->kind) {\r
10568   case CPSock:\r
10569   case CPRcmd:\r
10570     outCount = send(cp->sock, message, count, 0);\r
10571     if (outCount == SOCKET_ERROR) {\r
10572       *outError = WSAGetLastError();\r
10573     } else {\r
10574       *outError = NO_ERROR;\r
10575     }\r
10576     break;\r
10577 \r
10578   case CPReal:\r
10579     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10580                   &dOutCount, NULL)) {\r
10581       *outError = NO_ERROR;\r
10582       outCount = (int) dOutCount;\r
10583     } else {\r
10584       *outError = GetLastError();\r
10585     }\r
10586     break;\r
10587 \r
10588   case CPComm:\r
10589     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10590                             &dOutCount, &ovl);\r
10591     if (*outError == NO_ERROR) {\r
10592       outCount = (int) dOutCount;\r
10593     }\r
10594     break;\r
10595   }\r
10596   return outCount;\r
10597 }\r
10598 \r
10599 int\r
10600 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10601                        long msdelay)\r
10602 {\r
10603   /* Ignore delay, not implemented for WinBoard */\r
10604   return OutputToProcess(pr, message, count, outError);\r
10605 }\r
10606 \r
10607 \r
10608 void\r
10609 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10610                         char *buf, int count, int error)\r
10611 {\r
10612   DisplayFatalError("Not implemented", 0, 1);\r
10613 }\r
10614 \r
10615 /* see wgamelist.c for Game List functions */\r
10616 /* see wedittags.c for Edit Tags functions */\r
10617 \r
10618 \r
10619 VOID\r
10620 ICSInitScript()\r
10621 {\r
10622   FILE *f;\r
10623   char buf[MSG_SIZ];\r
10624   char *dummy;\r
10625 \r
10626   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10627     f = fopen(buf, "r");\r
10628     if (f != NULL) {\r
10629       ProcessICSInitScript(f);\r
10630       fclose(f);\r
10631     }\r
10632   }\r
10633 }\r
10634 \r
10635 \r
10636 VOID\r
10637 StartAnalysisClock()\r
10638 {\r
10639   if (analysisTimerEvent) return;\r
10640   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10641                                         (UINT) 2000, NULL);\r
10642 }\r
10643 \r
10644 LRESULT CALLBACK\r
10645 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10646 {\r
10647   static HANDLE hwndText;\r
10648   RECT rect;\r
10649   static int sizeX, sizeY;\r
10650   int newSizeX, newSizeY, flags;\r
10651   MINMAXINFO *mmi;\r
10652 \r
10653   switch (message) {\r
10654   case WM_INITDIALOG: /* message: initialize dialog box */\r
10655     /* Initialize the dialog items */\r
10656     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10657     SetWindowText(hDlg, analysisTitle);\r
10658     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10659     /* Size and position the dialog */\r
10660     if (!analysisDialog) {\r
10661       analysisDialog = hDlg;\r
10662       flags = SWP_NOZORDER;\r
10663       GetClientRect(hDlg, &rect);\r
10664       sizeX = rect.right;\r
10665       sizeY = rect.bottom;\r
10666       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10667           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10668         WINDOWPLACEMENT wp;\r
10669         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10670         wp.length = sizeof(WINDOWPLACEMENT);\r
10671         wp.flags = 0;\r
10672         wp.showCmd = SW_SHOW;\r
10673         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10674         wp.rcNormalPosition.left = analysisX;\r
10675         wp.rcNormalPosition.right = analysisX + analysisW;\r
10676         wp.rcNormalPosition.top = analysisY;\r
10677         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10678         SetWindowPlacement(hDlg, &wp);\r
10679 \r
10680         GetClientRect(hDlg, &rect);\r
10681         newSizeX = rect.right;\r
10682         newSizeY = rect.bottom;\r
10683         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10684                               newSizeX, newSizeY);\r
10685         sizeX = newSizeX;\r
10686         sizeY = newSizeY;\r
10687       }\r
10688     }\r
10689     return FALSE;\r
10690 \r
10691   case WM_COMMAND: /* message: received a command */\r
10692     switch (LOWORD(wParam)) {\r
10693     case IDCANCEL:\r
10694       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10695           ExitAnalyzeMode();\r
10696           ModeHighlight();\r
10697           return TRUE;\r
10698       }\r
10699       EditGameEvent();\r
10700       return TRUE;\r
10701     default:\r
10702       break;\r
10703     }\r
10704     break;\r
10705 \r
10706   case WM_SIZE:\r
10707     newSizeX = LOWORD(lParam);\r
10708     newSizeY = HIWORD(lParam);\r
10709     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10710     sizeX = newSizeX;\r
10711     sizeY = newSizeY;\r
10712     break;\r
10713 \r
10714   case WM_GETMINMAXINFO:\r
10715     /* Prevent resizing window too small */\r
10716     mmi = (MINMAXINFO *) lParam;\r
10717     mmi->ptMinTrackSize.x = 100;\r
10718     mmi->ptMinTrackSize.y = 100;\r
10719     break;\r
10720   }\r
10721   return FALSE;\r
10722 }\r
10723 \r
10724 VOID\r
10725 AnalysisPopUp(char* title, char* str)\r
10726 {\r
10727   FARPROC lpProc;\r
10728   char *p, *q;\r
10729 \r
10730   /* [AS] */\r
10731   EngineOutputPopUp();\r
10732   return;\r
10733 \r
10734   if (str == NULL) str = "";\r
10735   p = (char *) malloc(2 * strlen(str) + 2);\r
10736   q = p;\r
10737   while (*str) {\r
10738     if (*str == '\n') *q++ = '\r';\r
10739     *q++ = *str++;\r
10740   }\r
10741   *q = NULLCHAR;\r
10742   if (analysisText != NULL) free(analysisText);\r
10743   analysisText = p;\r
10744 \r
10745   if (analysisDialog) {\r
10746     SetWindowText(analysisDialog, title);\r
10747     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10748     ShowWindow(analysisDialog, SW_SHOW);\r
10749   } else {\r
10750     analysisTitle = title;\r
10751     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10752     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10753                  hwndMain, (DLGPROC)lpProc);\r
10754     FreeProcInstance(lpProc);\r
10755   }\r
10756   analysisDialogUp = TRUE;  \r
10757 }\r
10758 \r
10759 VOID\r
10760 AnalysisPopDown()\r
10761 {\r
10762   if (analysisDialog) {\r
10763     ShowWindow(analysisDialog, SW_HIDE);\r
10764   }\r
10765   analysisDialogUp = FALSE;  \r
10766 }\r
10767 \r
10768 \r
10769 VOID\r
10770 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10771 {\r
10772   highlightInfo.sq[0].x = fromX;\r
10773   highlightInfo.sq[0].y = fromY;\r
10774   highlightInfo.sq[1].x = toX;\r
10775   highlightInfo.sq[1].y = toY;\r
10776 }\r
10777 \r
10778 VOID\r
10779 ClearHighlights()\r
10780 {\r
10781   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10782     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10783 }\r
10784 \r
10785 VOID\r
10786 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10787 {\r
10788   premoveHighlightInfo.sq[0].x = fromX;\r
10789   premoveHighlightInfo.sq[0].y = fromY;\r
10790   premoveHighlightInfo.sq[1].x = toX;\r
10791   premoveHighlightInfo.sq[1].y = toY;\r
10792 }\r
10793 \r
10794 VOID\r
10795 ClearPremoveHighlights()\r
10796 {\r
10797   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10798     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10799 }\r
10800 \r
10801 VOID\r
10802 ShutDownFrontEnd()\r
10803 {\r
10804   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10805   DeleteClipboardTempFiles();\r
10806 }\r
10807 \r
10808 void\r
10809 BoardToTop()\r
10810 {\r
10811     if (IsIconic(hwndMain))\r
10812       ShowWindow(hwndMain, SW_RESTORE);\r
10813 \r
10814     SetActiveWindow(hwndMain);\r
10815 }\r
10816 \r
10817 /*\r
10818  * Prototypes for animation support routines\r
10819  */\r
10820 static void ScreenSquare(int column, int row, POINT * pt);\r
10821 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10822      POINT frames[], int * nFrames);\r
10823 \r
10824 \r
10825 void\r
10826 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10827 {       // [HGM] atomic: animate blast wave\r
10828         int i;\r
10829 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10830         explodeInfo.fromX = fromX;\r
10831         explodeInfo.fromY = fromY;\r
10832         explodeInfo.toX = toX;\r
10833         explodeInfo.toY = toY;\r
10834         for(i=1; i<nFrames; i++) {\r
10835             explodeInfo.radius = (i*180)/(nFrames-1);\r
10836             DrawPosition(FALSE, NULL);\r
10837             Sleep(appData.animSpeed);\r
10838         }\r
10839         explodeInfo.radius = 0;\r
10840         DrawPosition(TRUE, NULL);\r
10841 }\r
10842 \r
10843 #define kFactor 4\r
10844 \r
10845 void\r
10846 AnimateMove(board, fromX, fromY, toX, toY)\r
10847      Board board;\r
10848      int fromX;\r
10849      int fromY;\r
10850      int toX;\r
10851      int toY;\r
10852 {\r
10853   ChessSquare piece;\r
10854   POINT start, finish, mid;\r
10855   POINT frames[kFactor * 2 + 1];\r
10856   int nFrames, n;\r
10857 \r
10858   if (!appData.animate) return;\r
10859   if (doingSizing) return;\r
10860   if (fromY < 0 || fromX < 0) return;\r
10861   piece = board[fromY][fromX];\r
10862   if (piece >= EmptySquare) return;\r
10863 \r
10864   ScreenSquare(fromX, fromY, &start);\r
10865   ScreenSquare(toX, toY, &finish);\r
10866 \r
10867   /* All pieces except knights move in straight line */\r
10868   if (piece != WhiteKnight && piece != BlackKnight) {\r
10869     mid.x = start.x + (finish.x - start.x) / 2;\r
10870     mid.y = start.y + (finish.y - start.y) / 2;\r
10871   } else {\r
10872     /* Knight: make diagonal movement then straight */\r
10873     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10874        mid.x = start.x + (finish.x - start.x) / 2;\r
10875        mid.y = finish.y;\r
10876      } else {\r
10877        mid.x = finish.x;\r
10878        mid.y = start.y + (finish.y - start.y) / 2;\r
10879      }\r
10880   }\r
10881   \r
10882   /* Don't use as many frames for very short moves */\r
10883   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10884     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10885   else\r
10886     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10887 \r
10888   animInfo.from.x = fromX;\r
10889   animInfo.from.y = fromY;\r
10890   animInfo.to.x = toX;\r
10891   animInfo.to.y = toY;\r
10892   animInfo.lastpos = start;\r
10893   animInfo.piece = piece;\r
10894   for (n = 0; n < nFrames; n++) {\r
10895     animInfo.pos = frames[n];\r
10896     DrawPosition(FALSE, NULL);\r
10897     animInfo.lastpos = animInfo.pos;\r
10898     Sleep(appData.animSpeed);\r
10899   }\r
10900   animInfo.pos = finish;\r
10901   DrawPosition(FALSE, NULL);\r
10902   animInfo.piece = EmptySquare;\r
10903   if(gameInfo.variant == VariantAtomic && \r
10904      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10905         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10906 }\r
10907 \r
10908 /*      Convert board position to corner of screen rect and color       */\r
10909 \r
10910 static void\r
10911 ScreenSquare(column, row, pt)\r
10912      int column; int row; POINT * pt;\r
10913 {\r
10914   if (flipView) {\r
10915     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10916     pt->y = lineGap + row * (squareSize + lineGap);\r
10917   } else {\r
10918     pt->x = lineGap + column * (squareSize + lineGap);\r
10919     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10920   }\r
10921 }\r
10922 \r
10923 /*      Generate a series of frame coords from start->mid->finish.\r
10924         The movement rate doubles until the half way point is\r
10925         reached, then halves back down to the final destination,\r
10926         which gives a nice slow in/out effect. The algorithmn\r
10927         may seem to generate too many intermediates for short\r
10928         moves, but remember that the purpose is to attract the\r
10929         viewers attention to the piece about to be moved and\r
10930         then to where it ends up. Too few frames would be less\r
10931         noticeable.                                             */\r
10932 \r
10933 static void\r
10934 Tween(start, mid, finish, factor, frames, nFrames)\r
10935      POINT * start; POINT * mid;\r
10936      POINT * finish; int factor;\r
10937      POINT frames[]; int * nFrames;\r
10938 {\r
10939   int n, fraction = 1, count = 0;\r
10940 \r
10941   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10942   for (n = 0; n < factor; n++)\r
10943     fraction *= 2;\r
10944   for (n = 0; n < factor; n++) {\r
10945     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10946     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10947     count ++;\r
10948     fraction = fraction / 2;\r
10949   }\r
10950   \r
10951   /* Midpoint */\r
10952   frames[count] = *mid;\r
10953   count ++;\r
10954   \r
10955   /* Slow out, stepping 1/2, then 1/4, ... */\r
10956   fraction = 2;\r
10957   for (n = 0; n < factor; n++) {\r
10958     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10959     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10960     count ++;\r
10961     fraction = fraction * 2;\r
10962   }\r
10963   *nFrames = count;\r
10964 }\r
10965 \r
10966 void\r
10967 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10968 {\r
10969 #if 0\r
10970     char buf[256];\r
10971 \r
10972     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10973         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10974 \r
10975     OutputDebugString( buf );\r
10976 #endif\r
10977 \r
10978     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10979 \r
10980     EvalGraphSet( first, last, current, pvInfoList );\r
10981 }\r
10982 \r
10983 void SetProgramStats( FrontEndProgramStats * stats )\r
10984 {\r
10985 #if 0\r
10986     char buf[1024];\r
10987 \r
10988     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10989         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10990 \r
10991     OutputDebugString( buf );\r
10992 #endif\r
10993 \r
10994     EngineOutputUpdate( stats );\r
10995 }\r