added shortcuts
[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         } else if((char)wParam == 003) CopyGameToClipboard();\r
5667          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5668     }\r
5669 \r
5670     break;\r
5671 \r
5672   case WM_PALETTECHANGED:\r
5673     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5674       int nnew;\r
5675       HDC hdc = GetDC(hwndMain);\r
5676       SelectPalette(hdc, hPal, TRUE);\r
5677       nnew = RealizePalette(hdc);\r
5678       if (nnew > 0) {\r
5679         paletteChanged = TRUE;\r
5680 #if 0\r
5681         UpdateColors(hdc);\r
5682 #else\r
5683         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5684 #endif\r
5685       }\r
5686       ReleaseDC(hwnd, hdc);\r
5687     }\r
5688     break;\r
5689 \r
5690   case WM_QUERYNEWPALETTE:\r
5691     if (!appData.monoMode /*&& paletteChanged*/) {\r
5692       int nnew;\r
5693       HDC hdc = GetDC(hwndMain);\r
5694       paletteChanged = FALSE;\r
5695       SelectPalette(hdc, hPal, FALSE);\r
5696       nnew = RealizePalette(hdc);\r
5697       if (nnew > 0) {\r
5698         InvalidateRect(hwnd, &boardRect, FALSE);\r
5699       }\r
5700       ReleaseDC(hwnd, hdc);\r
5701       return TRUE;\r
5702     }\r
5703     return FALSE;\r
5704 \r
5705   case WM_COMMAND: /* message: command from application menu */\r
5706     wmId    = LOWORD(wParam);\r
5707     wmEvent = HIWORD(wParam);\r
5708 \r
5709     switch (wmId) {\r
5710     case IDM_NewGame:\r
5711       ResetGameEvent();\r
5712       AnalysisPopDown();\r
5713       SAY("new game enter a move to play against the computer with white");\r
5714       break;\r
5715 \r
5716     case IDM_NewGameFRC:\r
5717       if( NewGameFRC() == 0 ) {\r
5718         ResetGameEvent();\r
5719         AnalysisPopDown();\r
5720       }\r
5721       break;\r
5722 \r
5723     case IDM_NewVariant:\r
5724       NewVariantPopup(hwnd);\r
5725       break;\r
5726 \r
5727     case IDM_LoadGame:\r
5728       LoadGameDialog(hwnd, "Load Game from File");\r
5729       break;\r
5730 \r
5731     case IDM_LoadNextGame:\r
5732       ReloadGame(1);\r
5733       break;\r
5734 \r
5735     case IDM_LoadPrevGame:\r
5736       ReloadGame(-1);\r
5737       break;\r
5738 \r
5739     case IDM_ReloadGame:\r
5740       ReloadGame(0);\r
5741       break;\r
5742 \r
5743     case IDM_LoadPosition:\r
5744       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5745         Reset(FALSE, TRUE);\r
5746       }\r
5747       number = 1;\r
5748       f = OpenFileDialog(hwnd, "rb", "",\r
5749                          appData.oldSaveStyle ? "pos" : "fen",\r
5750                          POSITION_FILT,\r
5751                          "Load Position from File", &number, fileTitle, NULL);\r
5752       if (f != NULL) {\r
5753         LoadPosition(f, number, fileTitle);\r
5754       }\r
5755       break;\r
5756 \r
5757     case IDM_LoadNextPosition:\r
5758       ReloadPosition(1);\r
5759       break;\r
5760 \r
5761     case IDM_LoadPrevPosition:\r
5762       ReloadPosition(-1);\r
5763       break;\r
5764 \r
5765     case IDM_ReloadPosition:\r
5766       ReloadPosition(0);\r
5767       break;\r
5768 \r
5769     case IDM_SaveGame:\r
5770       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5771       f = OpenFileDialog(hwnd, "a", defName,\r
5772                          appData.oldSaveStyle ? "gam" : "pgn",\r
5773                          GAME_FILT,\r
5774                          "Save Game to File", NULL, fileTitle, NULL);\r
5775       if (f != NULL) {\r
5776         SaveGame(f, 0, "");\r
5777       }\r
5778       break;\r
5779 \r
5780     case IDM_SavePosition:\r
5781       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5782       f = OpenFileDialog(hwnd, "a", defName,\r
5783                          appData.oldSaveStyle ? "pos" : "fen",\r
5784                          POSITION_FILT,\r
5785                          "Save Position to File", NULL, fileTitle, NULL);\r
5786       if (f != NULL) {\r
5787         SavePosition(f, 0, "");\r
5788       }\r
5789       break;\r
5790 \r
5791     case IDM_SaveDiagram:\r
5792       defName = "diagram";\r
5793       f = OpenFileDialog(hwnd, "wb", defName,\r
5794                          "bmp",\r
5795                          DIAGRAM_FILT,\r
5796                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5797       if (f != NULL) {\r
5798         SaveDiagram(f);\r
5799       }\r
5800       break;\r
5801 \r
5802     case IDM_CopyGame:\r
5803       CopyGameToClipboard();\r
5804       break;\r
5805 \r
5806     case IDM_PasteGame:\r
5807       PasteGameFromClipboard();\r
5808       break;\r
5809 \r
5810     case IDM_CopyGameListToClipboard:\r
5811       CopyGameListToClipboard();\r
5812       break;\r
5813 \r
5814     /* [AS] Autodetect FEN or PGN data */\r
5815     case IDM_PasteAny:\r
5816       PasteGameOrFENFromClipboard();\r
5817       break;\r
5818 \r
5819     /* [AS] Move history */\r
5820     case IDM_ShowMoveHistory:\r
5821         if( MoveHistoryIsUp() ) {\r
5822             MoveHistoryPopDown();\r
5823         }\r
5824         else {\r
5825             MoveHistoryPopUp();\r
5826         }\r
5827         break;\r
5828 \r
5829     /* [AS] Eval graph */\r
5830     case IDM_ShowEvalGraph:\r
5831         if( EvalGraphIsUp() ) {\r
5832             EvalGraphPopDown();\r
5833         }\r
5834         else {\r
5835             EvalGraphPopUp();\r
5836             SetFocus(hwndMain);\r
5837         }\r
5838         break;\r
5839 \r
5840     /* [AS] Engine output */\r
5841     case IDM_ShowEngineOutput:\r
5842         if( EngineOutputIsUp() ) {\r
5843             EngineOutputPopDown();\r
5844         }\r
5845         else {\r
5846             EngineOutputPopUp();\r
5847         }\r
5848         break;\r
5849 \r
5850     /* [AS] User adjudication */\r
5851     case IDM_UserAdjudication_White:\r
5852         UserAdjudicationEvent( +1 );\r
5853         break;\r
5854 \r
5855     case IDM_UserAdjudication_Black:\r
5856         UserAdjudicationEvent( -1 );\r
5857         break;\r
5858 \r
5859     case IDM_UserAdjudication_Draw:\r
5860         UserAdjudicationEvent( 0 );\r
5861         break;\r
5862 \r
5863     /* [AS] Game list options dialog */\r
5864     case IDM_GameListOptions:\r
5865       GameListOptions();\r
5866       break;\r
5867 \r
5868     case IDM_CopyPosition:\r
5869       CopyFENToClipboard();\r
5870       break;\r
5871 \r
5872     case IDM_PastePosition:\r
5873       PasteFENFromClipboard();\r
5874       break;\r
5875 \r
5876     case IDM_MailMove:\r
5877       MailMoveEvent();\r
5878       break;\r
5879 \r
5880     case IDM_ReloadCMailMsg:\r
5881       Reset(TRUE, TRUE);\r
5882       ReloadCmailMsgEvent(FALSE);\r
5883       break;\r
5884 \r
5885     case IDM_Minimize:\r
5886       ShowWindow(hwnd, SW_MINIMIZE);\r
5887       break;\r
5888 \r
5889     case IDM_Exit:\r
5890       ExitEvent(0);\r
5891       break;\r
5892 \r
5893     case IDM_MachineWhite:\r
5894       MachineWhiteEvent();\r
5895       /*\r
5896        * refresh the tags dialog only if it's visible\r
5897        */\r
5898       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5899           char *tags;\r
5900           tags = PGNTags(&gameInfo);\r
5901           TagsPopUp(tags, CmailMsg());\r
5902           free(tags);\r
5903       }\r
5904       SAY("computer starts playing white");\r
5905       break;\r
5906 \r
5907     case IDM_MachineBlack:\r
5908       MachineBlackEvent();\r
5909       /*\r
5910        * refresh the tags dialog only if it's visible\r
5911        */\r
5912       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5913           char *tags;\r
5914           tags = PGNTags(&gameInfo);\r
5915           TagsPopUp(tags, CmailMsg());\r
5916           free(tags);\r
5917       }\r
5918       SAY("computer starts playing black");\r
5919       break;\r
5920 \r
5921     case IDM_TwoMachines:\r
5922       TwoMachinesEvent();\r
5923       /*\r
5924        * refresh the tags dialog only if it's visible\r
5925        */\r
5926       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5927           char *tags;\r
5928           tags = PGNTags(&gameInfo);\r
5929           TagsPopUp(tags, CmailMsg());\r
5930           free(tags);\r
5931       }\r
5932       SAY("programs start playing each other");\r
5933       break;\r
5934 \r
5935     case IDM_AnalysisMode:\r
5936       if (!first.analysisSupport) {\r
5937         sprintf(buf, "%s does not support analysis", first.tidy);\r
5938         DisplayError(buf, 0);\r
5939       } else {\r
5940         SAY("analyzing current position");\r
5941         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5942         if (appData.icsActive) {\r
5943                if (gameMode != IcsObserving) {\r
5944                        sprintf(buf, "You are not observing a game");\r
5945                        DisplayError(buf, 0);\r
5946                        /* secure check */\r
5947                        if (appData.icsEngineAnalyze) {\r
5948                                if (appData.debugMode) \r
5949                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5950                                ExitAnalyzeMode();\r
5951                                ModeHighlight();\r
5952                                break;\r
5953                        }\r
5954                        break;\r
5955                } else {\r
5956                        /* if enable, user want disable icsEngineAnalyze */\r
5957                        if (appData.icsEngineAnalyze) {\r
5958                                ExitAnalyzeMode();\r
5959                                ModeHighlight();\r
5960                                break;\r
5961                        }\r
5962                        appData.icsEngineAnalyze = TRUE;\r
5963                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5964                }\r
5965         } \r
5966         if (!appData.showThinking) ToggleShowThinking();\r
5967         AnalyzeModeEvent();\r
5968       }\r
5969       break;\r
5970 \r
5971     case IDM_AnalyzeFile:\r
5972       if (!first.analysisSupport) {\r
5973         char buf[MSG_SIZ];\r
5974         sprintf(buf, "%s does not support analysis", first.tidy);\r
5975         DisplayError(buf, 0);\r
5976       } else {\r
5977         if (!appData.showThinking) ToggleShowThinking();\r
5978         AnalyzeFileEvent();\r
5979         LoadGameDialog(hwnd, "Analyze Game from File");\r
5980         AnalysisPeriodicEvent(1);\r
5981       }\r
5982       break;\r
5983 \r
5984     case IDM_IcsClient:\r
5985       IcsClientEvent();\r
5986       break;\r
5987 \r
5988     case IDM_EditGame:\r
5989       EditGameEvent();\r
5990       SAY("edit game");\r
5991       break;\r
5992 \r
5993     case IDM_EditPosition:\r
5994       EditPositionEvent();\r
5995       SAY("to set up a position type a FEN");\r
5996       break;\r
5997 \r
5998     case IDM_Training:\r
5999       TrainingEvent();\r
6000       break;\r
6001 \r
6002     case IDM_ShowGameList:\r
6003       ShowGameListProc();\r
6004       break;\r
6005 \r
6006     case IDM_EditTags:\r
6007       EditTagsProc();\r
6008       break;\r
6009 \r
6010     case IDM_EditComment:\r
6011       if (commentDialogUp && editComment) {\r
6012         CommentPopDown();\r
6013       } else {\r
6014         EditCommentEvent();\r
6015       }\r
6016       break;\r
6017 \r
6018     case IDM_Pause:\r
6019       PauseEvent();\r
6020       break;\r
6021 \r
6022     case IDM_Accept:\r
6023       AcceptEvent();\r
6024       break;\r
6025 \r
6026     case IDM_Decline:\r
6027       DeclineEvent();\r
6028       break;\r
6029 \r
6030     case IDM_Rematch:\r
6031       RematchEvent();\r
6032       break;\r
6033 \r
6034     case IDM_CallFlag:\r
6035       CallFlagEvent();\r
6036       break;\r
6037 \r
6038     case IDM_Draw:\r
6039       DrawEvent();\r
6040       break;\r
6041 \r
6042     case IDM_Adjourn:\r
6043       AdjournEvent();\r
6044       break;\r
6045 \r
6046     case IDM_Abort:\r
6047       AbortEvent();\r
6048       break;\r
6049 \r
6050     case IDM_Resign:\r
6051       ResignEvent();\r
6052       break;\r
6053 \r
6054     case IDM_StopObserving:\r
6055       StopObservingEvent();\r
6056       break;\r
6057 \r
6058     case IDM_StopExamining:\r
6059       StopExaminingEvent();\r
6060       break;\r
6061 \r
6062     case IDM_TypeInMove:\r
6063       PopUpMoveDialog('\000');\r
6064       break;\r
6065 \r
6066     case IDM_TypeInName:\r
6067       PopUpNameDialog('\000');\r
6068       break;\r
6069 \r
6070     case IDM_Backward:\r
6071       BackwardEvent();\r
6072       SetFocus(hwndMain);\r
6073       break;\r
6074 \r
6075     JAWS_MENU_ITEMS\r
6076 \r
6077     case IDM_Forward:\r
6078       ForwardEvent();\r
6079       SetFocus(hwndMain);\r
6080       break;\r
6081 \r
6082     case IDM_ToStart:\r
6083       ToStartEvent();\r
6084       SetFocus(hwndMain);\r
6085       break;\r
6086 \r
6087     case IDM_ToEnd:\r
6088       ToEndEvent();\r
6089       SetFocus(hwndMain);\r
6090       break;\r
6091 \r
6092     case IDM_Revert:\r
6093       RevertEvent();\r
6094       break;\r
6095 \r
6096     case IDM_TruncateGame:\r
6097       TruncateGameEvent();\r
6098       break;\r
6099 \r
6100     case IDM_MoveNow:\r
6101       MoveNowEvent();\r
6102       break;\r
6103 \r
6104     case IDM_RetractMove:\r
6105       RetractMoveEvent();\r
6106       break;\r
6107 \r
6108     case IDM_FlipView:\r
6109       flipView = !flipView;\r
6110       DrawPosition(FALSE, NULL);\r
6111       break;\r
6112 \r
6113     case IDM_FlipClock:\r
6114       flipClock = !flipClock;\r
6115       DisplayBothClocks();\r
6116       DrawPosition(FALSE, NULL);\r
6117       break;\r
6118 \r
6119     case IDM_GeneralOptions:\r
6120       GeneralOptionsPopup(hwnd);\r
6121       DrawPosition(TRUE, NULL);\r
6122       break;\r
6123 \r
6124     case IDM_BoardOptions:\r
6125       BoardOptionsPopup(hwnd);\r
6126       break;\r
6127 \r
6128     case IDM_EnginePlayOptions:\r
6129       EnginePlayOptionsPopup(hwnd);\r
6130       break;\r
6131 \r
6132     case IDM_OptionsUCI:\r
6133       UciOptionsPopup(hwnd);\r
6134       break;\r
6135 \r
6136     case IDM_IcsOptions:\r
6137       IcsOptionsPopup(hwnd);\r
6138       break;\r
6139 \r
6140     case IDM_Fonts:\r
6141       FontsOptionsPopup(hwnd);\r
6142       break;\r
6143 \r
6144     case IDM_Sounds:\r
6145       SoundOptionsPopup(hwnd);\r
6146       break;\r
6147 \r
6148     case IDM_CommPort:\r
6149       CommPortOptionsPopup(hwnd);\r
6150       break;\r
6151 \r
6152     case IDM_LoadOptions:\r
6153       LoadOptionsPopup(hwnd);\r
6154       break;\r
6155 \r
6156     case IDM_SaveOptions:\r
6157       SaveOptionsPopup(hwnd);\r
6158       break;\r
6159 \r
6160     case IDM_TimeControl:\r
6161       TimeControlOptionsPopup(hwnd);\r
6162       break;\r
6163 \r
6164     case IDM_SaveSettings:\r
6165       SaveSettings(settingsFileName);\r
6166       break;\r
6167 \r
6168     case IDM_SaveSettingsOnExit:\r
6169       saveSettingsOnExit = !saveSettingsOnExit;\r
6170       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6171                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6172                                          MF_CHECKED : MF_UNCHECKED));\r
6173       break;\r
6174 \r
6175     case IDM_Hint:\r
6176       HintEvent();\r
6177       break;\r
6178 \r
6179     case IDM_Book:\r
6180       BookEvent();\r
6181       break;\r
6182 \r
6183     case IDM_AboutGame:\r
6184       AboutGameEvent();\r
6185       break;\r
6186 \r
6187     case IDM_Debug:\r
6188       appData.debugMode = !appData.debugMode;\r
6189       if (appData.debugMode) {\r
6190         char dir[MSG_SIZ];\r
6191         GetCurrentDirectory(MSG_SIZ, dir);\r
6192         SetCurrentDirectory(installDir);\r
6193         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6194         SetCurrentDirectory(dir);\r
6195         setbuf(debugFP, NULL);\r
6196       } else {\r
6197         fclose(debugFP);\r
6198         debugFP = NULL;\r
6199       }\r
6200       break;\r
6201 \r
6202     case IDM_HELPCONTENTS:\r
6203       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6204           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6205           MessageBox (GetFocus(),\r
6206                     "Unable to activate help",\r
6207                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6208       }\r
6209       break;\r
6210 \r
6211     case IDM_HELPSEARCH:\r
6212         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6213             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6214         MessageBox (GetFocus(),\r
6215                     "Unable to activate help",\r
6216                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6217       }\r
6218       break;\r
6219 \r
6220     case IDM_HELPHELP:\r
6221       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6222         MessageBox (GetFocus(),\r
6223                     "Unable to activate help",\r
6224                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6225       }\r
6226       break;\r
6227 \r
6228     case IDM_ABOUT:\r
6229       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6230       DialogBox(hInst, \r
6231         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6232         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6233       FreeProcInstance(lpProc);\r
6234       break;\r
6235 \r
6236     case IDM_DirectCommand1:\r
6237       AskQuestionEvent("Direct Command",\r
6238                        "Send to chess program:", "", "1");\r
6239       break;\r
6240     case IDM_DirectCommand2:\r
6241       AskQuestionEvent("Direct Command",\r
6242                        "Send to second chess program:", "", "2");\r
6243       break;\r
6244 \r
6245     case EP_WhitePawn:\r
6246       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6247       fromX = fromY = -1;\r
6248       break;\r
6249 \r
6250     case EP_WhiteKnight:\r
6251       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6252       fromX = fromY = -1;\r
6253       break;\r
6254 \r
6255     case EP_WhiteBishop:\r
6256       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6257       fromX = fromY = -1;\r
6258       break;\r
6259 \r
6260     case EP_WhiteRook:\r
6261       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6262       fromX = fromY = -1;\r
6263       break;\r
6264 \r
6265     case EP_WhiteQueen:\r
6266       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6267       fromX = fromY = -1;\r
6268       break;\r
6269 \r
6270     case EP_WhiteFerz:\r
6271       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6272       fromX = fromY = -1;\r
6273       break;\r
6274 \r
6275     case EP_WhiteWazir:\r
6276       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6277       fromX = fromY = -1;\r
6278       break;\r
6279 \r
6280     case EP_WhiteAlfil:\r
6281       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6282       fromX = fromY = -1;\r
6283       break;\r
6284 \r
6285     case EP_WhiteCannon:\r
6286       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6287       fromX = fromY = -1;\r
6288       break;\r
6289 \r
6290     case EP_WhiteCardinal:\r
6291       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6292       fromX = fromY = -1;\r
6293       break;\r
6294 \r
6295     case EP_WhiteMarshall:\r
6296       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6297       fromX = fromY = -1;\r
6298       break;\r
6299 \r
6300     case EP_WhiteKing:\r
6301       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6302       fromX = fromY = -1;\r
6303       break;\r
6304 \r
6305     case EP_BlackPawn:\r
6306       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6307       fromX = fromY = -1;\r
6308       break;\r
6309 \r
6310     case EP_BlackKnight:\r
6311       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6312       fromX = fromY = -1;\r
6313       break;\r
6314 \r
6315     case EP_BlackBishop:\r
6316       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6317       fromX = fromY = -1;\r
6318       break;\r
6319 \r
6320     case EP_BlackRook:\r
6321       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6322       fromX = fromY = -1;\r
6323       break;\r
6324 \r
6325     case EP_BlackQueen:\r
6326       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6327       fromX = fromY = -1;\r
6328       break;\r
6329 \r
6330     case EP_BlackFerz:\r
6331       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6332       fromX = fromY = -1;\r
6333       break;\r
6334 \r
6335     case EP_BlackWazir:\r
6336       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6337       fromX = fromY = -1;\r
6338       break;\r
6339 \r
6340     case EP_BlackAlfil:\r
6341       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6342       fromX = fromY = -1;\r
6343       break;\r
6344 \r
6345     case EP_BlackCannon:\r
6346       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6347       fromX = fromY = -1;\r
6348       break;\r
6349 \r
6350     case EP_BlackCardinal:\r
6351       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6352       fromX = fromY = -1;\r
6353       break;\r
6354 \r
6355     case EP_BlackMarshall:\r
6356       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6357       fromX = fromY = -1;\r
6358       break;\r
6359 \r
6360     case EP_BlackKing:\r
6361       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6362       fromX = fromY = -1;\r
6363       break;\r
6364 \r
6365     case EP_EmptySquare:\r
6366       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6367       fromX = fromY = -1;\r
6368       break;\r
6369 \r
6370     case EP_ClearBoard:\r
6371       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6372       fromX = fromY = -1;\r
6373       break;\r
6374 \r
6375     case EP_White:\r
6376       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6377       fromX = fromY = -1;\r
6378       break;\r
6379 \r
6380     case EP_Black:\r
6381       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6382       fromX = fromY = -1;\r
6383       break;\r
6384 \r
6385     case EP_Promote:\r
6386       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6387       fromX = fromY = -1;\r
6388       break;\r
6389 \r
6390     case EP_Demote:\r
6391       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6392       fromX = fromY = -1;\r
6393       break;\r
6394 \r
6395     case DP_Pawn:\r
6396       DropMenuEvent(WhitePawn, fromX, fromY);\r
6397       fromX = fromY = -1;\r
6398       break;\r
6399 \r
6400     case DP_Knight:\r
6401       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6402       fromX = fromY = -1;\r
6403       break;\r
6404 \r
6405     case DP_Bishop:\r
6406       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6407       fromX = fromY = -1;\r
6408       break;\r
6409 \r
6410     case DP_Rook:\r
6411       DropMenuEvent(WhiteRook, fromX, fromY);\r
6412       fromX = fromY = -1;\r
6413       break;\r
6414 \r
6415     case DP_Queen:\r
6416       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6417       fromX = fromY = -1;\r
6418       break;\r
6419 \r
6420     default:\r
6421       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6422     }\r
6423     break;\r
6424 \r
6425   case WM_TIMER:\r
6426     switch (wParam) {\r
6427     case CLOCK_TIMER_ID:\r
6428       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6429       clockTimerEvent = 0;\r
6430       DecrementClocks(); /* call into back end */\r
6431       break;\r
6432     case LOAD_GAME_TIMER_ID:\r
6433       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6434       loadGameTimerEvent = 0;\r
6435       AutoPlayGameLoop(); /* call into back end */\r
6436       break;\r
6437     case ANALYSIS_TIMER_ID:\r
6438       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6439                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6440         AnalysisPeriodicEvent(0);\r
6441       } else {\r
6442         KillTimer(hwnd, analysisTimerEvent);\r
6443         analysisTimerEvent = 0;\r
6444       }\r
6445       break;\r
6446     case DELAYED_TIMER_ID:\r
6447       KillTimer(hwnd, delayedTimerEvent);\r
6448       delayedTimerEvent = 0;\r
6449       delayedTimerCallback();\r
6450       break;\r
6451     }\r
6452     break;\r
6453 \r
6454   case WM_USER_Input:\r
6455     InputEvent(hwnd, message, wParam, lParam);\r
6456     break;\r
6457 \r
6458   /* [AS] Also move "attached" child windows */\r
6459   case WM_WINDOWPOSCHANGING:\r
6460 \r
6461     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6462         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6463 \r
6464         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6465             /* Window is moving */\r
6466             RECT rcMain;\r
6467 \r
6468 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6469             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6470             rcMain.right  = boardX + winWidth;\r
6471             rcMain.top    = boardY;\r
6472             rcMain.bottom = boardY + winHeight;\r
6473             \r
6474             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6475             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6476             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6477             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6478             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6479             boardX = lpwp->x;\r
6480             boardY = lpwp->y;\r
6481         }\r
6482     }\r
6483     break;\r
6484 \r
6485   /* [AS] Snapping */\r
6486   case WM_ENTERSIZEMOVE:\r
6487     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6488     if (hwnd == hwndMain) {\r
6489       doingSizing = TRUE;\r
6490       lastSizing = 0;\r
6491     }\r
6492     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6493     break;\r
6494 \r
6495   case WM_SIZING:\r
6496     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6497     if (hwnd == hwndMain) {\r
6498       lastSizing = wParam;\r
6499     }\r
6500     break;\r
6501 \r
6502   case WM_MOVING:\r
6503     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6504       return OnMoving( &sd, hwnd, wParam, lParam );\r
6505 \r
6506   case WM_EXITSIZEMOVE:\r
6507     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6508     if (hwnd == hwndMain) {\r
6509       RECT client;\r
6510       doingSizing = FALSE;\r
6511       InvalidateRect(hwnd, &boardRect, FALSE);\r
6512       GetClientRect(hwnd, &client);\r
6513       ResizeBoard(client.right, client.bottom, lastSizing);\r
6514       lastSizing = 0;\r
6515       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6516     }\r
6517     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6518     break;\r
6519 \r
6520   case WM_DESTROY: /* message: window being destroyed */\r
6521     PostQuitMessage(0);\r
6522     break;\r
6523 \r
6524   case WM_CLOSE:\r
6525     if (hwnd == hwndMain) {\r
6526       ExitEvent(0);\r
6527     }\r
6528     break;\r
6529 \r
6530   default:      /* Passes it on if unprocessed */\r
6531     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6532   }\r
6533   return 0;\r
6534 }\r
6535 \r
6536 /*---------------------------------------------------------------------------*\\r
6537  *\r
6538  * Misc utility routines\r
6539  *\r
6540 \*---------------------------------------------------------------------------*/\r
6541 \r
6542 /*\r
6543  * Decent random number generator, at least not as bad as Windows\r
6544  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6545  */\r
6546 unsigned int randstate;\r
6547 \r
6548 int\r
6549 myrandom(void)\r
6550 {\r
6551   randstate = randstate * 1664525 + 1013904223;\r
6552   return (int) randstate & 0x7fffffff;\r
6553 }\r
6554 \r
6555 void\r
6556 mysrandom(unsigned int seed)\r
6557 {\r
6558   randstate = seed;\r
6559 }\r
6560 \r
6561 \r
6562 /* \r
6563  * returns TRUE if user selects a different color, FALSE otherwise \r
6564  */\r
6565 \r
6566 BOOL\r
6567 ChangeColor(HWND hwnd, COLORREF *which)\r
6568 {\r
6569   static BOOL firstTime = TRUE;\r
6570   static DWORD customColors[16];\r
6571   CHOOSECOLOR cc;\r
6572   COLORREF newcolor;\r
6573   int i;\r
6574   ColorClass ccl;\r
6575 \r
6576   if (firstTime) {\r
6577     /* Make initial colors in use available as custom colors */\r
6578     /* Should we put the compiled-in defaults here instead? */\r
6579     i = 0;\r
6580     customColors[i++] = lightSquareColor & 0xffffff;\r
6581     customColors[i++] = darkSquareColor & 0xffffff;\r
6582     customColors[i++] = whitePieceColor & 0xffffff;\r
6583     customColors[i++] = blackPieceColor & 0xffffff;\r
6584     customColors[i++] = highlightSquareColor & 0xffffff;\r
6585     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6586 \r
6587     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6588       customColors[i++] = textAttribs[ccl].color;\r
6589     }\r
6590     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6591     firstTime = FALSE;\r
6592   }\r
6593 \r
6594   cc.lStructSize = sizeof(cc);\r
6595   cc.hwndOwner = hwnd;\r
6596   cc.hInstance = NULL;\r
6597   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6598   cc.lpCustColors = (LPDWORD) customColors;\r
6599   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6600 \r
6601   if (!ChooseColor(&cc)) return FALSE;\r
6602 \r
6603   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6604   if (newcolor == *which) return FALSE;\r
6605   *which = newcolor;\r
6606   return TRUE;\r
6607 \r
6608   /*\r
6609   InitDrawingColors();\r
6610   InvalidateRect(hwnd, &boardRect, FALSE);\r
6611   */\r
6612 }\r
6613 \r
6614 BOOLEAN\r
6615 MyLoadSound(MySound *ms)\r
6616 {\r
6617   BOOL ok = FALSE;\r
6618   struct stat st;\r
6619   FILE *f;\r
6620 \r
6621   if (ms->data) free(ms->data);\r
6622   ms->data = NULL;\r
6623 \r
6624   switch (ms->name[0]) {\r
6625   case NULLCHAR:\r
6626     /* Silence */\r
6627     ok = TRUE;\r
6628     break;\r
6629   case '$':\r
6630     /* System sound from Control Panel.  Don't preload here. */\r
6631     ok = TRUE;\r
6632     break;\r
6633   case '!':\r
6634     if (ms->name[1] == NULLCHAR) {\r
6635       /* "!" alone = silence */\r
6636       ok = TRUE;\r
6637     } else {\r
6638       /* Builtin wave resource.  Error if not found. */\r
6639       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6640       if (h == NULL) break;\r
6641       ms->data = (void *)LoadResource(hInst, h);\r
6642       if (h == NULL) break;\r
6643       ok = TRUE;\r
6644     }\r
6645     break;\r
6646   default:\r
6647     /* .wav file.  Error if not found. */\r
6648     f = fopen(ms->name, "rb");\r
6649     if (f == NULL) break;\r
6650     if (fstat(fileno(f), &st) < 0) break;\r
6651     ms->data = malloc(st.st_size);\r
6652     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6653     fclose(f);\r
6654     ok = TRUE;\r
6655     break;\r
6656   }\r
6657   if (!ok) {\r
6658     char buf[MSG_SIZ];\r
6659     sprintf(buf, "Error loading sound %s", ms->name);\r
6660     DisplayError(buf, GetLastError());\r
6661   }\r
6662   return ok;\r
6663 }\r
6664 \r
6665 BOOLEAN\r
6666 MyPlaySound(MySound *ms)\r
6667 {\r
6668   BOOLEAN ok = FALSE;\r
6669         if(appData.debugMode) fprintf(debugFP, "make sound %s %x %d\n", ms->name, ms, ms->name[0]);\r
6670   switch (ms->name[0]) {\r
6671   case NULLCHAR:\r
6672         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6673     /* Silence */\r
6674     ok = TRUE;\r
6675     break;\r
6676   case '$':\r
6677     /* System sound from Control Panel (deprecated feature).\r
6678        "$" alone or an unset sound name gets default beep (still in use). */\r
6679     if (ms->name[1]) {\r
6680       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6681     }\r
6682     if (!ok) ok = MessageBeep(MB_OK);\r
6683     break; \r
6684   case '!':\r
6685     /* Builtin wave resource, or "!" alone for silence */\r
6686     if (ms->name[1]) {\r
6687       if (ms->data == NULL) return FALSE;\r
6688       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6689     } else {\r
6690       ok = TRUE;\r
6691     }\r
6692     break;\r
6693   default:\r
6694     /* .wav file.  Error if not found. */\r
6695     if (ms->data == NULL) return FALSE;\r
6696     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6697     break;\r
6698   }\r
6699   /* Don't print an error: this can happen innocently if the sound driver\r
6700      is busy; for instance, if another instance of WinBoard is playing\r
6701      a sound at about the same time. */\r
6702 #if 0\r
6703   if (!ok) {\r
6704     char buf[MSG_SIZ];\r
6705     sprintf(buf, "Error playing sound %s", ms->name);\r
6706     DisplayError(buf, GetLastError());\r
6707   }\r
6708 #endif\r
6709   return ok;\r
6710 }\r
6711 \r
6712 \r
6713 LRESULT CALLBACK\r
6714 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6715 {\r
6716   BOOL ok;\r
6717   OPENFILENAME *ofn;\r
6718   static UINT *number; /* gross that this is static */\r
6719 \r
6720   switch (message) {\r
6721   case WM_INITDIALOG: /* message: initialize dialog box */\r
6722     /* Center the dialog over the application window */\r
6723     ofn = (OPENFILENAME *) lParam;\r
6724     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6725       number = (UINT *) ofn->lCustData;\r
6726       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6727     } else {\r
6728       number = NULL;\r
6729     }\r
6730     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6731     return FALSE;  /* Allow for further processing */\r
6732 \r
6733   case WM_COMMAND:\r
6734     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6735       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6736     }\r
6737     return FALSE;  /* Allow for further processing */\r
6738   }\r
6739   return FALSE;\r
6740 }\r
6741 \r
6742 UINT APIENTRY\r
6743 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6744 {\r
6745   static UINT *number;\r
6746   OPENFILENAME *ofname;\r
6747   OFNOTIFY *ofnot;\r
6748   switch (uiMsg) {\r
6749   case WM_INITDIALOG:\r
6750     ofname = (OPENFILENAME *)lParam;\r
6751     number = (UINT *)(ofname->lCustData);\r
6752     break;\r
6753   case WM_NOTIFY:\r
6754     ofnot = (OFNOTIFY *)lParam;\r
6755     if (ofnot->hdr.code == CDN_FILEOK) {\r
6756       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6757     }\r
6758     break;\r
6759   }\r
6760   return 0;\r
6761 }\r
6762 \r
6763 \r
6764 FILE *\r
6765 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6766                char *nameFilt, char *dlgTitle, UINT *number,\r
6767                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6768 {\r
6769   OPENFILENAME openFileName;\r
6770   char buf1[MSG_SIZ];\r
6771   FILE *f;\r
6772 \r
6773   if (fileName == NULL) fileName = buf1;\r
6774   if (defName == NULL) {\r
6775     strcpy(fileName, "*.");\r
6776     strcat(fileName, defExt);\r
6777   } else {\r
6778     strcpy(fileName, defName);\r
6779   }\r
6780   if (fileTitle) strcpy(fileTitle, "");\r
6781   if (number) *number = 0;\r
6782 \r
6783   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6784   openFileName.hwndOwner         = hwnd;\r
6785   openFileName.hInstance         = (HANDLE) hInst;\r
6786   openFileName.lpstrFilter       = nameFilt;\r
6787   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6788   openFileName.nMaxCustFilter    = 0L;\r
6789   openFileName.nFilterIndex      = 1L;\r
6790   openFileName.lpstrFile         = fileName;\r
6791   openFileName.nMaxFile          = MSG_SIZ;\r
6792   openFileName.lpstrFileTitle    = fileTitle;\r
6793   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6794   openFileName.lpstrInitialDir   = NULL;\r
6795   openFileName.lpstrTitle        = dlgTitle;\r
6796   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6797     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6798     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6799     | (oldDialog ? 0 : OFN_EXPLORER);\r
6800   openFileName.nFileOffset       = 0;\r
6801   openFileName.nFileExtension    = 0;\r
6802   openFileName.lpstrDefExt       = defExt;\r
6803   openFileName.lCustData         = (LONG) number;\r
6804   openFileName.lpfnHook          = oldDialog ?\r
6805     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6806   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6807 \r
6808   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6809                         GetOpenFileName(&openFileName)) {\r
6810     /* open the file */\r
6811     f = fopen(openFileName.lpstrFile, write);\r
6812     if (f == NULL) {\r
6813       MessageBox(hwnd, "File open failed", NULL,\r
6814                  MB_OK|MB_ICONEXCLAMATION);\r
6815       return NULL;\r
6816     }\r
6817   } else {\r
6818     int err = CommDlgExtendedError();\r
6819     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6820     return FALSE;\r
6821   }\r
6822   return f;\r
6823 }\r
6824 \r
6825 \r
6826 \r
6827 VOID APIENTRY\r
6828 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6829 {\r
6830   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6831 \r
6832   /*\r
6833    * Get the first pop-up menu in the menu template. This is the\r
6834    * menu that TrackPopupMenu displays.\r
6835    */\r
6836   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6837 \r
6838   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6839 \r
6840   /*\r
6841    * TrackPopup uses screen coordinates, so convert the\r
6842    * coordinates of the mouse click to screen coordinates.\r
6843    */\r
6844   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6845 \r
6846   /* Draw and track the floating pop-up menu. */\r
6847   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6848                  pt.x, pt.y, 0, hwnd, NULL);\r
6849 \r
6850   /* Destroy the menu.*/\r
6851   DestroyMenu(hmenu);\r
6852 }\r
6853    \r
6854 typedef struct {\r
6855   HWND hDlg, hText;\r
6856   int sizeX, sizeY, newSizeX, newSizeY;\r
6857   HDWP hdwp;\r
6858 } ResizeEditPlusButtonsClosure;\r
6859 \r
6860 BOOL CALLBACK\r
6861 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6862 {\r
6863   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6864   RECT rect;\r
6865   POINT pt;\r
6866 \r
6867   if (hChild == cl->hText) return TRUE;\r
6868   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6869   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6870   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6871   ScreenToClient(cl->hDlg, &pt);\r
6872   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6873     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6874   return TRUE;\r
6875 }\r
6876 \r
6877 /* Resize a dialog that has a (rich) edit field filling most of\r
6878    the top, with a row of buttons below */\r
6879 VOID\r
6880 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6881 {\r
6882   RECT rectText;\r
6883   int newTextHeight, newTextWidth;\r
6884   ResizeEditPlusButtonsClosure cl;\r
6885   \r
6886   /*if (IsIconic(hDlg)) return;*/\r
6887   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6888   \r
6889   cl.hdwp = BeginDeferWindowPos(8);\r
6890 \r
6891   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6892   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6893   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6894   if (newTextHeight < 0) {\r
6895     newSizeY += -newTextHeight;\r
6896     newTextHeight = 0;\r
6897   }\r
6898   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6899     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6900 \r
6901   cl.hDlg = hDlg;\r
6902   cl.hText = hText;\r
6903   cl.sizeX = sizeX;\r
6904   cl.sizeY = sizeY;\r
6905   cl.newSizeX = newSizeX;\r
6906   cl.newSizeY = newSizeY;\r
6907   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6908 \r
6909   EndDeferWindowPos(cl.hdwp);\r
6910 }\r
6911 \r
6912 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6913 {\r
6914     RECT    rChild, rParent;\r
6915     int     wChild, hChild, wParent, hParent;\r
6916     int     wScreen, hScreen, xNew, yNew;\r
6917     HDC     hdc;\r
6918 \r
6919     /* Get the Height and Width of the child window */\r
6920     GetWindowRect (hwndChild, &rChild);\r
6921     wChild = rChild.right - rChild.left;\r
6922     hChild = rChild.bottom - rChild.top;\r
6923 \r
6924     /* Get the Height and Width of the parent window */\r
6925     GetWindowRect (hwndParent, &rParent);\r
6926     wParent = rParent.right - rParent.left;\r
6927     hParent = rParent.bottom - rParent.top;\r
6928 \r
6929     /* Get the display limits */\r
6930     hdc = GetDC (hwndChild);\r
6931     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6932     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6933     ReleaseDC(hwndChild, hdc);\r
6934 \r
6935     /* Calculate new X position, then adjust for screen */\r
6936     xNew = rParent.left + ((wParent - wChild) /2);\r
6937     if (xNew < 0) {\r
6938         xNew = 0;\r
6939     } else if ((xNew+wChild) > wScreen) {\r
6940         xNew = wScreen - wChild;\r
6941     }\r
6942 \r
6943     /* Calculate new Y position, then adjust for screen */\r
6944     if( mode == 0 ) {\r
6945         yNew = rParent.top  + ((hParent - hChild) /2);\r
6946     }\r
6947     else {\r
6948         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6949     }\r
6950 \r
6951     if (yNew < 0) {\r
6952         yNew = 0;\r
6953     } else if ((yNew+hChild) > hScreen) {\r
6954         yNew = hScreen - hChild;\r
6955     }\r
6956 \r
6957     /* Set it, and return */\r
6958     return SetWindowPos (hwndChild, NULL,\r
6959                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6960 }\r
6961 \r
6962 /* Center one window over another */\r
6963 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6964 {\r
6965     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6966 }\r
6967 \r
6968 /*---------------------------------------------------------------------------*\\r
6969  *\r
6970  * Startup Dialog functions\r
6971  *\r
6972 \*---------------------------------------------------------------------------*/\r
6973 void\r
6974 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6975 {\r
6976   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6977 \r
6978   while (*cd != NULL) {\r
6979     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6980     cd++;\r
6981   }\r
6982 }\r
6983 \r
6984 void\r
6985 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6986 {\r
6987   char buf1[ARG_MAX];\r
6988   int len;\r
6989 \r
6990   if (str[0] == '@') {\r
6991     FILE* f = fopen(str + 1, "r");\r
6992     if (f == NULL) {\r
6993       DisplayFatalError(str + 1, errno, 2);\r
6994       return;\r
6995     }\r
6996     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6997     fclose(f);\r
6998     buf1[len] = NULLCHAR;\r
6999     str = buf1;\r
7000   }\r
7001 \r
7002   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7003 \r
7004   for (;;) {\r
7005     char buf[MSG_SIZ];\r
7006     char *end = strchr(str, '\n');\r
7007     if (end == NULL) return;\r
7008     memcpy(buf, str, end - str);\r
7009     buf[end - str] = NULLCHAR;\r
7010     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
7011     str = end + 1;\r
7012   }\r
7013 }\r
7014 \r
7015 void\r
7016 SetStartupDialogEnables(HWND hDlg)\r
7017 {\r
7018   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
7019     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7020     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
7021   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7022     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
7023   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
7024     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
7025   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
7026     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
7027   EnableWindow(GetDlgItem(hDlg, IDOK),\r
7028     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7029     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
7030     IsDlgButtonChecked(hDlg, OPT_View));\r
7031 }\r
7032 \r
7033 char *\r
7034 QuoteForFilename(char *filename)\r
7035 {\r
7036   int dquote, space;\r
7037   dquote = strchr(filename, '"') != NULL;\r
7038   space = strchr(filename, ' ') != NULL;\r
7039   if (dquote || space) {\r
7040     if (dquote) {\r
7041       return "'";\r
7042     } else {\r
7043       return "\"";\r
7044     }\r
7045   } else {\r
7046     return "";\r
7047   }\r
7048 }\r
7049 \r
7050 VOID\r
7051 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
7052 {\r
7053   char buf[MSG_SIZ];\r
7054   char *q;\r
7055 \r
7056   InitComboStringsFromOption(hwndCombo, nthnames);\r
7057   q = QuoteForFilename(nthcp);\r
7058   sprintf(buf, "%s%s%s", q, nthcp, q);\r
7059   if (*nthdir != NULLCHAR) {\r
7060     q = QuoteForFilename(nthdir);\r
7061     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
7062   }\r
7063   if (*nthcp == NULLCHAR) {\r
7064     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7065   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7066     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7067     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7068   }\r
7069 }\r
7070 \r
7071 LRESULT CALLBACK\r
7072 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7073 {\r
7074   char buf[MSG_SIZ];\r
7075   HANDLE hwndCombo;\r
7076   char *p;\r
7077 \r
7078   switch (message) {\r
7079   case WM_INITDIALOG:\r
7080     /* Center the dialog */\r
7081     CenterWindow (hDlg, GetDesktopWindow());\r
7082     /* Initialize the dialog items */\r
7083     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7084                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7085                   firstChessProgramNames);\r
7086     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7087                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7088                   secondChessProgramNames);\r
7089     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7090     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7091     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7092     if (*appData.icsHelper != NULLCHAR) {\r
7093       char *q = QuoteForFilename(appData.icsHelper);\r
7094       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7095     }\r
7096     if (*appData.icsHost == NULLCHAR) {\r
7097       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7098       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7099     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7100       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7101       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7102     }\r
7103 \r
7104     if (appData.icsActive) {\r
7105       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7106     }\r
7107     else if (appData.noChessProgram) {\r
7108       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7109     }\r
7110     else {\r
7111       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7112     }\r
7113 \r
7114     SetStartupDialogEnables(hDlg);\r
7115     return TRUE;\r
7116 \r
7117   case WM_COMMAND:\r
7118     switch (LOWORD(wParam)) {\r
7119     case IDOK:\r
7120       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7121         strcpy(buf, "/fcp=");\r
7122         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7123         p = buf;\r
7124         ParseArgs(StringGet, &p);\r
7125         strcpy(buf, "/scp=");\r
7126         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7127         p = buf;\r
7128         ParseArgs(StringGet, &p);\r
7129         appData.noChessProgram = FALSE;\r
7130         appData.icsActive = FALSE;\r
7131       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7132         strcpy(buf, "/ics /icshost=");\r
7133         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7134         p = buf;\r
7135         ParseArgs(StringGet, &p);\r
7136         if (appData.zippyPlay) {\r
7137           strcpy(buf, "/fcp=");\r
7138           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7139           p = buf;\r
7140           ParseArgs(StringGet, &p);\r
7141         }\r
7142       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7143         appData.noChessProgram = TRUE;\r
7144         appData.icsActive = FALSE;\r
7145       } else {\r
7146         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7147                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7148         return TRUE;\r
7149       }\r
7150       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7151         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7152         p = buf;\r
7153         ParseArgs(StringGet, &p);\r
7154       }\r
7155       EndDialog(hDlg, TRUE);\r
7156       return TRUE;\r
7157 \r
7158     case IDCANCEL:\r
7159       ExitEvent(0);\r
7160       return TRUE;\r
7161 \r
7162     case IDM_HELPCONTENTS:\r
7163       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7164         MessageBox (GetFocus(),\r
7165                     "Unable to activate help",\r
7166                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7167       }\r
7168       break;\r
7169 \r
7170     default:\r
7171       SetStartupDialogEnables(hDlg);\r
7172       break;\r
7173     }\r
7174     break;\r
7175   }\r
7176   return FALSE;\r
7177 }\r
7178 \r
7179 /*---------------------------------------------------------------------------*\\r
7180  *\r
7181  * About box dialog functions\r
7182  *\r
7183 \*---------------------------------------------------------------------------*/\r
7184 \r
7185 /* Process messages for "About" dialog box */\r
7186 LRESULT CALLBACK\r
7187 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7188 {\r
7189   switch (message) {\r
7190   case WM_INITDIALOG: /* message: initialize dialog box */\r
7191     /* Center the dialog over the application window */\r
7192     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7193     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7194     return (TRUE);\r
7195 \r
7196   case WM_COMMAND: /* message: received a command */\r
7197     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7198         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7199       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7200       return (TRUE);\r
7201     }\r
7202     break;\r
7203   }\r
7204   return (FALSE);\r
7205 }\r
7206 \r
7207 /*---------------------------------------------------------------------------*\\r
7208  *\r
7209  * Comment Dialog functions\r
7210  *\r
7211 \*---------------------------------------------------------------------------*/\r
7212 \r
7213 LRESULT CALLBACK\r
7214 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7215 {\r
7216   static HANDLE hwndText = NULL;\r
7217   int len, newSizeX, newSizeY, flags;\r
7218   static int sizeX, sizeY;\r
7219   char *str;\r
7220   RECT rect;\r
7221   MINMAXINFO *mmi;\r
7222 \r
7223   switch (message) {\r
7224   case WM_INITDIALOG: /* message: initialize dialog box */\r
7225     /* Initialize the dialog items */\r
7226     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7227     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7228     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7229     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7230     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7231     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7232     SetWindowText(hDlg, commentTitle);\r
7233     if (editComment) {\r
7234       SetFocus(hwndText);\r
7235     } else {\r
7236       SetFocus(GetDlgItem(hDlg, IDOK));\r
7237     }\r
7238     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7239                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7240                 MAKELPARAM(FALSE, 0));\r
7241     /* Size and position the dialog */\r
7242     if (!commentDialog) {\r
7243       commentDialog = hDlg;\r
7244       flags = SWP_NOZORDER;\r
7245       GetClientRect(hDlg, &rect);\r
7246       sizeX = rect.right;\r
7247       sizeY = rect.bottom;\r
7248       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7249           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7250         WINDOWPLACEMENT wp;\r
7251         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7252         wp.length = sizeof(WINDOWPLACEMENT);\r
7253         wp.flags = 0;\r
7254         wp.showCmd = SW_SHOW;\r
7255         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7256         wp.rcNormalPosition.left = commentX;\r
7257         wp.rcNormalPosition.right = commentX + commentW;\r
7258         wp.rcNormalPosition.top = commentY;\r
7259         wp.rcNormalPosition.bottom = commentY + commentH;\r
7260         SetWindowPlacement(hDlg, &wp);\r
7261 \r
7262         GetClientRect(hDlg, &rect);\r
7263         newSizeX = rect.right;\r
7264         newSizeY = rect.bottom;\r
7265         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7266                               newSizeX, newSizeY);\r
7267         sizeX = newSizeX;\r
7268         sizeY = newSizeY;\r
7269       }\r
7270     }\r
7271     return FALSE;\r
7272 \r
7273   case WM_COMMAND: /* message: received a command */\r
7274     switch (LOWORD(wParam)) {\r
7275     case IDOK:\r
7276       if (editComment) {\r
7277         char *p, *q;\r
7278         /* Read changed options from the dialog box */\r
7279         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7280         len = GetWindowTextLength(hwndText);\r
7281         str = (char *) malloc(len + 1);\r
7282         GetWindowText(hwndText, str, len + 1);\r
7283         p = q = str;\r
7284         while (*q) {\r
7285           if (*q == '\r')\r
7286             q++;\r
7287           else\r
7288             *p++ = *q++;\r
7289         }\r
7290         *p = NULLCHAR;\r
7291         ReplaceComment(commentIndex, str);\r
7292         free(str);\r
7293       }\r
7294       CommentPopDown();\r
7295       return TRUE;\r
7296 \r
7297     case IDCANCEL:\r
7298     case OPT_CancelComment:\r
7299       CommentPopDown();\r
7300       return TRUE;\r
7301 \r
7302     case OPT_ClearComment:\r
7303       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7304       break;\r
7305 \r
7306     case OPT_EditComment:\r
7307       EditCommentEvent();\r
7308       return TRUE;\r
7309 \r
7310     default:\r
7311       break;\r
7312     }\r
7313     break;\r
7314 \r
7315   case WM_SIZE:\r
7316     newSizeX = LOWORD(lParam);\r
7317     newSizeY = HIWORD(lParam);\r
7318     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7319     sizeX = newSizeX;\r
7320     sizeY = newSizeY;\r
7321     break;\r
7322 \r
7323   case WM_GETMINMAXINFO:\r
7324     /* Prevent resizing window too small */\r
7325     mmi = (MINMAXINFO *) lParam;\r
7326     mmi->ptMinTrackSize.x = 100;\r
7327     mmi->ptMinTrackSize.y = 100;\r
7328     break;\r
7329   }\r
7330   return FALSE;\r
7331 }\r
7332 \r
7333 VOID\r
7334 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7335 {\r
7336   FARPROC lpProc;\r
7337   char *p, *q;\r
7338 \r
7339   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7340 \r
7341   if (str == NULL) str = "";\r
7342   p = (char *) malloc(2 * strlen(str) + 2);\r
7343   q = p;\r
7344   while (*str) {\r
7345     if (*str == '\n') *q++ = '\r';\r
7346     *q++ = *str++;\r
7347   }\r
7348   *q = NULLCHAR;\r
7349   if (commentText != NULL) free(commentText);\r
7350 \r
7351   commentIndex = index;\r
7352   commentTitle = title;\r
7353   commentText = p;\r
7354   editComment = edit;\r
7355 \r
7356   if (commentDialog) {\r
7357     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7358     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7359   } else {\r
7360     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7361     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7362                  hwndMain, (DLGPROC)lpProc);\r
7363     FreeProcInstance(lpProc);\r
7364   }\r
7365   commentDialogUp = TRUE;\r
7366 }\r
7367 \r
7368 \r
7369 /*---------------------------------------------------------------------------*\\r
7370  *\r
7371  * Type-in move dialog functions\r
7372  * \r
7373 \*---------------------------------------------------------------------------*/\r
7374 \r
7375 LRESULT CALLBACK\r
7376 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7377 {\r
7378   char move[MSG_SIZ];\r
7379   HWND hInput;\r
7380   ChessMove moveType;\r
7381   int fromX, fromY, toX, toY;\r
7382   char promoChar;\r
7383 \r
7384   switch (message) {\r
7385   case WM_INITDIALOG:\r
7386     move[0] = (char) lParam;\r
7387     move[1] = NULLCHAR;\r
7388     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7389     hInput = GetDlgItem(hDlg, OPT_Move);\r
7390     SetWindowText(hInput, move);\r
7391     SetFocus(hInput);\r
7392     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7393     return FALSE;\r
7394 \r
7395   case WM_COMMAND:\r
7396     switch (LOWORD(wParam)) {\r
7397     case IDOK:\r
7398       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7399       { int n; Board board;\r
7400         // [HGM] FENedit\r
7401         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7402                 EditPositionPasteFEN(move);\r
7403                 EndDialog(hDlg, TRUE);\r
7404                 return TRUE;\r
7405         }\r
7406         // [HGM] movenum: allow move number to be typed in any mode\r
7407         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7408           currentMove = 2*n-1;\r
7409           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7410           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7411           EndDialog(hDlg, TRUE);\r
7412           DrawPosition(TRUE, boards[currentMove]);\r
7413           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7414           else DisplayMessage("", "");\r
7415           return TRUE;\r
7416         }\r
7417       }\r
7418       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7419         gameMode != Training) {\r
7420         DisplayMoveError("Displayed move is not current");\r
7421       } else {\r
7422 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7423         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7424           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7425         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7426         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7427           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7428           if (gameMode != Training)\r
7429               forwardMostMove = currentMove;\r
7430           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7431         } else {\r
7432           DisplayMoveError("Could not parse move");\r
7433         }\r
7434       }\r
7435       EndDialog(hDlg, TRUE);\r
7436       return TRUE;\r
7437     case IDCANCEL:\r
7438       EndDialog(hDlg, FALSE);\r
7439       return TRUE;\r
7440     default:\r
7441       break;\r
7442     }\r
7443     break;\r
7444   }\r
7445   return FALSE;\r
7446 }\r
7447 \r
7448 VOID\r
7449 PopUpMoveDialog(char firstchar)\r
7450 {\r
7451     FARPROC lpProc;\r
7452     \r
7453     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7454         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7455         gameMode == AnalyzeMode || gameMode == EditGame || \r
7456         gameMode == EditPosition || gameMode == IcsExamining ||\r
7457         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7458         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7459                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7460                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7461         gameMode == Training) {\r
7462       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7463       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7464         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7465       FreeProcInstance(lpProc);\r
7466     }\r
7467 }\r
7468 \r
7469 /*---------------------------------------------------------------------------*\\r
7470  *\r
7471  * Type-in name dialog functions\r
7472  * \r
7473 \*---------------------------------------------------------------------------*/\r
7474 \r
7475 LRESULT CALLBACK\r
7476 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7477 {\r
7478   char move[MSG_SIZ];\r
7479   HWND hInput;\r
7480 \r
7481   switch (message) {\r
7482   case WM_INITDIALOG:\r
7483     move[0] = (char) lParam;\r
7484     move[1] = NULLCHAR;\r
7485     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7486     hInput = GetDlgItem(hDlg, OPT_Name);\r
7487     SetWindowText(hInput, move);\r
7488     SetFocus(hInput);\r
7489     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7490     return FALSE;\r
7491 \r
7492   case WM_COMMAND:\r
7493     switch (LOWORD(wParam)) {\r
7494     case IDOK:\r
7495       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7496       appData.userName = strdup(move);\r
7497       SetUserLogo();\r
7498 \r
7499       EndDialog(hDlg, TRUE);\r
7500       return TRUE;\r
7501     case IDCANCEL:\r
7502       EndDialog(hDlg, FALSE);\r
7503       return TRUE;\r
7504     default:\r
7505       break;\r
7506     }\r
7507     break;\r
7508   }\r
7509   return FALSE;\r
7510 }\r
7511 \r
7512 VOID\r
7513 PopUpNameDialog(char firstchar)\r
7514 {\r
7515     FARPROC lpProc;\r
7516     \r
7517       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7518       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7519         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7520       FreeProcInstance(lpProc);\r
7521 }\r
7522 \r
7523 /*---------------------------------------------------------------------------*\\r
7524  *\r
7525  *  Error dialogs\r
7526  * \r
7527 \*---------------------------------------------------------------------------*/\r
7528 \r
7529 /* Nonmodal error box */\r
7530 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7531                              WPARAM wParam, LPARAM lParam);\r
7532 \r
7533 VOID\r
7534 ErrorPopUp(char *title, char *content)\r
7535 {\r
7536   FARPROC lpProc;\r
7537   char *p, *q;\r
7538   BOOLEAN modal = hwndMain == NULL;\r
7539 \r
7540   p = content;\r
7541   q = errorMessage;\r
7542   while (*p) {\r
7543     if (*p == '\n') {\r
7544       if (modal) {\r
7545         *q++ = ' ';\r
7546         p++;\r
7547       } else {\r
7548         *q++ = '\r';\r
7549         *q++ = *p++;\r
7550       }\r
7551     } else {\r
7552       *q++ = *p++;\r
7553     }\r
7554   }\r
7555   *q = NULLCHAR;\r
7556   strncpy(errorTitle, title, sizeof(errorTitle));\r
7557   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7558   \r
7559   if (modal) {\r
7560     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7561   } else {\r
7562     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7563     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7564                  hwndMain, (DLGPROC)lpProc);\r
7565     FreeProcInstance(lpProc);\r
7566   }\r
7567 }\r
7568 \r
7569 VOID\r
7570 ErrorPopDown()\r
7571 {\r
7572   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7573   if (errorDialog == NULL) return;\r
7574   DestroyWindow(errorDialog);\r
7575   errorDialog = NULL;\r
7576 }\r
7577 \r
7578 LRESULT CALLBACK\r
7579 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7580 {\r
7581   HANDLE hwndText;\r
7582   RECT rChild;\r
7583 \r
7584   switch (message) {\r
7585   case WM_INITDIALOG:\r
7586     GetWindowRect(hDlg, &rChild);\r
7587 \r
7588     /*\r
7589     SetWindowPos(hDlg, NULL, rChild.left,\r
7590       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7591       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7592     */\r
7593 \r
7594     /* \r
7595         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7596         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7597         and it doesn't work when you resize the dialog.\r
7598         For now, just give it a default position.\r
7599     */\r
7600     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7601 \r
7602     errorDialog = hDlg;\r
7603     SetWindowText(hDlg, errorTitle);\r
7604     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7605     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7606     return FALSE;\r
7607 \r
7608   case WM_COMMAND:\r
7609     switch (LOWORD(wParam)) {\r
7610     case IDOK:\r
7611     case IDCANCEL:\r
7612       if (errorDialog == hDlg) errorDialog = NULL;\r
7613       DestroyWindow(hDlg);\r
7614       return TRUE;\r
7615 \r
7616     default:\r
7617       break;\r
7618     }\r
7619     break;\r
7620   }\r
7621   return FALSE;\r
7622 }\r
7623 \r
7624 #ifdef GOTHIC\r
7625 HWND gothicDialog = NULL;\r
7626 \r
7627 LRESULT CALLBACK\r
7628 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7629 {\r
7630   HANDLE hwndText;\r
7631   RECT rChild;\r
7632   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7633 \r
7634   switch (message) {\r
7635   case WM_INITDIALOG:\r
7636     GetWindowRect(hDlg, &rChild);\r
7637 \r
7638     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7639                                                              SWP_NOZORDER);\r
7640 \r
7641     /* \r
7642         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7643         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7644         and it doesn't work when you resize the dialog.\r
7645         For now, just give it a default position.\r
7646     */\r
7647     gothicDialog = hDlg;\r
7648     SetWindowText(hDlg, errorTitle);\r
7649     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7650     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7651     return FALSE;\r
7652 \r
7653   case WM_COMMAND:\r
7654     switch (LOWORD(wParam)) {\r
7655     case IDOK:\r
7656     case IDCANCEL:\r
7657       if (errorDialog == hDlg) errorDialog = NULL;\r
7658       DestroyWindow(hDlg);\r
7659       return TRUE;\r
7660 \r
7661     default:\r
7662       break;\r
7663     }\r
7664     break;\r
7665   }\r
7666   return FALSE;\r
7667 }\r
7668 \r
7669 VOID\r
7670 GothicPopUp(char *title, VariantClass variant)\r
7671 {\r
7672   FARPROC lpProc;\r
7673   static char *lastTitle;\r
7674 \r
7675   strncpy(errorTitle, title, sizeof(errorTitle));\r
7676   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7677 \r
7678   if(lastTitle != title && gothicDialog != NULL) {\r
7679     DestroyWindow(gothicDialog);\r
7680     gothicDialog = NULL;\r
7681   }\r
7682   if(variant != VariantNormal && gothicDialog == NULL) {\r
7683     title = lastTitle;\r
7684     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7685     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7686                  hwndMain, (DLGPROC)lpProc);\r
7687     FreeProcInstance(lpProc);\r
7688   }\r
7689 }\r
7690 #endif\r
7691 \r
7692 /*---------------------------------------------------------------------------*\\r
7693  *\r
7694  *  Ics Interaction console functions\r
7695  *\r
7696 \*---------------------------------------------------------------------------*/\r
7697 \r
7698 #define HISTORY_SIZE 64\r
7699 static char *history[HISTORY_SIZE];\r
7700 int histIn = 0, histP = 0;\r
7701 \r
7702 VOID\r
7703 SaveInHistory(char *cmd)\r
7704 {\r
7705   if (history[histIn] != NULL) {\r
7706     free(history[histIn]);\r
7707     history[histIn] = NULL;\r
7708   }\r
7709   if (*cmd == NULLCHAR) return;\r
7710   history[histIn] = StrSave(cmd);\r
7711   histIn = (histIn + 1) % HISTORY_SIZE;\r
7712   if (history[histIn] != NULL) {\r
7713     free(history[histIn]);\r
7714     history[histIn] = NULL;\r
7715   }\r
7716   histP = histIn;\r
7717 }\r
7718 \r
7719 char *\r
7720 PrevInHistory(char *cmd)\r
7721 {\r
7722   int newhp;\r
7723   if (histP == histIn) {\r
7724     if (history[histIn] != NULL) free(history[histIn]);\r
7725     history[histIn] = StrSave(cmd);\r
7726   }\r
7727   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7728   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7729   histP = newhp;\r
7730   return history[histP];\r
7731 }\r
7732 \r
7733 char *\r
7734 NextInHistory()\r
7735 {\r
7736   if (histP == histIn) return NULL;\r
7737   histP = (histP + 1) % HISTORY_SIZE;\r
7738   return history[histP];\r
7739 }\r
7740 \r
7741 typedef struct {\r
7742   char *item;\r
7743   char *command;\r
7744   BOOLEAN getname;\r
7745   BOOLEAN immediate;\r
7746 } IcsTextMenuEntry;\r
7747 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7748 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7749 \r
7750 void\r
7751 ParseIcsTextMenu(char *icsTextMenuString)\r
7752 {\r
7753 //  int flags = 0;\r
7754   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7755   char *p = icsTextMenuString;\r
7756   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7757     free(e->item);\r
7758     e->item = NULL;\r
7759     if (e->command != NULL) {\r
7760       free(e->command);\r
7761       e->command = NULL;\r
7762     }\r
7763     e++;\r
7764   }\r
7765   e = icsTextMenuEntry;\r
7766   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7767     if (*p == ';' || *p == '\n') {\r
7768       e->item = strdup("-");\r
7769       e->command = NULL;\r
7770       p++;\r
7771     } else if (*p == '-') {\r
7772       e->item = strdup("-");\r
7773       e->command = NULL;\r
7774       p++;\r
7775       if (*p) p++;\r
7776     } else {\r
7777       char *q, *r, *s, *t;\r
7778       char c;\r
7779       q = strchr(p, ',');\r
7780       if (q == NULL) break;\r
7781       *q = NULLCHAR;\r
7782       r = strchr(q + 1, ',');\r
7783       if (r == NULL) break;\r
7784       *r = NULLCHAR;\r
7785       s = strchr(r + 1, ',');\r
7786       if (s == NULL) break;\r
7787       *s = NULLCHAR;\r
7788       c = ';';\r
7789       t = strchr(s + 1, c);\r
7790       if (t == NULL) {\r
7791         c = '\n';\r
7792         t = strchr(s + 1, c);\r
7793       }\r
7794       if (t != NULL) *t = NULLCHAR;\r
7795       e->item = strdup(p);\r
7796       e->command = strdup(q + 1);\r
7797       e->getname = *(r + 1) != '0';\r
7798       e->immediate = *(s + 1) != '0';\r
7799       *q = ',';\r
7800       *r = ',';\r
7801       *s = ',';\r
7802       if (t == NULL) break;\r
7803       *t = c;\r
7804       p = t + 1;\r
7805     }\r
7806     e++;\r
7807   } \r
7808 }\r
7809 \r
7810 HMENU\r
7811 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7812 {\r
7813   HMENU hmenu, h;\r
7814   int i = 0;\r
7815   hmenu = LoadMenu(hInst, "TextMenu");\r
7816   h = GetSubMenu(hmenu, 0);\r
7817   while (e->item) {\r
7818     if (strcmp(e->item, "-") == 0) {\r
7819       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7820     } else {\r
7821       if (e->item[0] == '|') {\r
7822         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7823                    IDM_CommandX + i, &e->item[1]);\r
7824       } else {\r
7825         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7826       }\r
7827     }\r
7828     e++;\r
7829     i++;\r
7830   } \r
7831   return hmenu;\r
7832 }\r
7833 \r
7834 WNDPROC consoleTextWindowProc;\r
7835 \r
7836 void\r
7837 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7838 {\r
7839   char buf[MSG_SIZ], name[MSG_SIZ];\r
7840   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7841   CHARRANGE sel;\r
7842 \r
7843   if (!getname) {\r
7844     SetWindowText(hInput, command);\r
7845     if (immediate) {\r
7846       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7847     } else {\r
7848       sel.cpMin = 999999;\r
7849       sel.cpMax = 999999;\r
7850       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7851       SetFocus(hInput);\r
7852     }\r
7853     return;\r
7854   }    \r
7855   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7856   if (sel.cpMin == sel.cpMax) {\r
7857     /* Expand to surrounding word */\r
7858     TEXTRANGE tr;\r
7859     do {\r
7860       tr.chrg.cpMax = sel.cpMin;\r
7861       tr.chrg.cpMin = --sel.cpMin;\r
7862       if (sel.cpMin < 0) break;\r
7863       tr.lpstrText = name;\r
7864       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7865     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7866     sel.cpMin++;\r
7867 \r
7868     do {\r
7869       tr.chrg.cpMin = sel.cpMax;\r
7870       tr.chrg.cpMax = ++sel.cpMax;\r
7871       tr.lpstrText = name;\r
7872       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7873     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7874     sel.cpMax--;\r
7875 \r
7876     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7877       MessageBeep(MB_ICONEXCLAMATION);\r
7878       return;\r
7879     }\r
7880     tr.chrg = sel;\r
7881     tr.lpstrText = name;\r
7882     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7883   } else {\r
7884     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7885       MessageBeep(MB_ICONEXCLAMATION);\r
7886       return;\r
7887     }\r
7888     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7889   }\r
7890   if (immediate) {\r
7891     sprintf(buf, "%s %s", command, name);\r
7892     SetWindowText(hInput, buf);\r
7893     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7894   } else {\r
7895     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7896     SetWindowText(hInput, buf);\r
7897     sel.cpMin = 999999;\r
7898     sel.cpMax = 999999;\r
7899     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7900     SetFocus(hInput);\r
7901   }\r
7902 }\r
7903 \r
7904 LRESULT CALLBACK \r
7905 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7906 {\r
7907   HWND hInput;\r
7908   CHARRANGE sel;\r
7909 \r
7910   switch (message) {\r
7911   case WM_KEYDOWN:\r
7912     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7913     switch (wParam) {\r
7914     case VK_PRIOR:\r
7915       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7916       return 0;\r
7917     case VK_NEXT:\r
7918       sel.cpMin = 999999;\r
7919       sel.cpMax = 999999;\r
7920       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7921       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7922       return 0;\r
7923     }\r
7924     break;\r
7925   case WM_CHAR:\r
7926    if(wParam != '\022') {\r
7927     if (wParam == '\t') {\r
7928       if (GetKeyState(VK_SHIFT) < 0) {\r
7929         /* shifted */\r
7930         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7931         if (buttonDesc[0].hwnd) {\r
7932           SetFocus(buttonDesc[0].hwnd);\r
7933         } else {\r
7934           SetFocus(hwndMain);\r
7935         }\r
7936       } else {\r
7937         /* unshifted */\r
7938         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7939       }\r
7940     } else {\r
7941       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7942       JAWS_DELETE( SetFocus(hInput); )\r
7943       SendMessage(hInput, message, wParam, lParam);\r
7944     }\r
7945     return 0;\r
7946    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7947   case WM_RBUTTONUP:\r
7948     if (GetKeyState(VK_SHIFT) & ~1) {\r
7949       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7950         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7951     } else {\r
7952       POINT pt;\r
7953       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7954       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7955       if (sel.cpMin == sel.cpMax) {\r
7956         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7957         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7958       }\r
7959       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7960         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7961       }\r
7962       pt.x = LOWORD(lParam);\r
7963       pt.y = HIWORD(lParam);\r
7964       MenuPopup(hwnd, pt, hmenu, -1);\r
7965     }\r
7966     return 0;\r
7967   case WM_PASTE:\r
7968     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7969     SetFocus(hInput);\r
7970     return SendMessage(hInput, message, wParam, lParam);\r
7971   case WM_MBUTTONDOWN:\r
7972     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7973   case WM_RBUTTONDOWN:\r
7974     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7975       /* Move selection here if it was empty */\r
7976       POINT pt;\r
7977       pt.x = LOWORD(lParam);\r
7978       pt.y = HIWORD(lParam);\r
7979       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7980       if (sel.cpMin == sel.cpMax) {\r
7981         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7982         sel.cpMax = sel.cpMin;\r
7983         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7984       }\r
7985       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7986     }\r
7987     return 0;\r
7988   case WM_COMMAND:\r
7989     switch (LOWORD(wParam)) {\r
7990     case IDM_QuickPaste:\r
7991       {\r
7992         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7993         if (sel.cpMin == sel.cpMax) {\r
7994           MessageBeep(MB_ICONEXCLAMATION);\r
7995           return 0;\r
7996         }\r
7997         SendMessage(hwnd, WM_COPY, 0, 0);\r
7998         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7999         SendMessage(hInput, WM_PASTE, 0, 0);\r
8000         SetFocus(hInput);\r
8001         return 0;\r
8002       }\r
8003     case IDM_Cut:\r
8004       SendMessage(hwnd, WM_CUT, 0, 0);\r
8005       return 0;\r
8006     case IDM_Paste:\r
8007       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8008       return 0;\r
8009     case IDM_Copy:\r
8010       SendMessage(hwnd, WM_COPY, 0, 0);\r
8011       return 0;\r
8012     default:\r
8013       {\r
8014         int i = LOWORD(wParam) - IDM_CommandX;\r
8015         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
8016             icsTextMenuEntry[i].command != NULL) {\r
8017           CommandX(hwnd, icsTextMenuEntry[i].command,\r
8018                    icsTextMenuEntry[i].getname,\r
8019                    icsTextMenuEntry[i].immediate);\r
8020           return 0;\r
8021         }\r
8022       }\r
8023       break;\r
8024     }\r
8025     break;\r
8026   }\r
8027   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
8028 }\r
8029 \r
8030 WNDPROC consoleInputWindowProc;\r
8031 \r
8032 LRESULT CALLBACK\r
8033 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8034 {\r
8035   char buf[MSG_SIZ];\r
8036   char *p;\r
8037   static BOOL sendNextChar = FALSE;\r
8038   static BOOL quoteNextChar = FALSE;\r
8039   InputSource *is = consoleInputSource;\r
8040   CHARFORMAT cf;\r
8041   CHARRANGE sel;\r
8042 \r
8043   switch (message) {\r
8044   case WM_CHAR:\r
8045     if (!appData.localLineEditing || sendNextChar) {\r
8046       is->buf[0] = (CHAR) wParam;\r
8047       is->count = 1;\r
8048       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8049       sendNextChar = FALSE;\r
8050       return 0;\r
8051     }\r
8052     if (quoteNextChar) {\r
8053       buf[0] = (char) wParam;\r
8054       buf[1] = NULLCHAR;\r
8055       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
8056       quoteNextChar = FALSE;\r
8057       return 0;\r
8058     }\r
8059     switch (wParam) {\r
8060     case '\r':   /* Enter key */\r
8061       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
8062       if (consoleEcho) SaveInHistory(is->buf);\r
8063       is->buf[is->count++] = '\n';\r
8064       is->buf[is->count] = NULLCHAR;\r
8065       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8066       if (consoleEcho) {\r
8067         ConsoleOutput(is->buf, is->count, TRUE);\r
8068       } else if (appData.localLineEditing) {\r
8069         ConsoleOutput("\n", 1, TRUE);\r
8070       }\r
8071       /* fall thru */\r
8072     case '\033': /* Escape key */\r
8073       SetWindowText(hwnd, "");\r
8074       cf.cbSize = sizeof(CHARFORMAT);\r
8075       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8076       if (consoleEcho) {\r
8077         cf.crTextColor = textAttribs[ColorNormal].color;\r
8078       } else {\r
8079         cf.crTextColor = COLOR_ECHOOFF;\r
8080       }\r
8081       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8082       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8083       return 0;\r
8084     case '\t':   /* Tab key */\r
8085       if (GetKeyState(VK_SHIFT) < 0) {\r
8086         /* shifted */\r
8087         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8088       } else {\r
8089         /* unshifted */\r
8090         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8091         if (buttonDesc[0].hwnd) {\r
8092           SetFocus(buttonDesc[0].hwnd);\r
8093         } else {\r
8094           SetFocus(hwndMain);\r
8095         }\r
8096       }\r
8097       return 0;\r
8098     case '\023': /* Ctrl+S */\r
8099       sendNextChar = TRUE;\r
8100       return 0;\r
8101     case '\021': /* Ctrl+Q */\r
8102       quoteNextChar = TRUE;\r
8103       return 0;\r
8104     JAWS_REPLAY\r
8105     default:\r
8106       break;\r
8107     }\r
8108     break;\r
8109   case WM_KEYDOWN:\r
8110     switch (wParam) {\r
8111     case VK_UP:\r
8112       GetWindowText(hwnd, buf, MSG_SIZ);\r
8113       p = PrevInHistory(buf);\r
8114       if (p != NULL) {\r
8115         SetWindowText(hwnd, p);\r
8116         sel.cpMin = 999999;\r
8117         sel.cpMax = 999999;\r
8118         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8119         return 0;\r
8120       }\r
8121       break;\r
8122     case VK_DOWN:\r
8123       p = NextInHistory();\r
8124       if (p != NULL) {\r
8125         SetWindowText(hwnd, p);\r
8126         sel.cpMin = 999999;\r
8127         sel.cpMax = 999999;\r
8128         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8129         return 0;\r
8130       }\r
8131       break;\r
8132     case VK_HOME:\r
8133     case VK_END:\r
8134       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8135       /* fall thru */\r
8136     case VK_PRIOR:\r
8137     case VK_NEXT:\r
8138       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8139       return 0;\r
8140     }\r
8141     break;\r
8142   case WM_MBUTTONDOWN:\r
8143     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8144       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8145     break;\r
8146   case WM_RBUTTONUP:\r
8147     if (GetKeyState(VK_SHIFT) & ~1) {\r
8148       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8149         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8150     } else {\r
8151       POINT pt;\r
8152       HMENU hmenu;\r
8153       hmenu = LoadMenu(hInst, "InputMenu");\r
8154       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8155       if (sel.cpMin == sel.cpMax) {\r
8156         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8157         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8158       }\r
8159       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8160         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8161       }\r
8162       pt.x = LOWORD(lParam);\r
8163       pt.y = HIWORD(lParam);\r
8164       MenuPopup(hwnd, pt, hmenu, -1);\r
8165     }\r
8166     return 0;\r
8167   case WM_COMMAND:\r
8168     switch (LOWORD(wParam)) { \r
8169     case IDM_Undo:\r
8170       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8171       return 0;\r
8172     case IDM_SelectAll:\r
8173       sel.cpMin = 0;\r
8174       sel.cpMax = -1; /*999999?*/\r
8175       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8176       return 0;\r
8177     case IDM_Cut:\r
8178       SendMessage(hwnd, WM_CUT, 0, 0);\r
8179       return 0;\r
8180     case IDM_Paste:\r
8181       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8182       return 0;\r
8183     case IDM_Copy:\r
8184       SendMessage(hwnd, WM_COPY, 0, 0);\r
8185       return 0;\r
8186     }\r
8187     break;\r
8188   }\r
8189   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8190 }\r
8191 \r
8192 #define CO_MAX  100000\r
8193 #define CO_TRIM   1000\r
8194 \r
8195 LRESULT CALLBACK\r
8196 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8197 {\r
8198   static SnapData sd;\r
8199   static HWND hText, hInput /*, hFocus*/;\r
8200 //  InputSource *is = consoleInputSource;\r
8201   RECT rect;\r
8202   static int sizeX, sizeY;\r
8203   int newSizeX, newSizeY;\r
8204   MINMAXINFO *mmi;\r
8205 \r
8206   switch (message) {\r
8207   case WM_INITDIALOG: /* message: initialize dialog box */\r
8208     hwndConsole = hDlg;\r
8209     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8210     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8211     SetFocus(hInput);\r
8212     consoleTextWindowProc = (WNDPROC)\r
8213       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8214     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8215     consoleInputWindowProc = (WNDPROC)\r
8216       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8217     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8218     Colorize(ColorNormal, TRUE);\r
8219     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8220     ChangedConsoleFont();\r
8221     GetClientRect(hDlg, &rect);\r
8222     sizeX = rect.right;\r
8223     sizeY = rect.bottom;\r
8224     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8225         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8226       WINDOWPLACEMENT wp;\r
8227       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8228       wp.length = sizeof(WINDOWPLACEMENT);\r
8229       wp.flags = 0;\r
8230       wp.showCmd = SW_SHOW;\r
8231       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8232       wp.rcNormalPosition.left = wpConsole.x;\r
8233       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8234       wp.rcNormalPosition.top = wpConsole.y;\r
8235       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8236       SetWindowPlacement(hDlg, &wp);\r
8237     }\r
8238 #if 1\r
8239    // [HGM] Chessknight's change 2004-07-13\r
8240    else { /* Determine Defaults */\r
8241        WINDOWPLACEMENT wp;\r
8242        wpConsole.x = winWidth + 1;\r
8243        wpConsole.y = boardY;\r
8244        wpConsole.width = screenWidth -  winWidth;\r
8245        wpConsole.height = winHeight;\r
8246        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8247        wp.length = sizeof(WINDOWPLACEMENT);\r
8248        wp.flags = 0;\r
8249        wp.showCmd = SW_SHOW;\r
8250        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8251        wp.rcNormalPosition.left = wpConsole.x;\r
8252        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8253        wp.rcNormalPosition.top = wpConsole.y;\r
8254        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8255        SetWindowPlacement(hDlg, &wp);\r
8256     }\r
8257 #endif\r
8258     return FALSE;\r
8259 \r
8260   case WM_SETFOCUS:\r
8261     SetFocus(hInput);\r
8262     return 0;\r
8263 \r
8264   case WM_CLOSE:\r
8265     ExitEvent(0);\r
8266     /* not reached */\r
8267     break;\r
8268 \r
8269   case WM_SIZE:\r
8270     if (IsIconic(hDlg)) break;\r
8271     newSizeX = LOWORD(lParam);\r
8272     newSizeY = HIWORD(lParam);\r
8273     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8274       RECT rectText, rectInput;\r
8275       POINT pt;\r
8276       int newTextHeight, newTextWidth;\r
8277       GetWindowRect(hText, &rectText);\r
8278       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8279       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8280       if (newTextHeight < 0) {\r
8281         newSizeY += -newTextHeight;\r
8282         newTextHeight = 0;\r
8283       }\r
8284       SetWindowPos(hText, NULL, 0, 0,\r
8285         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8286       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8287       pt.x = rectInput.left;\r
8288       pt.y = rectInput.top + newSizeY - sizeY;\r
8289       ScreenToClient(hDlg, &pt);\r
8290       SetWindowPos(hInput, NULL, \r
8291         pt.x, pt.y, /* needs client coords */   \r
8292         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8293         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8294     }\r
8295     sizeX = newSizeX;\r
8296     sizeY = newSizeY;\r
8297     break;\r
8298 \r
8299   case WM_GETMINMAXINFO:\r
8300     /* Prevent resizing window too small */\r
8301     mmi = (MINMAXINFO *) lParam;\r
8302     mmi->ptMinTrackSize.x = 100;\r
8303     mmi->ptMinTrackSize.y = 100;\r
8304     break;\r
8305 \r
8306   /* [AS] Snapping */\r
8307   case WM_ENTERSIZEMOVE:\r
8308     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8309 \r
8310   case WM_SIZING:\r
8311     return OnSizing( &sd, hDlg, wParam, lParam );\r
8312 \r
8313   case WM_MOVING:\r
8314     return OnMoving( &sd, hDlg, wParam, lParam );\r
8315 \r
8316   case WM_EXITSIZEMOVE:\r
8317     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8318   }\r
8319 \r
8320   return DefWindowProc(hDlg, message, wParam, lParam);\r
8321 }\r
8322 \r
8323 \r
8324 VOID\r
8325 ConsoleCreate()\r
8326 {\r
8327   HWND hCons;\r
8328   if (hwndConsole) return;\r
8329   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8330   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8331 }\r
8332 \r
8333 \r
8334 VOID\r
8335 ConsoleOutput(char* data, int length, int forceVisible)\r
8336 {\r
8337   HWND hText;\r
8338   int trim, exlen;\r
8339   char *p, *q;\r
8340   char buf[CO_MAX+1];\r
8341   POINT pEnd;\r
8342   RECT rect;\r
8343   static int delayLF = 0;\r
8344   CHARRANGE savesel, sel;\r
8345 \r
8346   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8347   p = data;\r
8348   q = buf;\r
8349   if (delayLF) {\r
8350     *q++ = '\r';\r
8351     *q++ = '\n';\r
8352     delayLF = 0;\r
8353   }\r
8354   while (length--) {\r
8355     if (*p == '\n') {\r
8356       if (*++p) {\r
8357         *q++ = '\r';\r
8358         *q++ = '\n';\r
8359       } else {\r
8360         delayLF = 1;\r
8361       }\r
8362     } else if (*p == '\007') {\r
8363        MyPlaySound(&sounds[(int)SoundBell]);\r
8364        p++;\r
8365     } else {\r
8366       *q++ = *p++;\r
8367     }\r
8368   }\r
8369   *q = NULLCHAR;\r
8370   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8371   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8372   /* Save current selection */\r
8373   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8374   exlen = GetWindowTextLength(hText);\r
8375   /* Find out whether current end of text is visible */\r
8376   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8377   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8378   /* Trim existing text if it's too long */\r
8379   if (exlen + (q - buf) > CO_MAX) {\r
8380     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8381     sel.cpMin = 0;\r
8382     sel.cpMax = trim;\r
8383     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8384     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8385     exlen -= trim;\r
8386     savesel.cpMin -= trim;\r
8387     savesel.cpMax -= trim;\r
8388     if (exlen < 0) exlen = 0;\r
8389     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8390     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8391   }\r
8392   /* Append the new text */\r
8393   sel.cpMin = exlen;\r
8394   sel.cpMax = exlen;\r
8395   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8396   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8397   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8398   if (forceVisible || exlen == 0 ||\r
8399       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8400        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8401     /* Scroll to make new end of text visible if old end of text\r
8402        was visible or new text is an echo of user typein */\r
8403     sel.cpMin = 9999999;\r
8404     sel.cpMax = 9999999;\r
8405     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8406     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8407     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8408     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8409   }\r
8410   if (savesel.cpMax == exlen || forceVisible) {\r
8411     /* Move insert point to new end of text if it was at the old\r
8412        end of text or if the new text is an echo of user typein */\r
8413     sel.cpMin = 9999999;\r
8414     sel.cpMax = 9999999;\r
8415     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8416   } else {\r
8417     /* Restore previous selection */\r
8418     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8419   }\r
8420   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8421 }\r
8422 \r
8423 /*---------*/\r
8424 \r
8425 \r
8426 void\r
8427 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8428 {\r
8429   char buf[100];\r
8430   char *str;\r
8431   COLORREF oldFg, oldBg;\r
8432   HFONT oldFont;\r
8433   RECT rect;\r
8434 \r
8435   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8436 \r
8437   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8438   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8439   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8440 \r
8441   rect.left = x;\r
8442   rect.right = x + squareSize;\r
8443   rect.top  = y;\r
8444   rect.bottom = y + squareSize;\r
8445   str = buf;\r
8446 \r
8447   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8448                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8449              y, ETO_CLIPPED|ETO_OPAQUE,\r
8450              &rect, str, strlen(str), NULL);\r
8451 \r
8452   (void) SetTextColor(hdc, oldFg);\r
8453   (void) SetBkColor(hdc, oldBg);\r
8454   (void) SelectObject(hdc, oldFont);\r
8455 }\r
8456 \r
8457 void\r
8458 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8459               RECT *rect, char *color, char *flagFell)\r
8460 {\r
8461   char buf[100];\r
8462   char *str;\r
8463   COLORREF oldFg, oldBg;\r
8464   HFONT oldFont;\r
8465 \r
8466   if (appData.clockMode) {\r
8467     if (tinyLayout)\r
8468       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8469     else\r
8470       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8471     str = buf;\r
8472   } else {\r
8473     str = color;\r
8474   }\r
8475 \r
8476   if (highlight) {\r
8477     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8478     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8479   } else {\r
8480     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8481     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8482   }\r
8483   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8484 \r
8485   JAWS_SILENCE\r
8486 \r
8487   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8488              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8489              rect, str, strlen(str), NULL);\r
8490   if(logoHeight > 0 && appData.clockMode) {\r
8491       RECT r;\r
8492       sprintf(buf, "%s %s", buf+7, flagFell);\r
8493       r.top = rect->top + logoHeight/2;\r
8494       r.left = rect->left;\r
8495       r.right = rect->right;\r
8496       r.bottom = rect->bottom;\r
8497       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8498                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8499                  &r, str, strlen(str), NULL);\r
8500   }\r
8501   (void) SetTextColor(hdc, oldFg);\r
8502   (void) SetBkColor(hdc, oldBg);\r
8503   (void) SelectObject(hdc, oldFont);\r
8504 }\r
8505 \r
8506 \r
8507 int\r
8508 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8509            OVERLAPPED *ovl)\r
8510 {\r
8511   int ok, err;\r
8512 \r
8513   /* [AS]  */\r
8514   if( count <= 0 ) {\r
8515     if (appData.debugMode) {\r
8516       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8517     }\r
8518 \r
8519     return ERROR_INVALID_USER_BUFFER;\r
8520   }\r
8521 \r
8522   ResetEvent(ovl->hEvent);\r
8523   ovl->Offset = ovl->OffsetHigh = 0;\r
8524   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8525   if (ok) {\r
8526     err = NO_ERROR;\r
8527   } else {\r
8528     err = GetLastError();\r
8529     if (err == ERROR_IO_PENDING) {\r
8530       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8531       if (ok)\r
8532         err = NO_ERROR;\r
8533       else\r
8534         err = GetLastError();\r
8535     }\r
8536   }\r
8537   return err;\r
8538 }\r
8539 \r
8540 int\r
8541 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8542             OVERLAPPED *ovl)\r
8543 {\r
8544   int ok, err;\r
8545 \r
8546   ResetEvent(ovl->hEvent);\r
8547   ovl->Offset = ovl->OffsetHigh = 0;\r
8548   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8549   if (ok) {\r
8550     err = NO_ERROR;\r
8551   } else {\r
8552     err = GetLastError();\r
8553     if (err == ERROR_IO_PENDING) {\r
8554       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8555       if (ok)\r
8556         err = NO_ERROR;\r
8557       else\r
8558         err = GetLastError();\r
8559     }\r
8560   }\r
8561   return err;\r
8562 }\r
8563 \r
8564 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8565 void CheckForInputBufferFull( InputSource * is )\r
8566 {\r
8567     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8568         /* Look for end of line */\r
8569         char * p = is->buf;\r
8570         \r
8571         while( p < is->next && *p != '\n' ) {\r
8572             p++;\r
8573         }\r
8574 \r
8575         if( p >= is->next ) {\r
8576             if (appData.debugMode) {\r
8577                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8578             }\r
8579 \r
8580             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8581             is->count = (DWORD) -1;\r
8582             is->next = is->buf;\r
8583         }\r
8584     }\r
8585 }\r
8586 \r
8587 DWORD\r
8588 InputThread(LPVOID arg)\r
8589 {\r
8590   InputSource *is;\r
8591   OVERLAPPED ovl;\r
8592 \r
8593   is = (InputSource *) arg;\r
8594   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8595   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8596   while (is->hThread != NULL) {\r
8597     is->error = DoReadFile(is->hFile, is->next,\r
8598                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8599                            &is->count, &ovl);\r
8600     if (is->error == NO_ERROR) {\r
8601       is->next += is->count;\r
8602     } else {\r
8603       if (is->error == ERROR_BROKEN_PIPE) {\r
8604         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8605         is->count = 0;\r
8606       } else {\r
8607         is->count = (DWORD) -1;\r
8608         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8609         break; \r
8610       }\r
8611     }\r
8612 \r
8613     CheckForInputBufferFull( is );\r
8614 \r
8615     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8616 \r
8617     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8618 \r
8619     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8620   }\r
8621 \r
8622   CloseHandle(ovl.hEvent);\r
8623   CloseHandle(is->hFile);\r
8624 \r
8625   if (appData.debugMode) {\r
8626     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8627   }\r
8628 \r
8629   return 0;\r
8630 }\r
8631 \r
8632 \r
8633 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8634 DWORD\r
8635 NonOvlInputThread(LPVOID arg)\r
8636 {\r
8637   InputSource *is;\r
8638   char *p, *q;\r
8639   int i;\r
8640   char prev;\r
8641 \r
8642   is = (InputSource *) arg;\r
8643   while (is->hThread != NULL) {\r
8644     is->error = ReadFile(is->hFile, is->next,\r
8645                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8646                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8647     if (is->error == NO_ERROR) {\r
8648       /* Change CRLF to LF */\r
8649       if (is->next > is->buf) {\r
8650         p = is->next - 1;\r
8651         i = is->count + 1;\r
8652       } else {\r
8653         p = is->next;\r
8654         i = is->count;\r
8655       }\r
8656       q = p;\r
8657       prev = NULLCHAR;\r
8658       while (i > 0) {\r
8659         if (prev == '\r' && *p == '\n') {\r
8660           *(q-1) = '\n';\r
8661           is->count--;\r
8662         } else { \r
8663           *q++ = *p;\r
8664         }\r
8665         prev = *p++;\r
8666         i--;\r
8667       }\r
8668       *q = NULLCHAR;\r
8669       is->next = q;\r
8670     } else {\r
8671       if (is->error == ERROR_BROKEN_PIPE) {\r
8672         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8673         is->count = 0; \r
8674       } else {\r
8675         is->count = (DWORD) -1;\r
8676       }\r
8677     }\r
8678 \r
8679     CheckForInputBufferFull( is );\r
8680 \r
8681     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8682 \r
8683     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8684 \r
8685     if (is->count < 0) break;  /* Quit on error */\r
8686   }\r
8687   CloseHandle(is->hFile);\r
8688   return 0;\r
8689 }\r
8690 \r
8691 DWORD\r
8692 SocketInputThread(LPVOID arg)\r
8693 {\r
8694   InputSource *is;\r
8695 \r
8696   is = (InputSource *) arg;\r
8697   while (is->hThread != NULL) {\r
8698     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8699     if ((int)is->count == SOCKET_ERROR) {\r
8700       is->count = (DWORD) -1;\r
8701       is->error = WSAGetLastError();\r
8702     } else {\r
8703       is->error = NO_ERROR;\r
8704       is->next += is->count;\r
8705       if (is->count == 0 && is->second == is) {\r
8706         /* End of file on stderr; quit with no message */\r
8707         break;\r
8708       }\r
8709     }\r
8710     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8711 \r
8712     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8713 \r
8714     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8715   }\r
8716   return 0;\r
8717 }\r
8718 \r
8719 VOID\r
8720 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8721 {\r
8722   InputSource *is;\r
8723 \r
8724   is = (InputSource *) lParam;\r
8725   if (is->lineByLine) {\r
8726     /* Feed in lines one by one */\r
8727     char *p = is->buf;\r
8728     char *q = p;\r
8729     while (q < is->next) {\r
8730       if (*q++ == '\n') {\r
8731         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8732         p = q;\r
8733       }\r
8734     }\r
8735     \r
8736     /* Move any partial line to the start of the buffer */\r
8737     q = is->buf;\r
8738     while (p < is->next) {\r
8739       *q++ = *p++;\r
8740     }\r
8741     is->next = q;\r
8742 \r
8743     if (is->error != NO_ERROR || is->count == 0) {\r
8744       /* Notify backend of the error.  Note: If there was a partial\r
8745          line at the end, it is not flushed through. */\r
8746       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8747     }\r
8748   } else {\r
8749     /* Feed in the whole chunk of input at once */\r
8750     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8751     is->next = is->buf;\r
8752   }\r
8753 }\r
8754 \r
8755 /*---------------------------------------------------------------------------*\\r
8756  *\r
8757  *  Menu enables. Used when setting various modes.\r
8758  *\r
8759 \*---------------------------------------------------------------------------*/\r
8760 \r
8761 typedef struct {\r
8762   int item;\r
8763   int flags;\r
8764 } Enables;\r
8765 \r
8766 VOID\r
8767 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8768 {\r
8769   while (enab->item > 0) {\r
8770     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8771     enab++;\r
8772   }\r
8773 }\r
8774 \r
8775 Enables gnuEnables[] = {\r
8776   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8777   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8778   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8779   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8780   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8781   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8782   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8783   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8784   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8785   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8786   { -1, -1 }\r
8787 };\r
8788 \r
8789 Enables icsEnables[] = {\r
8790   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8791   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8792   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8793   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8794   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8795   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8796   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8797   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8798   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8799   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8800   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8801   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8802   { -1, -1 }\r
8803 };\r
8804 \r
8805 #ifdef ZIPPY\r
8806 Enables zippyEnables[] = {\r
8807   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8808   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8809   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8810   { -1, -1 }\r
8811 };\r
8812 #endif\r
8813 \r
8814 Enables ncpEnables[] = {\r
8815   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8816   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8817   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8818   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8819   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8820   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8821   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8822   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8823   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8824   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8825   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8826   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8827   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8828   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8829   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8830   { -1, -1 }\r
8831 };\r
8832 \r
8833 Enables trainingOnEnables[] = {\r
8834   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8835   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8836   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8837   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8838   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8839   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8840   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8841   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8842   { -1, -1 }\r
8843 };\r
8844 \r
8845 Enables trainingOffEnables[] = {\r
8846   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8847   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8848   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8849   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8850   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8851   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8852   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8853   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8854   { -1, -1 }\r
8855 };\r
8856 \r
8857 /* These modify either ncpEnables or gnuEnables */\r
8858 Enables cmailEnables[] = {\r
8859   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8860   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8861   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8862   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8863   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8864   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8865   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8866   { -1, -1 }\r
8867 };\r
8868 \r
8869 Enables machineThinkingEnables[] = {\r
8870   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8871   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8872   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8873   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8874   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8875   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8876   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8877   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8878   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8879   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8880   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8881   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8882   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8883   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8884   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8885   { -1, -1 }\r
8886 };\r
8887 \r
8888 Enables userThinkingEnables[] = {\r
8889   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8890   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8891   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8892   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8893   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8894   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8895   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8896   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8897   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8898   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8899   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8900   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8901   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8902   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8903   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8904   { -1, -1 }\r
8905 };\r
8906 \r
8907 /*---------------------------------------------------------------------------*\\r
8908  *\r
8909  *  Front-end interface functions exported by XBoard.\r
8910  *  Functions appear in same order as prototypes in frontend.h.\r
8911  * \r
8912 \*---------------------------------------------------------------------------*/\r
8913 VOID\r
8914 ModeHighlight()\r
8915 {\r
8916   static UINT prevChecked = 0;\r
8917   static int prevPausing = 0;\r
8918   UINT nowChecked;\r
8919 \r
8920   if (pausing != prevPausing) {\r
8921     prevPausing = pausing;\r
8922     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8923                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8924     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8925   }\r
8926 \r
8927   switch (gameMode) {\r
8928   case BeginningOfGame:\r
8929     if (appData.icsActive)\r
8930       nowChecked = IDM_IcsClient;\r
8931     else if (appData.noChessProgram)\r
8932       nowChecked = IDM_EditGame;\r
8933     else\r
8934       nowChecked = IDM_MachineBlack;\r
8935     break;\r
8936   case MachinePlaysBlack:\r
8937     nowChecked = IDM_MachineBlack;\r
8938     break;\r
8939   case MachinePlaysWhite:\r
8940     nowChecked = IDM_MachineWhite;\r
8941     break;\r
8942   case TwoMachinesPlay:\r
8943     nowChecked = IDM_TwoMachines;\r
8944     break;\r
8945   case AnalyzeMode:\r
8946     nowChecked = IDM_AnalysisMode;\r
8947     break;\r
8948   case AnalyzeFile:\r
8949     nowChecked = IDM_AnalyzeFile;\r
8950     break;\r
8951   case EditGame:\r
8952     nowChecked = IDM_EditGame;\r
8953     break;\r
8954   case PlayFromGameFile:\r
8955     nowChecked = IDM_LoadGame;\r
8956     break;\r
8957   case EditPosition:\r
8958     nowChecked = IDM_EditPosition;\r
8959     break;\r
8960   case Training:\r
8961     nowChecked = IDM_Training;\r
8962     break;\r
8963   case IcsPlayingWhite:\r
8964   case IcsPlayingBlack:\r
8965   case IcsObserving:\r
8966   case IcsIdle:\r
8967     nowChecked = IDM_IcsClient;\r
8968     break;\r
8969   default:\r
8970   case EndOfGame:\r
8971     nowChecked = 0;\r
8972     break;\r
8973   }\r
8974   if (prevChecked != 0)\r
8975     (void) CheckMenuItem(GetMenu(hwndMain),\r
8976                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8977   if (nowChecked != 0)\r
8978     (void) CheckMenuItem(GetMenu(hwndMain),\r
8979                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8980 \r
8981   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8982     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8983                           MF_BYCOMMAND|MF_ENABLED);\r
8984   } else {\r
8985     (void) EnableMenuItem(GetMenu(hwndMain), \r
8986                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8987   }\r
8988 \r
8989   prevChecked = nowChecked;\r
8990 \r
8991   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8992   if (appData.icsActive) {\r
8993        if (appData.icsEngineAnalyze) {\r
8994                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8995                        MF_BYCOMMAND|MF_CHECKED);\r
8996        } else {\r
8997                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8998                        MF_BYCOMMAND|MF_UNCHECKED);\r
8999        }\r
9000   }\r
9001 }\r
9002 \r
9003 VOID\r
9004 SetICSMode()\r
9005 {\r
9006   HMENU hmenu = GetMenu(hwndMain);\r
9007   SetMenuEnables(hmenu, icsEnables);\r
9008   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
9009     MF_BYPOSITION|MF_ENABLED);\r
9010 #ifdef ZIPPY\r
9011   if (appData.zippyPlay) {\r
9012     SetMenuEnables(hmenu, zippyEnables);\r
9013     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
9014          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9015           MF_BYCOMMAND|MF_ENABLED);\r
9016   }\r
9017 #endif\r
9018 }\r
9019 \r
9020 VOID\r
9021 SetGNUMode()\r
9022 {\r
9023   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
9024 }\r
9025 \r
9026 VOID\r
9027 SetNCPMode()\r
9028 {\r
9029   HMENU hmenu = GetMenu(hwndMain);\r
9030   SetMenuEnables(hmenu, ncpEnables);\r
9031   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
9032     MF_BYPOSITION|MF_GRAYED);\r
9033     DrawMenuBar(hwndMain);\r
9034 }\r
9035 \r
9036 VOID\r
9037 SetCmailMode()\r
9038 {\r
9039   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9040 }\r
9041 \r
9042 VOID \r
9043 SetTrainingModeOn()\r
9044 {\r
9045   int i;\r
9046   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9047   for (i = 0; i < N_BUTTONS; i++) {\r
9048     if (buttonDesc[i].hwnd != NULL)\r
9049       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9050   }\r
9051   CommentPopDown();\r
9052 }\r
9053 \r
9054 VOID SetTrainingModeOff()\r
9055 {\r
9056   int i;\r
9057   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9058   for (i = 0; i < N_BUTTONS; i++) {\r
9059     if (buttonDesc[i].hwnd != NULL)\r
9060       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9061   }\r
9062 }\r
9063 \r
9064 \r
9065 VOID\r
9066 SetUserThinkingEnables()\r
9067 {\r
9068   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9069 }\r
9070 \r
9071 VOID\r
9072 SetMachineThinkingEnables()\r
9073 {\r
9074   HMENU hMenu = GetMenu(hwndMain);\r
9075   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9076 \r
9077   SetMenuEnables(hMenu, machineThinkingEnables);\r
9078 \r
9079   if (gameMode == MachinePlaysBlack) {\r
9080     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9081   } else if (gameMode == MachinePlaysWhite) {\r
9082     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9083   } else if (gameMode == TwoMachinesPlay) {\r
9084     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9085   }\r
9086 }\r
9087 \r
9088 \r
9089 VOID\r
9090 DisplayTitle(char *str)\r
9091 {\r
9092   char title[MSG_SIZ], *host;\r
9093   if (str[0] != NULLCHAR) {\r
9094     strcpy(title, str);\r
9095   } else if (appData.icsActive) {\r
9096     if (appData.icsCommPort[0] != NULLCHAR)\r
9097       host = "ICS";\r
9098     else \r
9099       host = appData.icsHost;\r
9100     sprintf(title, "%s: %s", szTitle, host);\r
9101   } else if (appData.noChessProgram) {\r
9102     strcpy(title, szTitle);\r
9103   } else {\r
9104     strcpy(title, szTitle);\r
9105     strcat(title, ": ");\r
9106     strcat(title, first.tidy);\r
9107   }\r
9108   SetWindowText(hwndMain, title);\r
9109 }\r
9110 \r
9111 \r
9112 VOID\r
9113 DisplayMessage(char *str1, char *str2)\r
9114 {\r
9115   HDC hdc;\r
9116   HFONT oldFont;\r
9117   int remain = MESSAGE_TEXT_MAX - 1;\r
9118   int len;\r
9119 \r
9120   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9121   messageText[0] = NULLCHAR;\r
9122   if (*str1) {\r
9123     len = strlen(str1);\r
9124     if (len > remain) len = remain;\r
9125     strncpy(messageText, str1, len);\r
9126     messageText[len] = NULLCHAR;\r
9127     remain -= len;\r
9128   }\r
9129   if (*str2 && remain >= 2) {\r
9130     if (*str1) {\r
9131       strcat(messageText, "  ");\r
9132       remain -= 2;\r
9133     }\r
9134     len = strlen(str2);\r
9135     if (len > remain) len = remain;\r
9136     strncat(messageText, str2, len);\r
9137   }\r
9138   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9139 \r
9140   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9141 \r
9142   SAYMACHINEMOVE();\r
9143 \r
9144   hdc = GetDC(hwndMain);\r
9145   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9146   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9147              &messageRect, messageText, strlen(messageText), NULL);\r
9148   (void) SelectObject(hdc, oldFont);\r
9149   (void) ReleaseDC(hwndMain, hdc);\r
9150 }\r
9151 \r
9152 VOID\r
9153 DisplayError(char *str, int error)\r
9154 {\r
9155   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9156   int len;\r
9157 \r
9158   if (error == 0) {\r
9159     strcpy(buf, str);\r
9160   } else {\r
9161     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9162                         NULL, error, LANG_NEUTRAL,\r
9163                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9164     if (len > 0) {\r
9165       sprintf(buf, "%s:\n%s", str, buf2);\r
9166     } else {\r
9167       ErrorMap *em = errmap;\r
9168       while (em->err != 0 && em->err != error) em++;\r
9169       if (em->err != 0) {\r
9170         sprintf(buf, "%s:\n%s", str, em->msg);\r
9171       } else {\r
9172         sprintf(buf, "%s:\nError code %d", str, error);\r
9173       }\r
9174     }\r
9175   }\r
9176   \r
9177   ErrorPopUp("Error", buf);\r
9178 }\r
9179 \r
9180 \r
9181 VOID\r
9182 DisplayMoveError(char *str)\r
9183 {\r
9184   fromX = fromY = -1;\r
9185   ClearHighlights();\r
9186   DrawPosition(FALSE, NULL);\r
9187   if (appData.popupMoveErrors) {\r
9188     ErrorPopUp("Error", str);\r
9189   } else {\r
9190     DisplayMessage(str, "");\r
9191     moveErrorMessageUp = TRUE;\r
9192   }\r
9193 }\r
9194 \r
9195 VOID\r
9196 DisplayFatalError(char *str, int error, int exitStatus)\r
9197 {\r
9198   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9199   int len;\r
9200   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9201 \r
9202   if (error != 0) {\r
9203     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9204                         NULL, error, LANG_NEUTRAL,\r
9205                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9206     if (len > 0) {\r
9207       sprintf(buf, "%s:\n%s", str, buf2);\r
9208     } else {\r
9209       ErrorMap *em = errmap;\r
9210       while (em->err != 0 && em->err != error) em++;\r
9211       if (em->err != 0) {\r
9212         sprintf(buf, "%s:\n%s", str, em->msg);\r
9213       } else {\r
9214         sprintf(buf, "%s:\nError code %d", str, error);\r
9215       }\r
9216     }\r
9217     str = buf;\r
9218   }\r
9219   if (appData.debugMode) {\r
9220     fprintf(debugFP, "%s: %s\n", label, str);\r
9221   }\r
9222   if (appData.popupExitMessage) {\r
9223     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9224                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9225   }\r
9226   ExitEvent(exitStatus);\r
9227 }\r
9228 \r
9229 \r
9230 VOID\r
9231 DisplayInformation(char *str)\r
9232 {\r
9233   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9234 }\r
9235 \r
9236 \r
9237 VOID\r
9238 DisplayNote(char *str)\r
9239 {\r
9240   ErrorPopUp("Note", str);\r
9241 }\r
9242 \r
9243 \r
9244 typedef struct {\r
9245   char *title, *question, *replyPrefix;\r
9246   ProcRef pr;\r
9247 } QuestionParams;\r
9248 \r
9249 LRESULT CALLBACK\r
9250 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9251 {\r
9252   static QuestionParams *qp;\r
9253   char reply[MSG_SIZ];\r
9254   int len, err;\r
9255 \r
9256   switch (message) {\r
9257   case WM_INITDIALOG:\r
9258     qp = (QuestionParams *) lParam;\r
9259     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9260     SetWindowText(hDlg, qp->title);\r
9261     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9262     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9263     return FALSE;\r
9264 \r
9265   case WM_COMMAND:\r
9266     switch (LOWORD(wParam)) {\r
9267     case IDOK:\r
9268       strcpy(reply, qp->replyPrefix);\r
9269       if (*reply) strcat(reply, " ");\r
9270       len = strlen(reply);\r
9271       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9272       strcat(reply, "\n");\r
9273       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9274       EndDialog(hDlg, TRUE);\r
9275       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9276       return TRUE;\r
9277     case IDCANCEL:\r
9278       EndDialog(hDlg, FALSE);\r
9279       return TRUE;\r
9280     default:\r
9281       break;\r
9282     }\r
9283     break;\r
9284   }\r
9285   return FALSE;\r
9286 }\r
9287 \r
9288 VOID\r
9289 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9290 {\r
9291     QuestionParams qp;\r
9292     FARPROC lpProc;\r
9293     \r
9294     qp.title = title;\r
9295     qp.question = question;\r
9296     qp.replyPrefix = replyPrefix;\r
9297     qp.pr = pr;\r
9298     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9299     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9300       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9301     FreeProcInstance(lpProc);\r
9302 }\r
9303 \r
9304 /* [AS] Pick FRC position */\r
9305 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9306 {\r
9307     static int * lpIndexFRC;\r
9308     BOOL index_is_ok;\r
9309     char buf[16];\r
9310 \r
9311     switch( message )\r
9312     {\r
9313     case WM_INITDIALOG:\r
9314         lpIndexFRC = (int *) lParam;\r
9315 \r
9316         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9317 \r
9318         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9319         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9320         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9321         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9322 \r
9323         break;\r
9324 \r
9325     case WM_COMMAND:\r
9326         switch( LOWORD(wParam) ) {\r
9327         case IDOK:\r
9328             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9329             EndDialog( hDlg, 0 );\r
9330             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9331             return TRUE;\r
9332         case IDCANCEL:\r
9333             EndDialog( hDlg, 1 );   \r
9334             return TRUE;\r
9335         case IDC_NFG_Edit:\r
9336             if( HIWORD(wParam) == EN_CHANGE ) {\r
9337                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9338 \r
9339                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9340             }\r
9341             return TRUE;\r
9342         case IDC_NFG_Random:\r
9343             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9344             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9345             return TRUE;\r
9346         }\r
9347 \r
9348         break;\r
9349     }\r
9350 \r
9351     return FALSE;\r
9352 }\r
9353 \r
9354 int NewGameFRC()\r
9355 {\r
9356     int result;\r
9357     int index = appData.defaultFrcPosition;\r
9358     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9359 \r
9360     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9361 \r
9362     if( result == 0 ) {\r
9363         appData.defaultFrcPosition = index;\r
9364     }\r
9365 \r
9366     return result;\r
9367 }\r
9368 \r
9369 /* [AS] Game list options */\r
9370 typedef struct {\r
9371     char id;\r
9372     char * name;\r
9373 } GLT_Item;\r
9374 \r
9375 static GLT_Item GLT_ItemInfo[] = {\r
9376     { GLT_EVENT,      "Event" },\r
9377     { GLT_SITE,       "Site" },\r
9378     { GLT_DATE,       "Date" },\r
9379     { GLT_ROUND,      "Round" },\r
9380     { GLT_PLAYERS,    "Players" },\r
9381     { GLT_RESULT,     "Result" },\r
9382     { GLT_WHITE_ELO,  "White Rating" },\r
9383     { GLT_BLACK_ELO,  "Black Rating" },\r
9384     { GLT_TIME_CONTROL,"Time Control" },\r
9385     { GLT_VARIANT,    "Variant" },\r
9386     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9387     { 0, 0 }\r
9388 };\r
9389 \r
9390 const char * GLT_FindItem( char id )\r
9391 {\r
9392     const char * result = 0;\r
9393 \r
9394     GLT_Item * list = GLT_ItemInfo;\r
9395 \r
9396     while( list->id != 0 ) {\r
9397         if( list->id == id ) {\r
9398             result = list->name;\r
9399             break;\r
9400         }\r
9401 \r
9402         list++;\r
9403     }\r
9404 \r
9405     return result;\r
9406 }\r
9407 \r
9408 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9409 {\r
9410     const char * name = GLT_FindItem( id );\r
9411 \r
9412     if( name != 0 ) {\r
9413         if( index >= 0 ) {\r
9414             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9415         }\r
9416         else {\r
9417             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9418         }\r
9419     }\r
9420 }\r
9421 \r
9422 void GLT_TagsToList( HWND hDlg, char * tags )\r
9423 {\r
9424     char * pc = tags;\r
9425 \r
9426     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9427 \r
9428     while( *pc ) {\r
9429         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9430         pc++;\r
9431     }\r
9432 \r
9433     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9434 \r
9435     pc = GLT_ALL_TAGS;\r
9436 \r
9437     while( *pc ) {\r
9438         if( strchr( tags, *pc ) == 0 ) {\r
9439             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9440         }\r
9441         pc++;\r
9442     }\r
9443 \r
9444     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9445 }\r
9446 \r
9447 char GLT_ListItemToTag( HWND hDlg, int index )\r
9448 {\r
9449     char result = '\0';\r
9450     char name[128];\r
9451 \r
9452     GLT_Item * list = GLT_ItemInfo;\r
9453 \r
9454     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9455         while( list->id != 0 ) {\r
9456             if( strcmp( list->name, name ) == 0 ) {\r
9457                 result = list->id;\r
9458                 break;\r
9459             }\r
9460 \r
9461             list++;\r
9462         }\r
9463     }\r
9464 \r
9465     return result;\r
9466 }\r
9467 \r
9468 void GLT_MoveSelection( HWND hDlg, int delta )\r
9469 {\r
9470     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9471     int idx2 = idx1 + delta;\r
9472     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9473 \r
9474     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9475         char buf[128];\r
9476 \r
9477         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9478         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9479         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9480         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9481     }\r
9482 }\r
9483 \r
9484 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9485 {\r
9486     static char glt[64];\r
9487     static char * lpUserGLT;\r
9488 \r
9489     switch( message )\r
9490     {\r
9491     case WM_INITDIALOG:\r
9492         lpUserGLT = (char *) lParam;\r
9493         \r
9494         strcpy( glt, lpUserGLT );\r
9495 \r
9496         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9497 \r
9498         /* Initialize list */\r
9499         GLT_TagsToList( hDlg, glt );\r
9500 \r
9501         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9502 \r
9503         break;\r
9504 \r
9505     case WM_COMMAND:\r
9506         switch( LOWORD(wParam) ) {\r
9507         case IDOK:\r
9508             {\r
9509                 char * pc = lpUserGLT;\r
9510                 int idx = 0;\r
9511 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9512                 char id;\r
9513 \r
9514                 do {\r
9515                     id = GLT_ListItemToTag( hDlg, idx );\r
9516 \r
9517                     *pc++ = id;\r
9518                     idx++;\r
9519                 } while( id != '\0' );\r
9520             }\r
9521             EndDialog( hDlg, 0 );\r
9522             return TRUE;\r
9523         case IDCANCEL:\r
9524             EndDialog( hDlg, 1 );\r
9525             return TRUE;\r
9526 \r
9527         case IDC_GLT_Default:\r
9528             strcpy( glt, GLT_DEFAULT_TAGS );\r
9529             GLT_TagsToList( hDlg, glt );\r
9530             return TRUE;\r
9531 \r
9532         case IDC_GLT_Restore:\r
9533             strcpy( glt, lpUserGLT );\r
9534             GLT_TagsToList( hDlg, glt );\r
9535             return TRUE;\r
9536 \r
9537         case IDC_GLT_Up:\r
9538             GLT_MoveSelection( hDlg, -1 );\r
9539             return TRUE;\r
9540 \r
9541         case IDC_GLT_Down:\r
9542             GLT_MoveSelection( hDlg, +1 );\r
9543             return TRUE;\r
9544         }\r
9545 \r
9546         break;\r
9547     }\r
9548 \r
9549     return FALSE;\r
9550 }\r
9551 \r
9552 int GameListOptions()\r
9553 {\r
9554     char glt[64];\r
9555     int result;\r
9556     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9557 \r
9558     strcpy( glt, appData.gameListTags );\r
9559 \r
9560     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9561 \r
9562     if( result == 0 ) {\r
9563         /* [AS] Memory leak here! */\r
9564         appData.gameListTags = strdup( glt ); \r
9565     }\r
9566 \r
9567     return result;\r
9568 }\r
9569 \r
9570 \r
9571 VOID\r
9572 DisplayIcsInteractionTitle(char *str)\r
9573 {\r
9574   char consoleTitle[MSG_SIZ];\r
9575 \r
9576   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9577   SetWindowText(hwndConsole, consoleTitle);\r
9578 }\r
9579 \r
9580 void\r
9581 DrawPosition(int fullRedraw, Board board)\r
9582 {\r
9583   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9584 }\r
9585 \r
9586 \r
9587 VOID\r
9588 ResetFrontEnd()\r
9589 {\r
9590   fromX = fromY = -1;\r
9591   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9592     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9593     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9594     dragInfo.lastpos = dragInfo.pos;\r
9595     dragInfo.start.x = dragInfo.start.y = -1;\r
9596     dragInfo.from = dragInfo.start;\r
9597     ReleaseCapture();\r
9598     DrawPosition(TRUE, NULL);\r
9599   }\r
9600 }\r
9601 \r
9602 \r
9603 VOID\r
9604 CommentPopUp(char *title, char *str)\r
9605 {\r
9606   HWND hwnd = GetActiveWindow();\r
9607   EitherCommentPopUp(0, title, str, FALSE);\r
9608   SetActiveWindow(hwnd);\r
9609 }\r
9610 \r
9611 VOID\r
9612 CommentPopDown(void)\r
9613 {\r
9614   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9615   if (commentDialog) {\r
9616     ShowWindow(commentDialog, SW_HIDE);\r
9617   }\r
9618   commentDialogUp = FALSE;\r
9619 }\r
9620 \r
9621 VOID\r
9622 EditCommentPopUp(int index, char *title, char *str)\r
9623 {\r
9624   EitherCommentPopUp(index, title, str, TRUE);\r
9625 }\r
9626 \r
9627 \r
9628 VOID\r
9629 RingBell()\r
9630 {\r
9631   MyPlaySound(&sounds[(int)SoundMove]);\r
9632 }\r
9633 \r
9634 VOID PlayIcsWinSound()\r
9635 {\r
9636   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9637 }\r
9638 \r
9639 VOID PlayIcsLossSound()\r
9640 {\r
9641   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9642 }\r
9643 \r
9644 VOID PlayIcsDrawSound()\r
9645 {\r
9646   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9647 }\r
9648 \r
9649 VOID PlayIcsUnfinishedSound()\r
9650 {\r
9651   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9652 }\r
9653 \r
9654 VOID\r
9655 PlayAlarmSound()\r
9656 {\r
9657   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9658 }\r
9659 \r
9660 \r
9661 VOID\r
9662 EchoOn()\r
9663 {\r
9664   HWND hInput;\r
9665   consoleEcho = TRUE;\r
9666   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9667   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9668   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9669 }\r
9670 \r
9671 \r
9672 VOID\r
9673 EchoOff()\r
9674 {\r
9675   CHARFORMAT cf;\r
9676   HWND hInput;\r
9677   consoleEcho = FALSE;\r
9678   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9679   /* This works OK: set text and background both to the same color */\r
9680   cf = consoleCF;\r
9681   cf.crTextColor = COLOR_ECHOOFF;\r
9682   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9683   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9684 }\r
9685 \r
9686 /* No Raw()...? */\r
9687 \r
9688 void Colorize(ColorClass cc, int continuation)\r
9689 {\r
9690   currentColorClass = cc;\r
9691   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9692   consoleCF.crTextColor = textAttribs[cc].color;\r
9693   consoleCF.dwEffects = textAttribs[cc].effects;\r
9694   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9695 }\r
9696 \r
9697 char *\r
9698 UserName()\r
9699 {\r
9700   static char buf[MSG_SIZ];\r
9701   DWORD bufsiz = MSG_SIZ;\r
9702 \r
9703   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9704         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9705   }\r
9706   if (!GetUserName(buf, &bufsiz)) {\r
9707     /*DisplayError("Error getting user name", GetLastError());*/\r
9708     strcpy(buf, "User");\r
9709   }\r
9710   return buf;\r
9711 }\r
9712 \r
9713 char *\r
9714 HostName()\r
9715 {\r
9716   static char buf[MSG_SIZ];\r
9717   DWORD bufsiz = MSG_SIZ;\r
9718 \r
9719   if (!GetComputerName(buf, &bufsiz)) {\r
9720     /*DisplayError("Error getting host name", GetLastError());*/\r
9721     strcpy(buf, "Unknown");\r
9722   }\r
9723   return buf;\r
9724 }\r
9725 \r
9726 \r
9727 int\r
9728 ClockTimerRunning()\r
9729 {\r
9730   return clockTimerEvent != 0;\r
9731 }\r
9732 \r
9733 int\r
9734 StopClockTimer()\r
9735 {\r
9736   if (clockTimerEvent == 0) return FALSE;\r
9737   KillTimer(hwndMain, clockTimerEvent);\r
9738   clockTimerEvent = 0;\r
9739   return TRUE;\r
9740 }\r
9741 \r
9742 void\r
9743 StartClockTimer(long millisec)\r
9744 {\r
9745   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9746                              (UINT) millisec, NULL);\r
9747 }\r
9748 \r
9749 void\r
9750 DisplayWhiteClock(long timeRemaining, int highlight)\r
9751 {\r
9752   HDC hdc;\r
9753   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9754 \r
9755   if(appData.noGUI) return;\r
9756   hdc = GetDC(hwndMain);\r
9757   if (!IsIconic(hwndMain)) {\r
9758     DisplayAClock(hdc, timeRemaining, highlight, \r
9759                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9760   }\r
9761   if (highlight && iconCurrent == iconBlack) {\r
9762     iconCurrent = iconWhite;\r
9763     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9764     if (IsIconic(hwndMain)) {\r
9765       DrawIcon(hdc, 2, 2, iconCurrent);\r
9766     }\r
9767   }\r
9768   (void) ReleaseDC(hwndMain, hdc);\r
9769   if (hwndConsole)\r
9770     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9771 }\r
9772 \r
9773 void\r
9774 DisplayBlackClock(long timeRemaining, int highlight)\r
9775 {\r
9776   HDC hdc;\r
9777   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9778 \r
9779   if(appData.noGUI) return;\r
9780   hdc = GetDC(hwndMain);\r
9781   if (!IsIconic(hwndMain)) {\r
9782     DisplayAClock(hdc, timeRemaining, highlight, \r
9783                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9784   }\r
9785   if (highlight && iconCurrent == iconWhite) {\r
9786     iconCurrent = iconBlack;\r
9787     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9788     if (IsIconic(hwndMain)) {\r
9789       DrawIcon(hdc, 2, 2, iconCurrent);\r
9790     }\r
9791   }\r
9792   (void) ReleaseDC(hwndMain, hdc);\r
9793   if (hwndConsole)\r
9794     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9795 }\r
9796 \r
9797 \r
9798 int\r
9799 LoadGameTimerRunning()\r
9800 {\r
9801   return loadGameTimerEvent != 0;\r
9802 }\r
9803 \r
9804 int\r
9805 StopLoadGameTimer()\r
9806 {\r
9807   if (loadGameTimerEvent == 0) return FALSE;\r
9808   KillTimer(hwndMain, loadGameTimerEvent);\r
9809   loadGameTimerEvent = 0;\r
9810   return TRUE;\r
9811 }\r
9812 \r
9813 void\r
9814 StartLoadGameTimer(long millisec)\r
9815 {\r
9816   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9817                                 (UINT) millisec, NULL);\r
9818 }\r
9819 \r
9820 void\r
9821 AutoSaveGame()\r
9822 {\r
9823   char *defName;\r
9824   FILE *f;\r
9825   char fileTitle[MSG_SIZ];\r
9826 \r
9827   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9828   f = OpenFileDialog(hwndMain, "a", defName,\r
9829                      appData.oldSaveStyle ? "gam" : "pgn",\r
9830                      GAME_FILT, \r
9831                      "Save Game to File", NULL, fileTitle, NULL);\r
9832   if (f != NULL) {\r
9833     SaveGame(f, 0, "");\r
9834     fclose(f);\r
9835   }\r
9836 }\r
9837 \r
9838 \r
9839 void\r
9840 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9841 {\r
9842   if (delayedTimerEvent != 0) {\r
9843     if (appData.debugMode) {\r
9844       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9845     }\r
9846     KillTimer(hwndMain, delayedTimerEvent);\r
9847     delayedTimerEvent = 0;\r
9848     delayedTimerCallback();\r
9849   }\r
9850   delayedTimerCallback = cb;\r
9851   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9852                                 (UINT) millisec, NULL);\r
9853 }\r
9854 \r
9855 DelayedEventCallback\r
9856 GetDelayedEvent()\r
9857 {\r
9858   if (delayedTimerEvent) {\r
9859     return delayedTimerCallback;\r
9860   } else {\r
9861     return NULL;\r
9862   }\r
9863 }\r
9864 \r
9865 void\r
9866 CancelDelayedEvent()\r
9867 {\r
9868   if (delayedTimerEvent) {\r
9869     KillTimer(hwndMain, delayedTimerEvent);\r
9870     delayedTimerEvent = 0;\r
9871   }\r
9872 }\r
9873 \r
9874 DWORD GetWin32Priority(int nice)\r
9875 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9876 /*\r
9877 REALTIME_PRIORITY_CLASS     0x00000100\r
9878 HIGH_PRIORITY_CLASS         0x00000080\r
9879 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9880 NORMAL_PRIORITY_CLASS       0x00000020\r
9881 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9882 IDLE_PRIORITY_CLASS         0x00000040\r
9883 */\r
9884         if (nice < -15) return 0x00000080;\r
9885         if (nice < 0)   return 0x00008000;\r
9886         if (nice == 0)  return 0x00000020;\r
9887         if (nice < 15)  return 0x00004000;\r
9888         return 0x00000040;\r
9889 }\r
9890 \r
9891 /* Start a child process running the given program.\r
9892    The process's standard output can be read from "from", and its\r
9893    standard input can be written to "to".\r
9894    Exit with fatal error if anything goes wrong.\r
9895    Returns an opaque pointer that can be used to destroy the process\r
9896    later.\r
9897 */\r
9898 int\r
9899 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9900 {\r
9901 #define BUFSIZE 4096\r
9902 \r
9903   HANDLE hChildStdinRd, hChildStdinWr,\r
9904     hChildStdoutRd, hChildStdoutWr;\r
9905   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9906   SECURITY_ATTRIBUTES saAttr;\r
9907   BOOL fSuccess;\r
9908   PROCESS_INFORMATION piProcInfo;\r
9909   STARTUPINFO siStartInfo;\r
9910   ChildProc *cp;\r
9911   char buf[MSG_SIZ];\r
9912   DWORD err;\r
9913 \r
9914   if (appData.debugMode) {\r
9915     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9916   }\r
9917 \r
9918   *pr = NoProc;\r
9919 \r
9920   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9921   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9922   saAttr.bInheritHandle = TRUE;\r
9923   saAttr.lpSecurityDescriptor = NULL;\r
9924 \r
9925   /*\r
9926    * The steps for redirecting child's STDOUT:\r
9927    *     1. Create anonymous pipe to be STDOUT for child.\r
9928    *     2. Create a noninheritable duplicate of read handle,\r
9929    *         and close the inheritable read handle.\r
9930    */\r
9931 \r
9932   /* Create a pipe for the child's STDOUT. */\r
9933   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9934     return GetLastError();\r
9935   }\r
9936 \r
9937   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9938   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9939                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9940                              FALSE,     /* not inherited */\r
9941                              DUPLICATE_SAME_ACCESS);\r
9942   if (! fSuccess) {\r
9943     return GetLastError();\r
9944   }\r
9945   CloseHandle(hChildStdoutRd);\r
9946 \r
9947   /*\r
9948    * The steps for redirecting child's STDIN:\r
9949    *     1. Create anonymous pipe to be STDIN for child.\r
9950    *     2. Create a noninheritable duplicate of write handle,\r
9951    *         and close the inheritable write handle.\r
9952    */\r
9953 \r
9954   /* Create a pipe for the child's STDIN. */\r
9955   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9956     return GetLastError();\r
9957   }\r
9958 \r
9959   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9960   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9961                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9962                              FALSE,     /* not inherited */\r
9963                              DUPLICATE_SAME_ACCESS);\r
9964   if (! fSuccess) {\r
9965     return GetLastError();\r
9966   }\r
9967   CloseHandle(hChildStdinWr);\r
9968 \r
9969   /* Arrange to (1) look in dir for the child .exe file, and\r
9970    * (2) have dir be the child's working directory.  Interpret\r
9971    * dir relative to the directory WinBoard loaded from. */\r
9972   GetCurrentDirectory(MSG_SIZ, buf);\r
9973   SetCurrentDirectory(installDir);\r
9974   SetCurrentDirectory(dir);\r
9975 \r
9976   /* Now create the child process. */\r
9977 \r
9978   siStartInfo.cb = sizeof(STARTUPINFO);\r
9979   siStartInfo.lpReserved = NULL;\r
9980   siStartInfo.lpDesktop = NULL;\r
9981   siStartInfo.lpTitle = NULL;\r
9982   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9983   siStartInfo.cbReserved2 = 0;\r
9984   siStartInfo.lpReserved2 = NULL;\r
9985   siStartInfo.hStdInput = hChildStdinRd;\r
9986   siStartInfo.hStdOutput = hChildStdoutWr;\r
9987   siStartInfo.hStdError = hChildStdoutWr;\r
9988 \r
9989   fSuccess = CreateProcess(NULL,\r
9990                            cmdLine,        /* command line */\r
9991                            NULL,           /* process security attributes */\r
9992                            NULL,           /* primary thread security attrs */\r
9993                            TRUE,           /* handles are inherited */\r
9994                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9995                            NULL,           /* use parent's environment */\r
9996                            NULL,\r
9997                            &siStartInfo, /* STARTUPINFO pointer */\r
9998                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9999 \r
10000   err = GetLastError();\r
10001   SetCurrentDirectory(buf); /* return to prev directory */\r
10002   if (! fSuccess) {\r
10003     return err;\r
10004   }\r
10005 \r
10006   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
10007     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
10008     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
10009   }\r
10010 \r
10011   /* Close the handles we don't need in the parent */\r
10012   CloseHandle(piProcInfo.hThread);\r
10013   CloseHandle(hChildStdinRd);\r
10014   CloseHandle(hChildStdoutWr);\r
10015 \r
10016   /* Prepare return value */\r
10017   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10018   cp->kind = CPReal;\r
10019   cp->hProcess = piProcInfo.hProcess;\r
10020   cp->pid = piProcInfo.dwProcessId;\r
10021   cp->hFrom = hChildStdoutRdDup;\r
10022   cp->hTo = hChildStdinWrDup;\r
10023 \r
10024   *pr = (void *) cp;\r
10025 \r
10026   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10027      2000 where engines sometimes don't see the initial command(s)\r
10028      from WinBoard and hang.  I don't understand how that can happen,\r
10029      but the Sleep is harmless, so I've put it in.  Others have also\r
10030      reported what may be the same problem, so hopefully this will fix\r
10031      it for them too.  */\r
10032   Sleep(500);\r
10033 \r
10034   return NO_ERROR;\r
10035 }\r
10036 \r
10037 \r
10038 void\r
10039 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10040 {\r
10041   ChildProc *cp; int result;\r
10042 \r
10043   cp = (ChildProc *) pr;\r
10044   if (cp == NULL) return;\r
10045 \r
10046   switch (cp->kind) {\r
10047   case CPReal:\r
10048     /* TerminateProcess is considered harmful, so... */\r
10049     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10050     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10051     /* The following doesn't work because the chess program\r
10052        doesn't "have the same console" as WinBoard.  Maybe\r
10053        we could arrange for this even though neither WinBoard\r
10054        nor the chess program uses a console for stdio? */\r
10055     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10056 \r
10057     /* [AS] Special termination modes for misbehaving programs... */\r
10058     if( signal == 9 ) { \r
10059         result = TerminateProcess( cp->hProcess, 0 );\r
10060 \r
10061         if ( appData.debugMode) {\r
10062             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10063         }\r
10064     }\r
10065     else if( signal == 10 ) {\r
10066         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10067 \r
10068         if( dw != WAIT_OBJECT_0 ) {\r
10069             result = TerminateProcess( cp->hProcess, 0 );\r
10070 \r
10071             if ( appData.debugMode) {\r
10072                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10073             }\r
10074 \r
10075         }\r
10076     }\r
10077 \r
10078     CloseHandle(cp->hProcess);\r
10079     break;\r
10080 \r
10081   case CPComm:\r
10082     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10083     break;\r
10084 \r
10085   case CPSock:\r
10086     closesocket(cp->sock);\r
10087     WSACleanup();\r
10088     break;\r
10089 \r
10090   case CPRcmd:\r
10091     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10092     closesocket(cp->sock);\r
10093     closesocket(cp->sock2);\r
10094     WSACleanup();\r
10095     break;\r
10096   }\r
10097   free(cp);\r
10098 }\r
10099 \r
10100 void\r
10101 InterruptChildProcess(ProcRef pr)\r
10102 {\r
10103   ChildProc *cp;\r
10104 \r
10105   cp = (ChildProc *) pr;\r
10106   if (cp == NULL) return;\r
10107   switch (cp->kind) {\r
10108   case CPReal:\r
10109     /* The following doesn't work because the chess program\r
10110        doesn't "have the same console" as WinBoard.  Maybe\r
10111        we could arrange for this even though neither WinBoard\r
10112        nor the chess program uses a console for stdio */\r
10113     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10114     break;\r
10115 \r
10116   case CPComm:\r
10117   case CPSock:\r
10118     /* Can't interrupt */\r
10119     break;\r
10120 \r
10121   case CPRcmd:\r
10122     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10123     break;\r
10124   }\r
10125 }\r
10126 \r
10127 \r
10128 int\r
10129 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10130 {\r
10131   char cmdLine[MSG_SIZ];\r
10132 \r
10133   if (port[0] == NULLCHAR) {\r
10134     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10135   } else {\r
10136     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10137   }\r
10138   return StartChildProcess(cmdLine, "", pr);\r
10139 }\r
10140 \r
10141 \r
10142 /* Code to open TCP sockets */\r
10143 \r
10144 int\r
10145 OpenTCP(char *host, char *port, ProcRef *pr)\r
10146 {\r
10147   ChildProc *cp;\r
10148   int err;\r
10149   SOCKET s;\r
10150   struct sockaddr_in sa, mysa;\r
10151   struct hostent FAR *hp;\r
10152   unsigned short uport;\r
10153   WORD wVersionRequested;\r
10154   WSADATA wsaData;\r
10155 \r
10156   /* Initialize socket DLL */\r
10157   wVersionRequested = MAKEWORD(1, 1);\r
10158   err = WSAStartup(wVersionRequested, &wsaData);\r
10159   if (err != 0) return err;\r
10160 \r
10161   /* Make socket */\r
10162   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10163     err = WSAGetLastError();\r
10164     WSACleanup();\r
10165     return err;\r
10166   }\r
10167 \r
10168   /* Bind local address using (mostly) don't-care values.\r
10169    */\r
10170   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10171   mysa.sin_family = AF_INET;\r
10172   mysa.sin_addr.s_addr = INADDR_ANY;\r
10173   uport = (unsigned short) 0;\r
10174   mysa.sin_port = htons(uport);\r
10175   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10176       == SOCKET_ERROR) {\r
10177     err = WSAGetLastError();\r
10178     WSACleanup();\r
10179     return err;\r
10180   }\r
10181 \r
10182   /* Resolve remote host name */\r
10183   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10184   if (!(hp = gethostbyname(host))) {\r
10185     unsigned int b0, b1, b2, b3;\r
10186 \r
10187     err = WSAGetLastError();\r
10188 \r
10189     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10190       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10191       hp->h_addrtype = AF_INET;\r
10192       hp->h_length = 4;\r
10193       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10194       hp->h_addr_list[0] = (char *) malloc(4);\r
10195       hp->h_addr_list[0][0] = (char) b0;\r
10196       hp->h_addr_list[0][1] = (char) b1;\r
10197       hp->h_addr_list[0][2] = (char) b2;\r
10198       hp->h_addr_list[0][3] = (char) b3;\r
10199     } else {\r
10200       WSACleanup();\r
10201       return err;\r
10202     }\r
10203   }\r
10204   sa.sin_family = hp->h_addrtype;\r
10205   uport = (unsigned short) atoi(port);\r
10206   sa.sin_port = htons(uport);\r
10207   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10208 \r
10209   /* Make connection */\r
10210   if (connect(s, (struct sockaddr *) &sa,\r
10211               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10212     err = WSAGetLastError();\r
10213     WSACleanup();\r
10214     return err;\r
10215   }\r
10216 \r
10217   /* Prepare return value */\r
10218   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10219   cp->kind = CPSock;\r
10220   cp->sock = s;\r
10221   *pr = (ProcRef *) cp;\r
10222 \r
10223   return NO_ERROR;\r
10224 }\r
10225 \r
10226 int\r
10227 OpenCommPort(char *name, ProcRef *pr)\r
10228 {\r
10229   HANDLE h;\r
10230   COMMTIMEOUTS ct;\r
10231   ChildProc *cp;\r
10232   char fullname[MSG_SIZ];\r
10233 \r
10234   if (*name != '\\')\r
10235     sprintf(fullname, "\\\\.\\%s", name);\r
10236   else\r
10237     strcpy(fullname, name);\r
10238 \r
10239   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10240                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10241   if (h == (HANDLE) -1) {\r
10242     return GetLastError();\r
10243   }\r
10244   hCommPort = h;\r
10245 \r
10246   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10247 \r
10248   /* Accumulate characters until a 100ms pause, then parse */\r
10249   ct.ReadIntervalTimeout = 100;\r
10250   ct.ReadTotalTimeoutMultiplier = 0;\r
10251   ct.ReadTotalTimeoutConstant = 0;\r
10252   ct.WriteTotalTimeoutMultiplier = 0;\r
10253   ct.WriteTotalTimeoutConstant = 0;\r
10254   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10255 \r
10256   /* Prepare return value */\r
10257   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10258   cp->kind = CPComm;\r
10259   cp->hFrom = h;\r
10260   cp->hTo = h;\r
10261   *pr = (ProcRef *) cp;\r
10262 \r
10263   return NO_ERROR;\r
10264 }\r
10265 \r
10266 int\r
10267 OpenLoopback(ProcRef *pr)\r
10268 {\r
10269   DisplayFatalError("Not implemented", 0, 1);\r
10270   return NO_ERROR;\r
10271 }\r
10272 \r
10273 \r
10274 int\r
10275 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10276 {\r
10277   ChildProc *cp;\r
10278   int err;\r
10279   SOCKET s, s2, s3;\r
10280   struct sockaddr_in sa, mysa;\r
10281   struct hostent FAR *hp;\r
10282   unsigned short uport;\r
10283   WORD wVersionRequested;\r
10284   WSADATA wsaData;\r
10285   int fromPort;\r
10286   char stderrPortStr[MSG_SIZ];\r
10287 \r
10288   /* Initialize socket DLL */\r
10289   wVersionRequested = MAKEWORD(1, 1);\r
10290   err = WSAStartup(wVersionRequested, &wsaData);\r
10291   if (err != 0) return err;\r
10292 \r
10293   /* Resolve remote host name */\r
10294   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10295   if (!(hp = gethostbyname(host))) {\r
10296     unsigned int b0, b1, b2, b3;\r
10297 \r
10298     err = WSAGetLastError();\r
10299 \r
10300     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10301       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10302       hp->h_addrtype = AF_INET;\r
10303       hp->h_length = 4;\r
10304       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10305       hp->h_addr_list[0] = (char *) malloc(4);\r
10306       hp->h_addr_list[0][0] = (char) b0;\r
10307       hp->h_addr_list[0][1] = (char) b1;\r
10308       hp->h_addr_list[0][2] = (char) b2;\r
10309       hp->h_addr_list[0][3] = (char) b3;\r
10310     } else {\r
10311       WSACleanup();\r
10312       return err;\r
10313     }\r
10314   }\r
10315   sa.sin_family = hp->h_addrtype;\r
10316   uport = (unsigned short) 514;\r
10317   sa.sin_port = htons(uport);\r
10318   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10319 \r
10320   /* Bind local socket to unused "privileged" port address\r
10321    */\r
10322   s = INVALID_SOCKET;\r
10323   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10324   mysa.sin_family = AF_INET;\r
10325   mysa.sin_addr.s_addr = INADDR_ANY;\r
10326   for (fromPort = 1023;; fromPort--) {\r
10327     if (fromPort < 0) {\r
10328       WSACleanup();\r
10329       return WSAEADDRINUSE;\r
10330     }\r
10331     if (s == INVALID_SOCKET) {\r
10332       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10333         err = WSAGetLastError();\r
10334         WSACleanup();\r
10335         return err;\r
10336       }\r
10337     }\r
10338     uport = (unsigned short) fromPort;\r
10339     mysa.sin_port = htons(uport);\r
10340     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10341         == SOCKET_ERROR) {\r
10342       err = WSAGetLastError();\r
10343       if (err == WSAEADDRINUSE) continue;\r
10344       WSACleanup();\r
10345       return err;\r
10346     }\r
10347     if (connect(s, (struct sockaddr *) &sa,\r
10348       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10349       err = WSAGetLastError();\r
10350       if (err == WSAEADDRINUSE) {\r
10351         closesocket(s);\r
10352         s = -1;\r
10353         continue;\r
10354       }\r
10355       WSACleanup();\r
10356       return err;\r
10357     }\r
10358     break;\r
10359   }\r
10360 \r
10361   /* Bind stderr local socket to unused "privileged" port address\r
10362    */\r
10363   s2 = INVALID_SOCKET;\r
10364   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10365   mysa.sin_family = AF_INET;\r
10366   mysa.sin_addr.s_addr = INADDR_ANY;\r
10367   for (fromPort = 1023;; fromPort--) {\r
10368     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10369     if (fromPort < 0) {\r
10370       (void) closesocket(s);\r
10371       WSACleanup();\r
10372       return WSAEADDRINUSE;\r
10373     }\r
10374     if (s2 == INVALID_SOCKET) {\r
10375       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10376         err = WSAGetLastError();\r
10377         closesocket(s);\r
10378         WSACleanup();\r
10379         return err;\r
10380       }\r
10381     }\r
10382     uport = (unsigned short) fromPort;\r
10383     mysa.sin_port = htons(uport);\r
10384     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10385         == SOCKET_ERROR) {\r
10386       err = WSAGetLastError();\r
10387       if (err == WSAEADDRINUSE) continue;\r
10388       (void) closesocket(s);\r
10389       WSACleanup();\r
10390       return err;\r
10391     }\r
10392     if (listen(s2, 1) == SOCKET_ERROR) {\r
10393       err = WSAGetLastError();\r
10394       if (err == WSAEADDRINUSE) {\r
10395         closesocket(s2);\r
10396         s2 = INVALID_SOCKET;\r
10397         continue;\r
10398       }\r
10399       (void) closesocket(s);\r
10400       (void) closesocket(s2);\r
10401       WSACleanup();\r
10402       return err;\r
10403     }\r
10404     break;\r
10405   }\r
10406   prevStderrPort = fromPort; // remember port used\r
10407   sprintf(stderrPortStr, "%d", fromPort);\r
10408 \r
10409   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10410     err = WSAGetLastError();\r
10411     (void) closesocket(s);\r
10412     (void) closesocket(s2);\r
10413     WSACleanup();\r
10414     return err;\r
10415   }\r
10416 \r
10417   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10418     err = WSAGetLastError();\r
10419     (void) closesocket(s);\r
10420     (void) closesocket(s2);\r
10421     WSACleanup();\r
10422     return err;\r
10423   }\r
10424   if (*user == NULLCHAR) user = UserName();\r
10425   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10426     err = WSAGetLastError();\r
10427     (void) closesocket(s);\r
10428     (void) closesocket(s2);\r
10429     WSACleanup();\r
10430     return err;\r
10431   }\r
10432   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10433     err = WSAGetLastError();\r
10434     (void) closesocket(s);\r
10435     (void) closesocket(s2);\r
10436     WSACleanup();\r
10437     return err;\r
10438   }\r
10439 \r
10440   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10441     err = WSAGetLastError();\r
10442     (void) closesocket(s);\r
10443     (void) closesocket(s2);\r
10444     WSACleanup();\r
10445     return err;\r
10446   }\r
10447   (void) closesocket(s2);  /* Stop listening */\r
10448 \r
10449   /* Prepare return value */\r
10450   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10451   cp->kind = CPRcmd;\r
10452   cp->sock = s;\r
10453   cp->sock2 = s3;\r
10454   *pr = (ProcRef *) cp;\r
10455 \r
10456   return NO_ERROR;\r
10457 }\r
10458 \r
10459 \r
10460 InputSourceRef\r
10461 AddInputSource(ProcRef pr, int lineByLine,\r
10462                InputCallback func, VOIDSTAR closure)\r
10463 {\r
10464   InputSource *is, *is2 = NULL;\r
10465   ChildProc *cp = (ChildProc *) pr;\r
10466 \r
10467   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10468   is->lineByLine = lineByLine;\r
10469   is->func = func;\r
10470   is->closure = closure;\r
10471   is->second = NULL;\r
10472   is->next = is->buf;\r
10473   if (pr == NoProc) {\r
10474     is->kind = CPReal;\r
10475     consoleInputSource = is;\r
10476   } else {\r
10477     is->kind = cp->kind;\r
10478     /* \r
10479         [AS] Try to avoid a race condition if the thread is given control too early:\r
10480         we create all threads suspended so that the is->hThread variable can be\r
10481         safely assigned, then let the threads start with ResumeThread.\r
10482     */\r
10483     switch (cp->kind) {\r
10484     case CPReal:\r
10485       is->hFile = cp->hFrom;\r
10486       cp->hFrom = NULL; /* now owned by InputThread */\r
10487       is->hThread =\r
10488         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10489                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10490       break;\r
10491 \r
10492     case CPComm:\r
10493       is->hFile = cp->hFrom;\r
10494       cp->hFrom = NULL; /* now owned by InputThread */\r
10495       is->hThread =\r
10496         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10497                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10498       break;\r
10499 \r
10500     case CPSock:\r
10501       is->sock = cp->sock;\r
10502       is->hThread =\r
10503         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10504                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10505       break;\r
10506 \r
10507     case CPRcmd:\r
10508       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10509       *is2 = *is;\r
10510       is->sock = cp->sock;\r
10511       is->second = is2;\r
10512       is2->sock = cp->sock2;\r
10513       is2->second = is2;\r
10514       is->hThread =\r
10515         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10516                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10517       is2->hThread =\r
10518         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10519                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10520       break;\r
10521     }\r
10522 \r
10523     if( is->hThread != NULL ) {\r
10524         ResumeThread( is->hThread );\r
10525     }\r
10526 \r
10527     if( is2 != NULL && is2->hThread != NULL ) {\r
10528         ResumeThread( is2->hThread );\r
10529     }\r
10530   }\r
10531 \r
10532   return (InputSourceRef) is;\r
10533 }\r
10534 \r
10535 void\r
10536 RemoveInputSource(InputSourceRef isr)\r
10537 {\r
10538   InputSource *is;\r
10539 \r
10540   is = (InputSource *) isr;\r
10541   is->hThread = NULL;  /* tell thread to stop */\r
10542   CloseHandle(is->hThread);\r
10543   if (is->second != NULL) {\r
10544     is->second->hThread = NULL;\r
10545     CloseHandle(is->second->hThread);\r
10546   }\r
10547 }\r
10548 \r
10549 \r
10550 int\r
10551 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10552 {\r
10553   DWORD dOutCount;\r
10554   int outCount = SOCKET_ERROR;\r
10555   ChildProc *cp = (ChildProc *) pr;\r
10556   static OVERLAPPED ovl;\r
10557 \r
10558   if (pr == NoProc) {\r
10559     ConsoleOutput(message, count, FALSE);\r
10560     return count;\r
10561   } \r
10562 \r
10563   if (ovl.hEvent == NULL) {\r
10564     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10565   }\r
10566   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10567 \r
10568   switch (cp->kind) {\r
10569   case CPSock:\r
10570   case CPRcmd:\r
10571     outCount = send(cp->sock, message, count, 0);\r
10572     if (outCount == SOCKET_ERROR) {\r
10573       *outError = WSAGetLastError();\r
10574     } else {\r
10575       *outError = NO_ERROR;\r
10576     }\r
10577     break;\r
10578 \r
10579   case CPReal:\r
10580     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10581                   &dOutCount, NULL)) {\r
10582       *outError = NO_ERROR;\r
10583       outCount = (int) dOutCount;\r
10584     } else {\r
10585       *outError = GetLastError();\r
10586     }\r
10587     break;\r
10588 \r
10589   case CPComm:\r
10590     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10591                             &dOutCount, &ovl);\r
10592     if (*outError == NO_ERROR) {\r
10593       outCount = (int) dOutCount;\r
10594     }\r
10595     break;\r
10596   }\r
10597   return outCount;\r
10598 }\r
10599 \r
10600 int\r
10601 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10602                        long msdelay)\r
10603 {\r
10604   /* Ignore delay, not implemented for WinBoard */\r
10605   return OutputToProcess(pr, message, count, outError);\r
10606 }\r
10607 \r
10608 \r
10609 void\r
10610 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10611                         char *buf, int count, int error)\r
10612 {\r
10613   DisplayFatalError("Not implemented", 0, 1);\r
10614 }\r
10615 \r
10616 /* see wgamelist.c for Game List functions */\r
10617 /* see wedittags.c for Edit Tags functions */\r
10618 \r
10619 \r
10620 VOID\r
10621 ICSInitScript()\r
10622 {\r
10623   FILE *f;\r
10624   char buf[MSG_SIZ];\r
10625   char *dummy;\r
10626 \r
10627   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10628     f = fopen(buf, "r");\r
10629     if (f != NULL) {\r
10630       ProcessICSInitScript(f);\r
10631       fclose(f);\r
10632     }\r
10633   }\r
10634 }\r
10635 \r
10636 \r
10637 VOID\r
10638 StartAnalysisClock()\r
10639 {\r
10640   if (analysisTimerEvent) return;\r
10641   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10642                                         (UINT) 2000, NULL);\r
10643 }\r
10644 \r
10645 LRESULT CALLBACK\r
10646 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10647 {\r
10648   static HANDLE hwndText;\r
10649   RECT rect;\r
10650   static int sizeX, sizeY;\r
10651   int newSizeX, newSizeY, flags;\r
10652   MINMAXINFO *mmi;\r
10653 \r
10654   switch (message) {\r
10655   case WM_INITDIALOG: /* message: initialize dialog box */\r
10656     /* Initialize the dialog items */\r
10657     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10658     SetWindowText(hDlg, analysisTitle);\r
10659     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10660     /* Size and position the dialog */\r
10661     if (!analysisDialog) {\r
10662       analysisDialog = hDlg;\r
10663       flags = SWP_NOZORDER;\r
10664       GetClientRect(hDlg, &rect);\r
10665       sizeX = rect.right;\r
10666       sizeY = rect.bottom;\r
10667       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10668           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10669         WINDOWPLACEMENT wp;\r
10670         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10671         wp.length = sizeof(WINDOWPLACEMENT);\r
10672         wp.flags = 0;\r
10673         wp.showCmd = SW_SHOW;\r
10674         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10675         wp.rcNormalPosition.left = analysisX;\r
10676         wp.rcNormalPosition.right = analysisX + analysisW;\r
10677         wp.rcNormalPosition.top = analysisY;\r
10678         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10679         SetWindowPlacement(hDlg, &wp);\r
10680 \r
10681         GetClientRect(hDlg, &rect);\r
10682         newSizeX = rect.right;\r
10683         newSizeY = rect.bottom;\r
10684         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10685                               newSizeX, newSizeY);\r
10686         sizeX = newSizeX;\r
10687         sizeY = newSizeY;\r
10688       }\r
10689     }\r
10690     return FALSE;\r
10691 \r
10692   case WM_COMMAND: /* message: received a command */\r
10693     switch (LOWORD(wParam)) {\r
10694     case IDCANCEL:\r
10695       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10696           ExitAnalyzeMode();\r
10697           ModeHighlight();\r
10698           return TRUE;\r
10699       }\r
10700       EditGameEvent();\r
10701       return TRUE;\r
10702     default:\r
10703       break;\r
10704     }\r
10705     break;\r
10706 \r
10707   case WM_SIZE:\r
10708     newSizeX = LOWORD(lParam);\r
10709     newSizeY = HIWORD(lParam);\r
10710     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10711     sizeX = newSizeX;\r
10712     sizeY = newSizeY;\r
10713     break;\r
10714 \r
10715   case WM_GETMINMAXINFO:\r
10716     /* Prevent resizing window too small */\r
10717     mmi = (MINMAXINFO *) lParam;\r
10718     mmi->ptMinTrackSize.x = 100;\r
10719     mmi->ptMinTrackSize.y = 100;\r
10720     break;\r
10721   }\r
10722   return FALSE;\r
10723 }\r
10724 \r
10725 VOID\r
10726 AnalysisPopUp(char* title, char* str)\r
10727 {\r
10728   FARPROC lpProc;\r
10729   char *p, *q;\r
10730 \r
10731   /* [AS] */\r
10732   EngineOutputPopUp();\r
10733   return;\r
10734 \r
10735   if (str == NULL) str = "";\r
10736   p = (char *) malloc(2 * strlen(str) + 2);\r
10737   q = p;\r
10738   while (*str) {\r
10739     if (*str == '\n') *q++ = '\r';\r
10740     *q++ = *str++;\r
10741   }\r
10742   *q = NULLCHAR;\r
10743   if (analysisText != NULL) free(analysisText);\r
10744   analysisText = p;\r
10745 \r
10746   if (analysisDialog) {\r
10747     SetWindowText(analysisDialog, title);\r
10748     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10749     ShowWindow(analysisDialog, SW_SHOW);\r
10750   } else {\r
10751     analysisTitle = title;\r
10752     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10753     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10754                  hwndMain, (DLGPROC)lpProc);\r
10755     FreeProcInstance(lpProc);\r
10756   }\r
10757   analysisDialogUp = TRUE;  \r
10758 }\r
10759 \r
10760 VOID\r
10761 AnalysisPopDown()\r
10762 {\r
10763   if (analysisDialog) {\r
10764     ShowWindow(analysisDialog, SW_HIDE);\r
10765   }\r
10766   analysisDialogUp = FALSE;  \r
10767 }\r
10768 \r
10769 \r
10770 VOID\r
10771 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10772 {\r
10773   highlightInfo.sq[0].x = fromX;\r
10774   highlightInfo.sq[0].y = fromY;\r
10775   highlightInfo.sq[1].x = toX;\r
10776   highlightInfo.sq[1].y = toY;\r
10777 }\r
10778 \r
10779 VOID\r
10780 ClearHighlights()\r
10781 {\r
10782   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10783     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10784 }\r
10785 \r
10786 VOID\r
10787 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10788 {\r
10789   premoveHighlightInfo.sq[0].x = fromX;\r
10790   premoveHighlightInfo.sq[0].y = fromY;\r
10791   premoveHighlightInfo.sq[1].x = toX;\r
10792   premoveHighlightInfo.sq[1].y = toY;\r
10793 }\r
10794 \r
10795 VOID\r
10796 ClearPremoveHighlights()\r
10797 {\r
10798   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10799     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10800 }\r
10801 \r
10802 VOID\r
10803 ShutDownFrontEnd()\r
10804 {\r
10805   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10806   DeleteClipboardTempFiles();\r
10807 }\r
10808 \r
10809 void\r
10810 BoardToTop()\r
10811 {\r
10812     if (IsIconic(hwndMain))\r
10813       ShowWindow(hwndMain, SW_RESTORE);\r
10814 \r
10815     SetActiveWindow(hwndMain);\r
10816 }\r
10817 \r
10818 /*\r
10819  * Prototypes for animation support routines\r
10820  */\r
10821 static void ScreenSquare(int column, int row, POINT * pt);\r
10822 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10823      POINT frames[], int * nFrames);\r
10824 \r
10825 \r
10826 void\r
10827 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10828 {       // [HGM] atomic: animate blast wave\r
10829         int i;\r
10830 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10831         explodeInfo.fromX = fromX;\r
10832         explodeInfo.fromY = fromY;\r
10833         explodeInfo.toX = toX;\r
10834         explodeInfo.toY = toY;\r
10835         for(i=1; i<nFrames; i++) {\r
10836             explodeInfo.radius = (i*180)/(nFrames-1);\r
10837             DrawPosition(FALSE, NULL);\r
10838             Sleep(appData.animSpeed);\r
10839         }\r
10840         explodeInfo.radius = 0;\r
10841         DrawPosition(TRUE, NULL);\r
10842 }\r
10843 \r
10844 #define kFactor 4\r
10845 \r
10846 void\r
10847 AnimateMove(board, fromX, fromY, toX, toY)\r
10848      Board board;\r
10849      int fromX;\r
10850      int fromY;\r
10851      int toX;\r
10852      int toY;\r
10853 {\r
10854   ChessSquare piece;\r
10855   POINT start, finish, mid;\r
10856   POINT frames[kFactor * 2 + 1];\r
10857   int nFrames, n;\r
10858 \r
10859   if (!appData.animate) return;\r
10860   if (doingSizing) return;\r
10861   if (fromY < 0 || fromX < 0) return;\r
10862   piece = board[fromY][fromX];\r
10863   if (piece >= EmptySquare) return;\r
10864 \r
10865   ScreenSquare(fromX, fromY, &start);\r
10866   ScreenSquare(toX, toY, &finish);\r
10867 \r
10868   /* All pieces except knights move in straight line */\r
10869   if (piece != WhiteKnight && piece != BlackKnight) {\r
10870     mid.x = start.x + (finish.x - start.x) / 2;\r
10871     mid.y = start.y + (finish.y - start.y) / 2;\r
10872   } else {\r
10873     /* Knight: make diagonal movement then straight */\r
10874     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10875        mid.x = start.x + (finish.x - start.x) / 2;\r
10876        mid.y = finish.y;\r
10877      } else {\r
10878        mid.x = finish.x;\r
10879        mid.y = start.y + (finish.y - start.y) / 2;\r
10880      }\r
10881   }\r
10882   \r
10883   /* Don't use as many frames for very short moves */\r
10884   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10885     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10886   else\r
10887     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10888 \r
10889   animInfo.from.x = fromX;\r
10890   animInfo.from.y = fromY;\r
10891   animInfo.to.x = toX;\r
10892   animInfo.to.y = toY;\r
10893   animInfo.lastpos = start;\r
10894   animInfo.piece = piece;\r
10895   for (n = 0; n < nFrames; n++) {\r
10896     animInfo.pos = frames[n];\r
10897     DrawPosition(FALSE, NULL);\r
10898     animInfo.lastpos = animInfo.pos;\r
10899     Sleep(appData.animSpeed);\r
10900   }\r
10901   animInfo.pos = finish;\r
10902   DrawPosition(FALSE, NULL);\r
10903   animInfo.piece = EmptySquare;\r
10904   if(gameInfo.variant == VariantAtomic && \r
10905      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10906         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10907 }\r
10908 \r
10909 /*      Convert board position to corner of screen rect and color       */\r
10910 \r
10911 static void\r
10912 ScreenSquare(column, row, pt)\r
10913      int column; int row; POINT * pt;\r
10914 {\r
10915   if (flipView) {\r
10916     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10917     pt->y = lineGap + row * (squareSize + lineGap);\r
10918   } else {\r
10919     pt->x = lineGap + column * (squareSize + lineGap);\r
10920     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10921   }\r
10922 }\r
10923 \r
10924 /*      Generate a series of frame coords from start->mid->finish.\r
10925         The movement rate doubles until the half way point is\r
10926         reached, then halves back down to the final destination,\r
10927         which gives a nice slow in/out effect. The algorithmn\r
10928         may seem to generate too many intermediates for short\r
10929         moves, but remember that the purpose is to attract the\r
10930         viewers attention to the piece about to be moved and\r
10931         then to where it ends up. Too few frames would be less\r
10932         noticeable.                                             */\r
10933 \r
10934 static void\r
10935 Tween(start, mid, finish, factor, frames, nFrames)\r
10936      POINT * start; POINT * mid;\r
10937      POINT * finish; int factor;\r
10938      POINT frames[]; int * nFrames;\r
10939 {\r
10940   int n, fraction = 1, count = 0;\r
10941 \r
10942   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10943   for (n = 0; n < factor; n++)\r
10944     fraction *= 2;\r
10945   for (n = 0; n < factor; n++) {\r
10946     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10947     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10948     count ++;\r
10949     fraction = fraction / 2;\r
10950   }\r
10951   \r
10952   /* Midpoint */\r
10953   frames[count] = *mid;\r
10954   count ++;\r
10955   \r
10956   /* Slow out, stepping 1/2, then 1/4, ... */\r
10957   fraction = 2;\r
10958   for (n = 0; n < factor; n++) {\r
10959     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10960     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10961     count ++;\r
10962     fraction = fraction * 2;\r
10963   }\r
10964   *nFrames = count;\r
10965 }\r
10966 \r
10967 void\r
10968 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10969 {\r
10970 #if 0\r
10971     char buf[256];\r
10972 \r
10973     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10974         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10975 \r
10976     OutputDebugString( buf );\r
10977 #endif\r
10978 \r
10979     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10980 \r
10981     EvalGraphSet( first, last, current, pvInfoList );\r
10982 }\r
10983 \r
10984 void SetProgramStats( FrontEndProgramStats * stats )\r
10985 {\r
10986 #if 0\r
10987     char buf[1024];\r
10988 \r
10989     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10990         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10991 \r
10992     OutputDebugString( buf );\r
10993 #endif\r
10994 \r
10995     EngineOutputUpdate( stats );\r
10996 }\r