updated copyright to reflect A. Scotte as copyright holder
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "winboard.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "wgamelist.h"\r
89 #include "wedittags.h"\r
90 #include "woptions.h"\r
91 #include "wsockerr.h"\r
92 #include "defaults.h"\r
93 #include "help.h"\r
94 #include "wsnap.h"\r
95 \r
96 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
97 \r
98   int myrandom(void);\r
99   void mysrandom(unsigned int seed);\r
100 \r
101 extern int whiteFlag, blackFlag;\r
102 Boolean flipClock = FALSE;\r
103 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 typedef struct {\r
112   ChessSquare piece;  \r
113   POINT pos;      /* window coordinates of current pos */\r
114   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
115   POINT from;     /* board coordinates of the piece's orig pos */\r
116   POINT to;       /* board coordinates of the piece's new pos */\r
117 } AnimInfo;\r
118 \r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
120 \r
121 typedef struct {\r
122   POINT start;    /* window coordinates of start pos */\r
123   POINT pos;      /* window coordinates of current pos */\r
124   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
125   POINT from;     /* board coordinates of the piece's orig pos */\r
126 } DragInfo;\r
127 \r
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
129 \r
130 typedef struct {\r
131   POINT sq[2];    /* board coordinates of from, to squares */\r
132 } HighlightInfo;\r
133 \r
134 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
136 \r
137 typedef struct { // [HGM] atomic\r
138   int fromX, fromY, toX, toY, radius;\r
139 } ExplodeInfo;\r
140 \r
141 static ExplodeInfo explodeInfo;\r
142 \r
143 /* Window class names */\r
144 char szAppName[] = "WinBoard";\r
145 char szConsoleName[] = "WBConsole";\r
146 \r
147 /* Title bar text */\r
148 char szTitle[] = "WinBoard";\r
149 char szConsoleTitle[] = "I C S Interaction";\r
150 \r
151 char *programName;\r
152 char *settingsFileName;\r
153 BOOLEAN saveSettingsOnExit;\r
154 char installDir[MSG_SIZ];\r
155 \r
156 BoardSize boardSize;\r
157 BOOLEAN chessProgram;\r
158 static int boardX, boardY;\r
159 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
160 static int squareSize, lineGap, minorSize;\r
161 static int winWidth, winHeight, winW, winH;\r
162 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
163 static int logoHeight = 0;\r
164 static char messageText[MESSAGE_TEXT_MAX];\r
165 static int clockTimerEvent = 0;\r
166 static int loadGameTimerEvent = 0;\r
167 static int analysisTimerEvent = 0;\r
168 static DelayedEventCallback delayedTimerCallback;\r
169 static int delayedTimerEvent = 0;\r
170 static int buttonCount = 2;\r
171 char *icsTextMenuString;\r
172 char *icsNames;\r
173 char *firstChessProgramNames;\r
174 char *secondChessProgramNames;\r
175 \r
176 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
177 \r
178 #define PALETTESIZE 256\r
179 \r
180 HINSTANCE hInst;          /* current instance */\r
181 HWND hwndMain = NULL;        /* root window*/\r
182 HWND hwndConsole = NULL;\r
183 BOOLEAN alwaysOnTop = FALSE;\r
184 RECT boardRect;\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
186   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
187 HPALETTE hPal;\r
188 ColorClass currentColorClass;\r
189 \r
190 HWND hCommPort = NULL;    /* currently open comm port */\r
191 static HWND hwndPause;    /* pause button */\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,\r
194   blackSquareBrush, /* [HGM] for band between board and holdings */\r
195   explodeBrush,     /* [HGM] atomic */\r
196   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
197 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
198 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
199 static HPEN gridPen = NULL;\r
200 static HPEN highlightPen = NULL;\r
201 static HPEN premovePen = NULL;\r
202 static NPLOGPALETTE pLogPal;\r
203 static BOOL paletteChanged = FALSE;\r
204 static HICON iconWhite, iconBlack, iconCurrent;\r
205 static int doingSizing = FALSE;\r
206 static int lastSizing = 0;\r
207 static int prevStderrPort;\r
208 static HBITMAP userLogo;\r
209 \r
210 /* [AS] Support for background textures */\r
211 #define BACK_TEXTURE_MODE_DISABLED      0\r
212 #define BACK_TEXTURE_MODE_PLAIN         1\r
213 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 #define oldDialog (_winmajor < 4)\r
226 #endif\r
227 \r
228 char *defaultTextAttribs[] = \r
229 {\r
230   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
231   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
232   COLOR_NONE\r
233 };\r
234 \r
235 typedef struct {\r
236   char *name;\r
237   int squareSize;\r
238   int lineGap;\r
239   int smallLayout;\r
240   int tinyLayout;\r
241   int cliWidth, cliHeight;\r
242 } SizeInfo;\r
243 \r
244 SizeInfo sizeInfo[] = \r
245 {\r
246   { "tiny",     21, 0, 1, 1, 0, 0 },\r
247   { "teeny",    25, 1, 1, 1, 0, 0 },\r
248   { "dinky",    29, 1, 1, 1, 0, 0 },\r
249   { "petite",   33, 1, 1, 1, 0, 0 },\r
250   { "slim",     37, 2, 1, 0, 0, 0 },\r
251   { "small",    40, 2, 1, 0, 0, 0 },\r
252   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
253   { "middling", 49, 2, 0, 0, 0, 0 },\r
254   { "average",  54, 2, 0, 0, 0, 0 },\r
255   { "moderate", 58, 3, 0, 0, 0, 0 },\r
256   { "medium",   64, 3, 0, 0, 0, 0 },\r
257   { "bulky",    72, 3, 0, 0, 0, 0 },\r
258   { "large",    80, 3, 0, 0, 0, 0 },\r
259   { "big",      87, 3, 0, 0, 0, 0 },\r
260   { "huge",     95, 3, 0, 0, 0, 0 },\r
261   { "giant",    108, 3, 0, 0, 0, 0 },\r
262   { "colossal", 116, 4, 0, 0, 0, 0 },\r
263   { "titanic",  129, 4, 0, 0, 0, 0 },\r
264   { NULL, 0, 0, 0, 0, 0, 0 }\r
265 };\r
266 \r
267 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
268 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
269 {\r
270   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
271   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
272   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
273   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
274   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
275   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
276   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
277   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
278   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
279   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
280   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
281   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
282   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
283   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
284   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
285   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
286   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
287   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
288 };\r
289 \r
290 MyFont *font[NUM_SIZES][NUM_FONTS];\r
291 \r
292 typedef struct {\r
293   char *label;\r
294   int id;\r
295   HWND hwnd;\r
296   WNDPROC wndproc;\r
297 } MyButtonDesc;\r
298 \r
299 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
300 #define N_BUTTONS 5\r
301 \r
302 MyButtonDesc buttonDesc[N_BUTTONS] =\r
303 {\r
304   {"<<", IDM_ToStart, NULL, NULL},\r
305   {"<", IDM_Backward, NULL, NULL},\r
306   {"P", IDM_Pause, NULL, NULL},\r
307   {">", IDM_Forward, NULL, NULL},\r
308   {">>", IDM_ToEnd, NULL, NULL},\r
309 };\r
310 \r
311 int tinyLayout = 0, smallLayout = 0;\r
312 #define MENU_BAR_ITEMS 6\r
313 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
314   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
315   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
316 };\r
317 \r
318 \r
319 MySound sounds[(int)NSoundClasses];\r
320 MyTextAttribs textAttribs[(int)NColorClasses];\r
321 \r
322 MyColorizeAttribs colorizeAttribs[] = {\r
323   { (COLORREF)0, 0, "Shout Text" },\r
324   { (COLORREF)0, 0, "SShout/CShout" },\r
325   { (COLORREF)0, 0, "Channel 1 Text" },\r
326   { (COLORREF)0, 0, "Channel Text" },\r
327   { (COLORREF)0, 0, "Kibitz Text" },\r
328   { (COLORREF)0, 0, "Tell Text" },\r
329   { (COLORREF)0, 0, "Challenge Text" },\r
330   { (COLORREF)0, 0, "Request Text" },\r
331   { (COLORREF)0, 0, "Seek Text" },\r
332   { (COLORREF)0, 0, "Normal Text" },\r
333   { (COLORREF)0, 0, "None" }\r
334 };\r
335 \r
336 \r
337 \r
338 static char *commentTitle;\r
339 static char *commentText;\r
340 static int commentIndex;\r
341 static Boolean editComment = FALSE;\r
342 HWND commentDialog = NULL;\r
343 BOOLEAN commentDialogUp = FALSE;\r
344 static int commentX, commentY, commentH, commentW;\r
345 \r
346 static char *analysisTitle;\r
347 static char *analysisText;\r
348 HWND analysisDialog = NULL;\r
349 BOOLEAN analysisDialogUp = FALSE;\r
350 static int analysisX, analysisY, analysisH, analysisW;\r
351 \r
352 char errorTitle[MSG_SIZ];\r
353 char errorMessage[2*MSG_SIZ];\r
354 HWND errorDialog = NULL;\r
355 BOOLEAN moveErrorMessageUp = FALSE;\r
356 BOOLEAN consoleEcho = TRUE;\r
357 CHARFORMAT consoleCF;\r
358 COLORREF consoleBackgroundColor;\r
359 \r
360 char *programVersion;\r
361 \r
362 #define CPReal 1\r
363 #define CPComm 2\r
364 #define CPSock 3\r
365 #define CPRcmd 4\r
366 typedef int CPKind;\r
367 \r
368 typedef struct {\r
369   CPKind kind;\r
370   HANDLE hProcess;\r
371   DWORD pid;\r
372   HANDLE hTo;\r
373   HANDLE hFrom;\r
374   SOCKET sock;\r
375   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
376 } ChildProc;\r
377 \r
378 #define INPUT_SOURCE_BUF_SIZE 4096\r
379 \r
380 typedef struct _InputSource {\r
381   CPKind kind;\r
382   HANDLE hFile;\r
383   SOCKET sock;\r
384   int lineByLine;\r
385   HANDLE hThread;\r
386   DWORD id;\r
387   char buf[INPUT_SOURCE_BUF_SIZE];\r
388   char *next;\r
389   DWORD count;\r
390   int error;\r
391   InputCallback func;\r
392   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
393   VOIDSTAR closure;\r
394 } InputSource;\r
395 \r
396 InputSource *consoleInputSource;\r
397 \r
398 DCB dcb;\r
399 \r
400 /* forward */\r
401 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
402 VOID ConsoleCreate();\r
403 LRESULT CALLBACK\r
404   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
405 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
406 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
407 VOID ParseCommSettings(char *arg, DCB *dcb);\r
408 LRESULT CALLBACK\r
409   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
410 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
411 void ParseIcsTextMenu(char *icsTextMenuString);\r
412 VOID PopUpMoveDialog(char firstchar);\r
413 VOID PopUpNameDialog(char firstchar);\r
414 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
415 \r
416 /* [AS] */\r
417 int NewGameFRC();\r
418 int GameListOptions();\r
419 \r
420 HWND moveHistoryDialog = NULL;\r
421 BOOLEAN moveHistoryDialogUp = FALSE;\r
422 \r
423 WindowPlacement wpMoveHistory;\r
424 \r
425 HWND evalGraphDialog = NULL;\r
426 BOOLEAN evalGraphDialogUp = FALSE;\r
427 \r
428 WindowPlacement wpEvalGraph;\r
429 \r
430 HWND engineOutputDialog = NULL;\r
431 BOOLEAN engineOutputDialogUp = FALSE;\r
432 \r
433 WindowPlacement wpEngineOutput;\r
434 WindowPlacement wpGameList;\r
435 WindowPlacement wpConsole;\r
436 \r
437 VOID MoveHistoryPopUp();\r
438 VOID MoveHistoryPopDown();\r
439 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
440 BOOL MoveHistoryIsUp();\r
441 \r
442 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
443 VOID EvalGraphPopUp();\r
444 VOID EvalGraphPopDown();\r
445 BOOL EvalGraphIsUp();\r
446 \r
447 VOID EngineOutputPopUp();\r
448 VOID EngineOutputPopDown();\r
449 BOOL EngineOutputIsUp();\r
450 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
451 \r
452 VOID GothicPopUp(char *title, VariantClass variant);\r
453 /*\r
454  * Setting "frozen" should disable all user input other than deleting\r
455  * the window.  We do this while engines are initializing themselves.\r
456  */\r
457 static int frozen = 0;\r
458 static int oldMenuItemState[MENU_BAR_ITEMS];\r
459 void FreezeUI()\r
460 {\r
461   HMENU hmenu;\r
462   int i;\r
463 \r
464   if (frozen) return;\r
465   frozen = 1;\r
466   hmenu = GetMenu(hwndMain);\r
467   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
468     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
469   }\r
470   DrawMenuBar(hwndMain);\r
471 }\r
472 \r
473 /* Undo a FreezeUI */\r
474 void ThawUI()\r
475 {\r
476   HMENU hmenu;\r
477   int i;\r
478 \r
479   if (!frozen) return;\r
480   frozen = 0;\r
481   hmenu = GetMenu(hwndMain);\r
482   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
483     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
484   }\r
485   DrawMenuBar(hwndMain);\r
486 }\r
487 \r
488 static int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
489 \r
490 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
491 #ifdef JAWS\r
492 #include "jaws.c"\r
493 #else\r
494 #define JAWS_INIT\r
495 #define JAWS_ALT_INTERCEPT\r
496 #define JAWS_KB_NAVIGATION\r
497 #define JAWS_MENU_ITEMS\r
498 #define JAWS_SILENCE\r
499 #define JAWS_REPLAY\r
500 #define JAWS_DELETE(X) X\r
501 #define SAYMACHINEMOVE()\r
502 #define SAY(X)\r
503 #endif\r
504 \r
505 /*---------------------------------------------------------------------------*\\r
506  *\r
507  * WinMain\r
508  *\r
509 \*---------------------------------------------------------------------------*/\r
510 \r
511 int APIENTRY\r
512 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
513         LPSTR lpCmdLine, int nCmdShow)\r
514 {\r
515   MSG msg;\r
516   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
517 //  INITCOMMONCONTROLSEX ex;\r
518 \r
519   debugFP = stderr;\r
520 \r
521   LoadLibrary("RICHED32.DLL");\r
522   consoleCF.cbSize = sizeof(CHARFORMAT);\r
523 \r
524   if (!InitApplication(hInstance)) {\r
525     return (FALSE);\r
526   }\r
527   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
528     return (FALSE);\r
529   }\r
530 \r
531   JAWS_INIT\r
532 \r
533 //  InitCommonControlsEx(&ex);\r
534   InitCommonControls();\r
535 \r
536   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
537   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
538   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
539 \r
540   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
541 \r
542   while (GetMessage(&msg, /* message structure */\r
543                     NULL, /* handle of window receiving the message */\r
544                     0,    /* lowest message to examine */\r
545                     0))   /* highest message to examine */\r
546     {\r
547 \r
548       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
549         // [HGM] navigate: switch between all windows with tab\r
550         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
551         int i, currentElement = 0;\r
552 \r
553         // first determine what element of the chain we come from (if any)\r
554         if(appData.icsActive) {\r
555             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
556             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
557         }\r
558         if(engineOutputDialog && EngineOutputIsUp()) {\r
559             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
560             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
561         }\r
562         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
563             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
564         }\r
565         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
566         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
567         if(msg.hwnd == e1)                 currentElement = 2; else\r
568         if(msg.hwnd == e2)                 currentElement = 3; else\r
569         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
570         if(msg.hwnd == mh)                currentElement = 4; else\r
571         if(msg.hwnd == evalGraphDialog)    currentElement = 7; else\r
572         if(msg.hwnd == hText)  currentElement = 5; else\r
573         if(msg.hwnd == hInput) currentElement = 6; else\r
574         for (i = 0; i < N_BUTTONS; i++) {\r
575             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
576         }\r
577 \r
578         // determine where to go to\r
579         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
580           do {\r
581             currentElement = (currentElement + direction) % 7;\r
582             switch(currentElement) {\r
583                 case 0:\r
584                   h = hwndMain; break; // passing this case always makes the loop exit\r
585                 case 1:\r
586                   h = buttonDesc[0].hwnd; break; // could be NULL\r
587                 case 2:\r
588                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
589                   h = e1; break;\r
590                 case 3:\r
591                   if(!EngineOutputIsUp()) continue;\r
592                   h = e2; break;\r
593                 case 4:\r
594                   if(!MoveHistoryIsUp()) continue;\r
595                   h = mh; break;\r
596 //              case 5: // input to eval graph does not seem to get here!\r
597 //                if(!EvalGraphIsUp()) continue;\r
598 //                h = evalGraphDialog; break;\r
599                 case 5:\r
600                   if(!appData.icsActive) continue;\r
601                   SAY("display");\r
602                   h = hText; break;\r
603                 case 6:\r
604                   if(!appData.icsActive) continue;\r
605                   SAY("input");\r
606                   h = hInput; break;\r
607             }\r
608           } while(h == 0);\r
609 \r
610           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
611           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
612           SetFocus(h);\r
613 \r
614           continue; // this message now has been processed\r
615         }\r
616       }\r
617 \r
618       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
619           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
620           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
621           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
622           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
623           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
624           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
625           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
626           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
627           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
628         TranslateMessage(&msg); /* Translates virtual key codes */\r
629         DispatchMessage(&msg);  /* Dispatches message to window */\r
630       }\r
631     }\r
632 \r
633 \r
634   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
635 }\r
636 \r
637 /*---------------------------------------------------------------------------*\\r
638  *\r
639  * Initialization functions\r
640  *\r
641 \*---------------------------------------------------------------------------*/\r
642 \r
643 void\r
644 SetUserLogo()\r
645 {   // update user logo if necessary\r
646     static char oldUserName[MSG_SIZ], *curName;\r
647 \r
648     if(appData.autoLogo) {\r
649           curName = UserName();\r
650           if(strcmp(curName, oldUserName)) {\r
651                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
652                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
653                 strcpy(oldUserName, curName);\r
654           }\r
655     }\r
656 }\r
657 \r
658 BOOL\r
659 InitApplication(HINSTANCE hInstance)\r
660 {\r
661   WNDCLASS wc;\r
662 \r
663   /* Fill in window class structure with parameters that describe the */\r
664   /* main window. */\r
665 \r
666   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
667   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
668   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
669   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
670   wc.hInstance     = hInstance;         /* Owner of this class */\r
671   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
672   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
673   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
674   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
675   wc.lpszClassName = szAppName;                 /* Name to register as */\r
676 \r
677   /* Register the window class and return success/failure code. */\r
678   if (!RegisterClass(&wc)) return FALSE;\r
679 \r
680   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
681   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
682   wc.cbClsExtra    = 0;\r
683   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
684   wc.hInstance     = hInstance;\r
685   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
686   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
687   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
688   wc.lpszMenuName  = NULL;\r
689   wc.lpszClassName = szConsoleName;\r
690 \r
691   if (!RegisterClass(&wc)) return FALSE;\r
692   return TRUE;\r
693 }\r
694 \r
695 \r
696 /* Set by InitInstance, used by EnsureOnScreen */\r
697 int screenHeight, screenWidth;\r
698 \r
699 void\r
700 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
701 {\r
702 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
703   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
704   if (*x > screenWidth - 32) *x = 0;\r
705   if (*y > screenHeight - 32) *y = 0;\r
706   if (*x < minX) *x = minX;\r
707   if (*y < minY) *y = minY;\r
708 }\r
709 \r
710 BOOL\r
711 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
712 {\r
713   HWND hwnd; /* Main window handle. */\r
714   int ibs;\r
715   WINDOWPLACEMENT wp;\r
716   char *filepart;\r
717 \r
718   hInst = hInstance;    /* Store instance handle in our global variable */\r
719 \r
720   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
721     *filepart = NULLCHAR;\r
722   } else {\r
723     GetCurrentDirectory(MSG_SIZ, installDir);\r
724   }\r
725   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
726   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
727   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
728   if (appData.debugMode) {\r
729     debugFP = fopen(appData.nameOfDebugFile, "w");\r
730     setbuf(debugFP, NULL);\r
731   }\r
732 \r
733   InitBackEnd1();\r
734 \r
735 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
736 //  InitEngineUCI( installDir, &second );\r
737 \r
738   /* Create a main window for this application instance. */\r
739   hwnd = CreateWindow(szAppName, szTitle,\r
740                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
741                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
742                       NULL, NULL, hInstance, NULL);\r
743   hwndMain = hwnd;\r
744 \r
745   /* If window could not be created, return "failure" */\r
746   if (!hwnd) {\r
747     return (FALSE);\r
748   }\r
749 \r
750   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
751   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
752       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
753 \r
754       if (first.programLogo == NULL && appData.debugMode) {\r
755           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
756       }\r
757   } else if(appData.autoLogo) {\r
758       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
759         char buf[MSG_SIZ];\r
760         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
761         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
762       }\r
763   }\r
764 \r
765   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
766       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
767 \r
768       if (second.programLogo == NULL && appData.debugMode) {\r
769           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
770       }\r
771   } else if(appData.autoLogo) {\r
772       char buf[MSG_SIZ];\r
773       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
774         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
775         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
776       } else\r
777       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
778         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
779         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
780       }\r
781   }\r
782 \r
783   SetUserLogo();\r
784 \r
785   iconWhite = LoadIcon(hInstance, "icon_white");\r
786   iconBlack = LoadIcon(hInstance, "icon_black");\r
787   iconCurrent = iconWhite;\r
788   InitDrawingColors();\r
789   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
790   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
791   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
792     /* Compute window size for each board size, and use the largest\r
793        size that fits on this screen as the default. */\r
794     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
795     if (boardSize == (BoardSize)-1 &&\r
796         winH <= screenHeight\r
797            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
798         && winW <= screenWidth) {\r
799       boardSize = (BoardSize)ibs;\r
800     }\r
801   }\r
802 \r
803   InitDrawingSizes(boardSize, 0);\r
804   InitMenuChecks();\r
805   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
806 \r
807   /* [AS] Load textures if specified */\r
808   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
809   \r
810   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
811       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
812       liteBackTextureMode = appData.liteBackTextureMode;\r
813 \r
814       if (liteBackTexture == NULL && appData.debugMode) {\r
815           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
816       }\r
817   }\r
818   \r
819   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
820       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
821       darkBackTextureMode = appData.darkBackTextureMode;\r
822 \r
823       if (darkBackTexture == NULL && appData.debugMode) {\r
824           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
825       }\r
826   }\r
827 \r
828   mysrandom( (unsigned) time(NULL) );\r
829 \r
830   /* [AS] Restore layout */\r
831   if( wpMoveHistory.visible ) {\r
832       MoveHistoryPopUp();\r
833   }\r
834 \r
835   if( wpEvalGraph.visible ) {\r
836       EvalGraphPopUp();\r
837   }\r
838 \r
839   if( wpEngineOutput.visible ) {\r
840       EngineOutputPopUp();\r
841   }\r
842 \r
843   InitBackEnd2();\r
844 \r
845   /* Make the window visible; update its client area; and return "success" */\r
846   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
847   wp.length = sizeof(WINDOWPLACEMENT);\r
848   wp.flags = 0;\r
849   wp.showCmd = nCmdShow;\r
850   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
851   wp.rcNormalPosition.left = boardX;\r
852   wp.rcNormalPosition.right = boardX + winWidth;\r
853   wp.rcNormalPosition.top = boardY;\r
854   wp.rcNormalPosition.bottom = boardY + winHeight;\r
855   SetWindowPlacement(hwndMain, &wp);\r
856 \r
857   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
858                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
859 \r
860   if (hwndConsole) {\r
861 #if AOT_CONSOLE\r
862     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
863                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
864 #endif\r
865     ShowWindow(hwndConsole, nCmdShow);\r
866   }\r
867   UpdateWindow(hwnd);\r
868 \r
869   return TRUE;\r
870 \r
871 }\r
872 \r
873 \r
874 typedef enum {\r
875   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
876   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
877   ArgSettingsFilename,\r
878   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
879 } ArgType;\r
880 \r
881 typedef struct {\r
882   char *argName;\r
883   ArgType argType;\r
884   /***\r
885   union {\r
886     String *pString;       // ArgString\r
887     int *pInt;             // ArgInt\r
888     float *pFloat;         // ArgFloat\r
889     Boolean *pBoolean;     // ArgBoolean\r
890     COLORREF *pColor;      // ArgColor\r
891     ColorClass cc;         // ArgAttribs\r
892     String *pFilename;     // ArgFilename\r
893     BoardSize *pBoardSize; // ArgBoardSize\r
894     int whichFont;         // ArgFont\r
895     DCB *pDCB;             // ArgCommSettings\r
896     String *pFilename;     // ArgSettingsFilename\r
897   } argLoc;\r
898   ***/\r
899   LPVOID argLoc;\r
900   BOOL save;\r
901 } ArgDescriptor;\r
902 \r
903 int junk;\r
904 ArgDescriptor argDescriptors[] = {\r
905   /* positional arguments */\r
906   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
907   { "", ArgNone, NULL },\r
908   /* keyword arguments */\r
909   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
910   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
911   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
912   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
913   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
914   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
915   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
916   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
917   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
918   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
919   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
920   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
921   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
922   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
923   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
924   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
925   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
926   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
927     FALSE },\r
928   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
929     FALSE },\r
930   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
931     FALSE },\r
932   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
933   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
934     FALSE },\r
935   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
936   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
937   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
938   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
939   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
940   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
941   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
942   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
943   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
944   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
945   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
946   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
947   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
948   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
949   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
950   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
951   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
952   /*!!bitmapDirectory?*/\r
953   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
954   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
955   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
956   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
957   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
958   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
959   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
960   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
961   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
962   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
963   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
964   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
965   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
966   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
967   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
968   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
969   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
970   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
971   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
972   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
973   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
974   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
975   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
976   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
977   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
978   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
979   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
980   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
981   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
982   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
983   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
984   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
985   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
986   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
987   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
988   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
989   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
990   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
991   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
992   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
993   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
994   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
995   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
996   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
997   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
998   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
999   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1000   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
1001   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1002   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1003   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1004   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1005   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1006   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1007   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1008   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1009   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1010   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1011   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1012   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1013   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1014   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1015   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1016   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1017   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1018   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1019   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1020   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1021   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1022   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1023   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1024   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1025   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1026   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1027   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1028   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1029   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1030   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1031   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1032   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1033   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1034   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1035   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1036   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1037   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1038   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1039   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1040   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1041   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1042   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1043   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1044   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1045   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1046   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1047     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1048   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1049   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1050   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1051   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1052   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1053   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1054   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1055   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1056     TRUE }, /* must come after all fonts */\r
1057   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1058   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1059     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1060   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1061   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1062   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1063   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1064   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1065   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1066   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1067   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1068   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1069   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1070   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1071   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1072   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1073   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1074   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1075   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1076   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1077   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1078   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1079   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1080   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1081   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1082   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1083   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1084   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1085   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1086   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1087   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1088 #if 0\r
1089   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1090   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1091 #endif\r
1092   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1093   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1094   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1095   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1096   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1097   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1098   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1099   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1100   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1101   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1102   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1103   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1104   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1105   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1106   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1107   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1108   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1109   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1110   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1111   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1112   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1113   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1114   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1115   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1116   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1117   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1118   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1119   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1120   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1121   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1122   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1123   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1124   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1125   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1126   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1127   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1128   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1129   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1130   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1131   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1132   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1133   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1134   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1135   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1136   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1137   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1138   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1139   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1140   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1141   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1142   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1143   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1144   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1145   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1146   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1147   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1148   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1149   { "highlightLastMove", ArgBoolean,\r
1150     (LPVOID) &appData.highlightLastMove, TRUE },\r
1151   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1152   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1153   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1154   { "highlightDragging", ArgBoolean,\r
1155     (LPVOID) &appData.highlightDragging, TRUE },\r
1156   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1157   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1158   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1159   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1160   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1161   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1162   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1163   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1164   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1165   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1166   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1167   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1168   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1169   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1170   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1171   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1172   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1173   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1174   { "soundShout", ArgFilename,\r
1175     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1176   { "soundSShout", ArgFilename,\r
1177     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1178   { "soundChannel1", ArgFilename,\r
1179     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1180   { "soundChannel", ArgFilename,\r
1181     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1182   { "soundKibitz", ArgFilename,\r
1183     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1184   { "soundTell", ArgFilename,\r
1185     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1186   { "soundChallenge", ArgFilename,\r
1187     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1188   { "soundRequest", ArgFilename,\r
1189     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1190   { "soundSeek", ArgFilename,\r
1191     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1192   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1193   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1194   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1195   { "soundIcsLoss", ArgFilename, \r
1196     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1197   { "soundIcsDraw", ArgFilename, \r
1198     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1199   { "soundIcsUnfinished", ArgFilename, \r
1200     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1201   { "soundIcsAlarm", ArgFilename, \r
1202     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1203   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1204   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1205   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1206   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1207   { "reuseChessPrograms", ArgBoolean,\r
1208     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1209   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1210   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1211   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1212   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1213   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1214   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1215   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1216   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1217   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1218   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1219   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1220   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1221   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1222   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1223   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1224     TRUE },\r
1225   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1226     TRUE },\r
1227   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1228   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1229   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1230   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1231   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1232   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1233   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1234   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1235   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1236   /* [AS] New features */\r
1237   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1238   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1239   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1240   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1241   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1242   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1243   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1244   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1245   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1246   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1247   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1248   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1249   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1250   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1251   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1252   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1253   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1254   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1255   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1256   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1257   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1258   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1259   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1260   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1261   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1262   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1263   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1264   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1265   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1266   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1267   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1268   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1269   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1270   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1271   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1272   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1273   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1274   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1275   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1276   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1277   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1278   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1279   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1280   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1281   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1282   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1283   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1284   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1285   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1286   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1287 \r
1288   /* [HGM] board-size, adjudication and misc. options */\r
1289   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1290   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1291   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1292   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1293   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1294   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1295   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1296   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1297   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1298   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1299   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1300   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1301   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1302   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1303   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1304   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1305   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1306   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1307   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1308   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1309   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1310   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1311   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1312   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1313   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1314   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1315   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1316   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1317   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1318   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1319   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1320 \r
1321 #ifdef ZIPPY\r
1322   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1323   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1324   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1325   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1326   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1327   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1328   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1329   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1330   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1331   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1332   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1333   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1334   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1335     FALSE },\r
1336   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1337   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1338   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1339   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1340   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1341   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1342   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1343     FALSE },\r
1344   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1345   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1346   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1347   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1348   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1349   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1350   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1351   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1352   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1353   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1354   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1355   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1356   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1357   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1358   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1359   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1360   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1361   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1362   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1363 #endif\r
1364   /* [HGM] options for broadcasting and time odds */\r
1365   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1366   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1367   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1368   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1369   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1370   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1371   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1372   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1373   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1374   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1375   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1376 \r
1377   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1378   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1379   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1380   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1381   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1382   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1383   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1384   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1385   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1386   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1387   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1388   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1389   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1390   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1391   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1392   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1393   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1394   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1395   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1396   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1397   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1398   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1399   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1400   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1401   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1402   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1403   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1404   /* [AS] Layout stuff */\r
1405   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1406   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1407   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1408   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1409   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1410 \r
1411   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1412   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1413   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1414   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1415   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1416 \r
1417   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1418   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1419   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1420   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1421   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1422 \r
1423   { NULL, ArgNone, NULL, FALSE }\r
1424 };\r
1425 \r
1426 \r
1427 /* Kludge for indirection files on command line */\r
1428 char* lastIndirectionFilename;\r
1429 ArgDescriptor argDescriptorIndirection =\r
1430 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1431 \r
1432 \r
1433 VOID\r
1434 ExitArgError(char *msg, char *badArg)\r
1435 {\r
1436   char buf[MSG_SIZ];\r
1437 \r
1438   sprintf(buf, "%s %s", msg, badArg);\r
1439   DisplayFatalError(buf, 0, 2);\r
1440   exit(2);\r
1441 }\r
1442 \r
1443 /* Command line font name parser.  NULL name means do nothing.\r
1444    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1445    For backward compatibility, syntax without the colon is also\r
1446    accepted, but font names with digits in them won't work in that case.\r
1447 */\r
1448 VOID\r
1449 ParseFontName(char *name, MyFontParams *mfp)\r
1450 {\r
1451   char *p, *q;\r
1452   if (name == NULL) return;\r
1453   p = name;\r
1454   q = strchr(p, ':');\r
1455   if (q) {\r
1456     if (q - p >= sizeof(mfp->faceName))\r
1457       ExitArgError("Font name too long:", name);\r
1458     memcpy(mfp->faceName, p, q - p);\r
1459     mfp->faceName[q - p] = NULLCHAR;\r
1460     p = q + 1;\r
1461   } else {\r
1462     q = mfp->faceName;\r
1463     while (*p && !isdigit(*p)) {\r
1464       *q++ = *p++;\r
1465       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1466         ExitArgError("Font name too long:", name);\r
1467     }\r
1468     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1469     *q = NULLCHAR;\r
1470   }\r
1471   if (!*p) ExitArgError("Font point size missing:", name);\r
1472   mfp->pointSize = (float) atof(p);\r
1473   mfp->bold = (strchr(p, 'b') != NULL);\r
1474   mfp->italic = (strchr(p, 'i') != NULL);\r
1475   mfp->underline = (strchr(p, 'u') != NULL);\r
1476   mfp->strikeout = (strchr(p, 's') != NULL);\r
1477 }\r
1478 \r
1479 /* Color name parser.\r
1480    X version accepts X color names, but this one\r
1481    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1482 COLORREF\r
1483 ParseColorName(char *name)\r
1484 {\r
1485   int red, green, blue, count;\r
1486   char buf[MSG_SIZ];\r
1487 \r
1488   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1489   if (count != 3) {\r
1490     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1491       &red, &green, &blue);\r
1492   }\r
1493   if (count != 3) {\r
1494     sprintf(buf, "Can't parse color name %s", name);\r
1495     DisplayError(buf, 0);\r
1496     return RGB(0, 0, 0);\r
1497   }\r
1498   return PALETTERGB(red, green, blue);\r
1499 }\r
1500 \r
1501 \r
1502 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1503 {\r
1504   char *e = argValue;\r
1505   int eff = 0;\r
1506 \r
1507   while (*e) {\r
1508     if (*e == 'b')      eff |= CFE_BOLD;\r
1509     else if (*e == 'i') eff |= CFE_ITALIC;\r
1510     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1511     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1512     else if (*e == '#' || isdigit(*e)) break;\r
1513     e++;\r
1514   }\r
1515   *effects = eff;\r
1516   *color   = ParseColorName(e);\r
1517 }\r
1518 \r
1519 \r
1520 BoardSize\r
1521 ParseBoardSize(char *name)\r
1522 {\r
1523   BoardSize bs = SizeTiny;\r
1524   while (sizeInfo[bs].name != NULL) {\r
1525     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1526     bs++;\r
1527   }\r
1528   ExitArgError("Unrecognized board size value", name);\r
1529   return bs; /* not reached */\r
1530 }\r
1531 \r
1532 \r
1533 char\r
1534 StringGet(void *getClosure)\r
1535 {\r
1536   char **p = (char **) getClosure;\r
1537   return *((*p)++);\r
1538 }\r
1539 \r
1540 char\r
1541 FileGet(void *getClosure)\r
1542 {\r
1543   int c;\r
1544   FILE* f = (FILE*) getClosure;\r
1545 \r
1546   c = getc(f);\r
1547   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1548   if (c == EOF)\r
1549     return NULLCHAR;\r
1550   else\r
1551     return (char) c;\r
1552 }\r
1553 \r
1554 /* Parse settings file named "name". If file found, return the\r
1555    full name in fullname and return TRUE; else return FALSE */\r
1556 BOOLEAN\r
1557 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1558 {\r
1559   char *dummy;\r
1560   FILE *f;\r
1561   int ok; char buf[MSG_SIZ];\r
1562 \r
1563   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1564   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1565     sprintf(buf, "%s.ini", name);\r
1566     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1567   }\r
1568   if (ok) {\r
1569     f = fopen(fullname, "r");\r
1570     if (f != NULL) {\r
1571       ParseArgs(FileGet, f);\r
1572       fclose(f);\r
1573       return TRUE;\r
1574     }\r
1575   }\r
1576   return FALSE;\r
1577 }\r
1578 \r
1579 VOID\r
1580 ParseArgs(GetFunc get, void *cl)\r
1581 {\r
1582   char argName[ARG_MAX];\r
1583   char argValue[ARG_MAX];\r
1584   ArgDescriptor *ad;\r
1585   char start;\r
1586   char *q;\r
1587   int i, octval;\r
1588   char ch;\r
1589   int posarg = 0;\r
1590 \r
1591   ch = get(cl);\r
1592   for (;;) {\r
1593     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1594     if (ch == NULLCHAR) break;\r
1595     if (ch == ';') {\r
1596       /* Comment to end of line */\r
1597       ch = get(cl);\r
1598       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1599       continue;\r
1600     } else if (ch == '/' || ch == '-') {\r
1601       /* Switch */\r
1602       q = argName;\r
1603       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1604              ch != '\n' && ch != '\t') {\r
1605         *q++ = ch;\r
1606         ch = get(cl);\r
1607       }\r
1608       *q = NULLCHAR;\r
1609 \r
1610       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1611         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1612 \r
1613       if (ad->argName == NULL)\r
1614         ExitArgError("Unrecognized argument", argName);\r
1615 \r
1616     } else if (ch == '@') {\r
1617       /* Indirection file */\r
1618       ad = &argDescriptorIndirection;\r
1619       ch = get(cl);\r
1620     } else {\r
1621       /* Positional argument */\r
1622       ad = &argDescriptors[posarg++];\r
1623       strcpy(argName, ad->argName);\r
1624     }\r
1625 \r
1626     if (ad->argType == ArgTrue) {\r
1627       *(Boolean *) ad->argLoc = TRUE;\r
1628       continue;\r
1629     }\r
1630     if (ad->argType == ArgFalse) {\r
1631       *(Boolean *) ad->argLoc = FALSE;\r
1632       continue;\r
1633     }\r
1634 \r
1635     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1636     if (ch == NULLCHAR || ch == '\n') {\r
1637       ExitArgError("No value provided for argument", argName);\r
1638     }\r
1639     q = argValue;\r
1640     if (ch == '{') {\r
1641       // Quoting with { }.  No characters have to (or can) be escaped.\r
1642       // Thus the string cannot contain a '}' character.\r
1643       start = ch;\r
1644       ch = get(cl);\r
1645       while (start) {\r
1646         switch (ch) {\r
1647         case NULLCHAR:\r
1648           start = NULLCHAR;\r
1649           break;\r
1650           \r
1651         case '}':\r
1652           ch = get(cl);\r
1653           start = NULLCHAR;\r
1654           break;\r
1655 \r
1656         default:\r
1657           *q++ = ch;\r
1658           ch = get(cl);\r
1659           break;\r
1660         }\r
1661       }   \r
1662     } else if (ch == '\'' || ch == '"') {\r
1663       // Quoting with ' ' or " ", with \ as escape character.\r
1664       // Inconvenient for long strings that may contain Windows filenames.\r
1665       start = ch;\r
1666       ch = get(cl);\r
1667       while (start) {\r
1668         switch (ch) {\r
1669         case NULLCHAR:\r
1670           start = NULLCHAR;\r
1671           break;\r
1672 \r
1673         default:\r
1674         not_special:\r
1675           *q++ = ch;\r
1676           ch = get(cl);\r
1677           break;\r
1678 \r
1679         case '\'':\r
1680         case '\"':\r
1681           if (ch == start) {\r
1682             ch = get(cl);\r
1683             start = NULLCHAR;\r
1684             break;\r
1685           } else {\r
1686             goto not_special;\r
1687           }\r
1688 \r
1689         case '\\':\r
1690           if (ad->argType == ArgFilename\r
1691               || ad->argType == ArgSettingsFilename) {\r
1692               goto not_special;\r
1693           }\r
1694           ch = get(cl);\r
1695           switch (ch) {\r
1696           case NULLCHAR:\r
1697             ExitArgError("Incomplete \\ escape in value for", argName);\r
1698             break;\r
1699           case 'n':\r
1700             *q++ = '\n';\r
1701             ch = get(cl);\r
1702             break;\r
1703           case 'r':\r
1704             *q++ = '\r';\r
1705             ch = get(cl);\r
1706             break;\r
1707           case 't':\r
1708             *q++ = '\t';\r
1709             ch = get(cl);\r
1710             break;\r
1711           case 'b':\r
1712             *q++ = '\b';\r
1713             ch = get(cl);\r
1714             break;\r
1715           case 'f':\r
1716             *q++ = '\f';\r
1717             ch = get(cl);\r
1718             break;\r
1719           default:\r
1720             octval = 0;\r
1721             for (i = 0; i < 3; i++) {\r
1722               if (ch >= '0' && ch <= '7') {\r
1723                 octval = octval*8 + (ch - '0');\r
1724                 ch = get(cl);\r
1725               } else {\r
1726                 break;\r
1727               }\r
1728             }\r
1729             if (i > 0) {\r
1730               *q++ = (char) octval;\r
1731             } else {\r
1732               *q++ = ch;\r
1733               ch = get(cl);\r
1734             }\r
1735             break;\r
1736           }\r
1737           break;\r
1738         }\r
1739       }\r
1740     } else {\r
1741       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1742         *q++ = ch;\r
1743         ch = get(cl);\r
1744       }\r
1745     }\r
1746     *q = NULLCHAR;\r
1747 \r
1748     switch (ad->argType) {\r
1749     case ArgInt:\r
1750       *(int *) ad->argLoc = atoi(argValue);\r
1751       break;\r
1752 \r
1753     case ArgX:\r
1754       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1755       break;\r
1756 \r
1757     case ArgY:\r
1758       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1759       break;\r
1760 \r
1761     case ArgZ:\r
1762       *(int *) ad->argLoc = atoi(argValue);\r
1763       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1764       break;\r
1765 \r
1766     case ArgFloat:\r
1767       *(float *) ad->argLoc = (float) atof(argValue);\r
1768       break;\r
1769 \r
1770     case ArgString:\r
1771     case ArgFilename:\r
1772       *(char **) ad->argLoc = strdup(argValue);\r
1773       break;\r
1774 \r
1775     case ArgSettingsFilename:\r
1776       {\r
1777         char fullname[MSG_SIZ];\r
1778         if (ParseSettingsFile(argValue, fullname)) {\r
1779           if (ad->argLoc != NULL) {\r
1780             *(char **) ad->argLoc = strdup(fullname);\r
1781           }\r
1782         } else {\r
1783           if (ad->argLoc != NULL) {\r
1784           } else {\r
1785             ExitArgError("Failed to open indirection file", argValue);\r
1786           }\r
1787         }\r
1788       }\r
1789       break;\r
1790 \r
1791     case ArgBoolean:\r
1792       switch (argValue[0]) {\r
1793       case 't':\r
1794       case 'T':\r
1795         *(Boolean *) ad->argLoc = TRUE;\r
1796         break;\r
1797       case 'f':\r
1798       case 'F':\r
1799         *(Boolean *) ad->argLoc = FALSE;\r
1800         break;\r
1801       default:\r
1802         ExitArgError("Unrecognized boolean argument value", argValue);\r
1803         break;\r
1804       }\r
1805       break;\r
1806 \r
1807     case ArgColor:\r
1808       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1809       break;\r
1810 \r
1811     case ArgAttribs: {\r
1812       ColorClass cc = (ColorClass)ad->argLoc;\r
1813       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1814       }\r
1815       break;\r
1816       \r
1817     case ArgBoardSize:\r
1818       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1819       break;\r
1820 \r
1821     case ArgFont:\r
1822       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1823       break;\r
1824 \r
1825     case ArgCommSettings:\r
1826       ParseCommSettings(argValue, &dcb);\r
1827       break;\r
1828 \r
1829     case ArgNone:\r
1830       ExitArgError("Unrecognized argument", argValue);\r
1831       break;\r
1832     case ArgTrue:\r
1833     case ArgFalse: ;\r
1834     }\r
1835   }\r
1836 }\r
1837 \r
1838 VOID\r
1839 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1840 {\r
1841   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1842   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1843   DeleteDC(hdc);\r
1844   lf->lfWidth = 0;\r
1845   lf->lfEscapement = 0;\r
1846   lf->lfOrientation = 0;\r
1847   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1848   lf->lfItalic = mfp->italic;\r
1849   lf->lfUnderline = mfp->underline;\r
1850   lf->lfStrikeOut = mfp->strikeout;\r
1851   lf->lfCharSet = DEFAULT_CHARSET;\r
1852   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1853   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1854   lf->lfQuality = DEFAULT_QUALITY;\r
1855   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1856   strcpy(lf->lfFaceName, mfp->faceName);\r
1857 }\r
1858 \r
1859 VOID\r
1860 CreateFontInMF(MyFont *mf)\r
1861 {\r
1862   LFfromMFP(&mf->lf, &mf->mfp);\r
1863   if (mf->hf) DeleteObject(mf->hf);\r
1864   mf->hf = CreateFontIndirect(&mf->lf);\r
1865 }\r
1866 \r
1867 VOID\r
1868 SetDefaultTextAttribs()\r
1869 {\r
1870   ColorClass cc;\r
1871   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1872     ParseAttribs(&textAttribs[cc].color, \r
1873                  &textAttribs[cc].effects, \r
1874                  defaultTextAttribs[cc]);\r
1875   }\r
1876 }\r
1877 \r
1878 VOID\r
1879 SetDefaultSounds()\r
1880 {\r
1881   ColorClass cc;\r
1882   SoundClass sc;\r
1883   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1884     textAttribs[cc].sound.name = strdup("");\r
1885     textAttribs[cc].sound.data = NULL;\r
1886   }\r
1887   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1888     sounds[sc].name = strdup("");\r
1889     sounds[sc].data = NULL;\r
1890   }\r
1891   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1892 }\r
1893 \r
1894 VOID\r
1895 LoadAllSounds()\r
1896 {\r
1897   ColorClass cc;\r
1898   SoundClass sc;\r
1899   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1900     MyLoadSound(&textAttribs[cc].sound);\r
1901   }\r
1902   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1903     MyLoadSound(&sounds[sc]);\r
1904   }\r
1905 }\r
1906 \r
1907 VOID\r
1908 InitAppData(LPSTR lpCmdLine)\r
1909 {\r
1910   int i, j;\r
1911   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1912   char *dummy, *p;\r
1913 \r
1914   programName = szAppName;\r
1915 \r
1916   /* Initialize to defaults */\r
1917   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1918   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1919   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1920   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1921   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1922   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1923   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1924   SetDefaultTextAttribs();\r
1925   SetDefaultSounds();\r
1926   appData.movesPerSession = MOVES_PER_SESSION;\r
1927   appData.initString = INIT_STRING;\r
1928   appData.secondInitString = INIT_STRING;\r
1929   appData.firstComputerString = COMPUTER_STRING;\r
1930   appData.secondComputerString = COMPUTER_STRING;\r
1931   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1932   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1933   appData.firstPlaysBlack = FALSE;\r
1934   appData.noChessProgram = FALSE;\r
1935   chessProgram = FALSE;\r
1936   appData.firstHost = FIRST_HOST;\r
1937   appData.secondHost = SECOND_HOST;\r
1938   appData.firstDirectory = FIRST_DIRECTORY;\r
1939   appData.secondDirectory = SECOND_DIRECTORY;\r
1940   appData.bitmapDirectory = "";\r
1941   appData.remoteShell = REMOTE_SHELL;\r
1942   appData.remoteUser = "";\r
1943   appData.timeDelay = TIME_DELAY;\r
1944   appData.timeControl = TIME_CONTROL;\r
1945   appData.timeIncrement = TIME_INCREMENT;\r
1946   appData.icsActive = FALSE;\r
1947   appData.icsHost = "";\r
1948   appData.icsPort = ICS_PORT;\r
1949   appData.icsCommPort = ICS_COMM_PORT;\r
1950   appData.icsLogon = ICS_LOGON;\r
1951   appData.icsHelper = "";\r
1952   appData.useTelnet = FALSE;\r
1953   appData.telnetProgram = TELNET_PROGRAM;\r
1954   appData.gateway = "";\r
1955   appData.loadGameFile = "";\r
1956   appData.loadGameIndex = 0;\r
1957   appData.saveGameFile = "";\r
1958   appData.autoSaveGames = FALSE;\r
1959   appData.loadPositionFile = "";\r
1960   appData.loadPositionIndex = 1;\r
1961   appData.savePositionFile = "";\r
1962   appData.matchMode = FALSE;\r
1963   appData.matchGames = 0;\r
1964   appData.monoMode = FALSE;\r
1965   appData.debugMode = FALSE;\r
1966   appData.clockMode = TRUE;\r
1967   boardSize = (BoardSize) -1; /* determine by screen size */\r
1968   appData.Iconic = FALSE; /*unused*/\r
1969   appData.searchTime = "";\r
1970   appData.searchDepth = 0;\r
1971   appData.showCoords = FALSE;\r
1972   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1973   appData.autoCallFlag = FALSE;\r
1974   appData.flipView = FALSE;\r
1975   appData.autoFlipView = TRUE;\r
1976   appData.cmailGameName = "";\r
1977   appData.alwaysPromoteToQueen = FALSE;\r
1978   appData.oldSaveStyle = FALSE;\r
1979   appData.quietPlay = FALSE;\r
1980   appData.showThinking = FALSE;\r
1981   appData.ponderNextMove = TRUE;\r
1982   appData.periodicUpdates = TRUE;\r
1983   appData.popupExitMessage = TRUE;\r
1984   appData.popupMoveErrors = FALSE;\r
1985   appData.autoObserve = FALSE;\r
1986   appData.autoComment = FALSE;\r
1987   appData.animate = TRUE;\r
1988   appData.animSpeed = 10;\r
1989   appData.animateDragging = TRUE;\r
1990   appData.highlightLastMove = TRUE;\r
1991   appData.getMoveList = TRUE;\r
1992   appData.testLegality = TRUE;\r
1993   appData.premove = TRUE;\r
1994   appData.premoveWhite = FALSE;\r
1995   appData.premoveWhiteText = "";\r
1996   appData.premoveBlack = FALSE;\r
1997   appData.premoveBlackText = "";\r
1998   appData.icsAlarm = TRUE;\r
1999   appData.icsAlarmTime = 5000;\r
2000   appData.autoRaiseBoard = TRUE;\r
2001   appData.localLineEditing = TRUE;\r
2002   appData.colorize = TRUE;\r
2003   appData.reuseFirst = TRUE;\r
2004   appData.reuseSecond = TRUE;\r
2005   appData.blindfold = FALSE;\r
2006   appData.icsEngineAnalyze = FALSE;\r
2007   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2008   dcb.DCBlength = sizeof(DCB);\r
2009   dcb.BaudRate = 9600;\r
2010   dcb.fBinary = TRUE;\r
2011   dcb.fParity = FALSE;\r
2012   dcb.fOutxCtsFlow = FALSE;\r
2013   dcb.fOutxDsrFlow = FALSE;\r
2014   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2015   dcb.fDsrSensitivity = FALSE;\r
2016   dcb.fTXContinueOnXoff = TRUE;\r
2017   dcb.fOutX = FALSE;\r
2018   dcb.fInX = FALSE;\r
2019   dcb.fNull = FALSE;\r
2020   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2021   dcb.fAbortOnError = FALSE;\r
2022   dcb.ByteSize = 7;\r
2023   dcb.Parity = SPACEPARITY;\r
2024   dcb.StopBits = ONESTOPBIT;\r
2025   settingsFileName = SETTINGS_FILE;\r
2026   saveSettingsOnExit = TRUE;\r
2027   boardX = CW_USEDEFAULT;\r
2028   boardY = CW_USEDEFAULT;\r
2029   analysisX = CW_USEDEFAULT; \r
2030   analysisY = CW_USEDEFAULT; \r
2031   analysisW = CW_USEDEFAULT;\r
2032   analysisH = CW_USEDEFAULT;\r
2033   commentX = CW_USEDEFAULT; \r
2034   commentY = CW_USEDEFAULT; \r
2035   commentW = CW_USEDEFAULT;\r
2036   commentH = CW_USEDEFAULT;\r
2037   editTagsX = CW_USEDEFAULT; \r
2038   editTagsY = CW_USEDEFAULT; \r
2039   editTagsW = CW_USEDEFAULT;\r
2040   editTagsH = CW_USEDEFAULT;\r
2041   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2042   icsNames = ICS_NAMES;\r
2043   firstChessProgramNames = FCP_NAMES;\r
2044   secondChessProgramNames = SCP_NAMES;\r
2045   appData.initialMode = "";\r
2046   appData.variant = "normal";\r
2047   appData.firstProtocolVersion = PROTOVER;\r
2048   appData.secondProtocolVersion = PROTOVER;\r
2049   appData.showButtonBar = TRUE;\r
2050 \r
2051    /* [AS] New properties (see comments in header file) */\r
2052   appData.firstScoreIsAbsolute = FALSE;\r
2053   appData.secondScoreIsAbsolute = FALSE;\r
2054   appData.saveExtendedInfoInPGN = FALSE;\r
2055   appData.hideThinkingFromHuman = FALSE;\r
2056   appData.liteBackTextureFile = "";\r
2057   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2058   appData.darkBackTextureFile = "";\r
2059   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2060   appData.renderPiecesWithFont = "";\r
2061   appData.fontToPieceTable = "";\r
2062   appData.fontBackColorWhite = 0;\r
2063   appData.fontForeColorWhite = 0;\r
2064   appData.fontBackColorBlack = 0;\r
2065   appData.fontForeColorBlack = 0;\r
2066   appData.fontPieceSize = 80;\r
2067   appData.overrideLineGap = 1;\r
2068   appData.adjudicateLossThreshold = 0;\r
2069   appData.delayBeforeQuit = 0;\r
2070   appData.delayAfterQuit = 0;\r
2071   appData.nameOfDebugFile = "winboard.debug";\r
2072   appData.pgnEventHeader = "Computer Chess Game";\r
2073   appData.defaultFrcPosition = -1;\r
2074   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2075   appData.saveOutOfBookInfo = TRUE;\r
2076   appData.showEvalInMoveHistory = TRUE;\r
2077   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2078   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2079   appData.highlightMoveWithArrow = FALSE;\r
2080   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2081   appData.useStickyWindows = TRUE;\r
2082   appData.adjudicateDrawMoves = 0;\r
2083   appData.autoDisplayComment = TRUE;\r
2084   appData.autoDisplayTags = TRUE;\r
2085   appData.firstIsUCI = FALSE;\r
2086   appData.secondIsUCI = FALSE;\r
2087   appData.firstHasOwnBookUCI = TRUE;\r
2088   appData.secondHasOwnBookUCI = TRUE;\r
2089   appData.polyglotDir = "";\r
2090   appData.usePolyglotBook = FALSE;\r
2091   appData.polyglotBook = "";\r
2092   appData.defaultHashSize = 64;\r
2093   appData.defaultCacheSizeEGTB = 4;\r
2094   appData.defaultPathEGTB = "c:\\egtb";\r
2095   appData.firstOptions = "";\r
2096   appData.secondOptions = "";\r
2097 \r
2098   InitWindowPlacement( &wpGameList );\r
2099   InitWindowPlacement( &wpMoveHistory );\r
2100   InitWindowPlacement( &wpEvalGraph );\r
2101   InitWindowPlacement( &wpEngineOutput );\r
2102   InitWindowPlacement( &wpConsole );\r
2103 \r
2104   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2105   appData.NrFiles      = -1;\r
2106   appData.NrRanks      = -1;\r
2107   appData.holdingsSize = -1;\r
2108   appData.testClaims   = FALSE;\r
2109   appData.checkMates   = FALSE;\r
2110   appData.materialDraws= FALSE;\r
2111   appData.trivialDraws = FALSE;\r
2112   appData.ruleMoves    = 51;\r
2113   appData.drawRepeats  = 6;\r
2114   appData.matchPause   = 10000;\r
2115   appData.alphaRank    = FALSE;\r
2116   appData.allWhite     = FALSE;\r
2117   appData.upsideDown   = FALSE;\r
2118   appData.serverPause  = 15;\r
2119   appData.serverMovesName   = NULL;\r
2120   appData.suppressLoadMoves = FALSE;\r
2121   appData.firstTimeOdds  = 1;\r
2122   appData.secondTimeOdds = 1;\r
2123   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2124   appData.secondAccumulateTC = 1;\r
2125   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2126   appData.secondNPS = -1;\r
2127   appData.engineComments = 1;\r
2128   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2129   appData.egtFormats = "";\r
2130 \r
2131 #ifdef ZIPPY\r
2132   appData.zippyTalk = ZIPPY_TALK;\r
2133   appData.zippyPlay = ZIPPY_PLAY;\r
2134   appData.zippyLines = ZIPPY_LINES;\r
2135   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2136   appData.zippyPassword = ZIPPY_PASSWORD;\r
2137   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2138   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2139   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2140   appData.zippyUseI = ZIPPY_USE_I;\r
2141   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2142   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2143   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2144   appData.zippyGameStart = ZIPPY_GAME_START;\r
2145   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2146   appData.zippyAbort = ZIPPY_ABORT;\r
2147   appData.zippyVariants = ZIPPY_VARIANTS;\r
2148   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2149   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2150 #endif\r
2151 \r
2152   /* Point font array elements to structures and\r
2153      parse default font names */\r
2154   for (i=0; i<NUM_FONTS; i++) {\r
2155     for (j=0; j<NUM_SIZES; j++) {\r
2156       font[j][i] = &fontRec[j][i];\r
2157       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2158     }\r
2159   }\r
2160   \r
2161   /* Parse default settings file if any */\r
2162   if (ParseSettingsFile(settingsFileName, buf)) {\r
2163     settingsFileName = strdup(buf);\r
2164   }\r
2165 \r
2166   /* Parse command line */\r
2167   ParseArgs(StringGet, &lpCmdLine);\r
2168 \r
2169   /* [HGM] make sure board size is acceptable */\r
2170   if(appData.NrFiles > BOARD_SIZE ||\r
2171      appData.NrRanks > BOARD_SIZE   )\r
2172       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2173 \r
2174   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2175    * with options from the command line, we now make an even higher priority\r
2176    * overrule by WB options attached to the engine command line. This so that\r
2177    * tournament managers can use WB options (such as /timeOdds) that follow\r
2178    * the engines.\r
2179    */\r
2180   if(appData.firstChessProgram != NULL) {\r
2181       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2182       static char *f = "first";\r
2183       char buf[MSG_SIZ], *q = buf;\r
2184       if(p != NULL) { // engine command line contains WinBoard options\r
2185           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2186           ParseArgs(StringGet, &q);\r
2187           p[-1] = 0; // cut them offengine command line\r
2188       }\r
2189   }\r
2190   // now do same for second chess program\r
2191   if(appData.secondChessProgram != NULL) {\r
2192       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2193       static char *s = "second";\r
2194       char buf[MSG_SIZ], *q = buf;\r
2195       if(p != NULL) { // engine command line contains WinBoard options\r
2196           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2197           ParseArgs(StringGet, &q);\r
2198           p[-1] = 0; // cut them offengine command line\r
2199       }\r
2200   }\r
2201 \r
2202 \r
2203   /* Propagate options that affect others */\r
2204   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2205   if (appData.icsActive || appData.noChessProgram) {\r
2206      chessProgram = FALSE;  /* not local chess program mode */\r
2207   }\r
2208 \r
2209   /* Open startup dialog if needed */\r
2210   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2211       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2212       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2213                         *appData.secondChessProgram == NULLCHAR))) {\r
2214     FARPROC lpProc;\r
2215     \r
2216     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2217     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2218     FreeProcInstance(lpProc);\r
2219   }\r
2220 \r
2221   /* Make sure save files land in the right (?) directory */\r
2222   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2223     appData.saveGameFile = strdup(buf);\r
2224   }\r
2225   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2226     appData.savePositionFile = strdup(buf);\r
2227   }\r
2228 \r
2229   /* Finish initialization for fonts and sounds */\r
2230   for (i=0; i<NUM_FONTS; i++) {\r
2231     for (j=0; j<NUM_SIZES; j++) {\r
2232       CreateFontInMF(font[j][i]);\r
2233     }\r
2234   }\r
2235   /* xboard, and older WinBoards, controlled the move sound with the\r
2236      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2237      always turn the option on (so that the backend will call us),\r
2238      then let the user turn the sound off by setting it to silence if\r
2239      desired.  To accommodate old winboard.ini files saved by old\r
2240      versions of WinBoard, we also turn off the sound if the option\r
2241      was initially set to false. */\r
2242   if (!appData.ringBellAfterMoves) {\r
2243     sounds[(int)SoundMove].name = strdup("");\r
2244     appData.ringBellAfterMoves = TRUE;\r
2245   }\r
2246   GetCurrentDirectory(MSG_SIZ, currDir);\r
2247   SetCurrentDirectory(installDir);\r
2248   LoadAllSounds();\r
2249   SetCurrentDirectory(currDir);\r
2250 \r
2251   p = icsTextMenuString;\r
2252   if (p[0] == '@') {\r
2253     FILE* f = fopen(p + 1, "r");\r
2254     if (f == NULL) {\r
2255       DisplayFatalError(p + 1, errno, 2);\r
2256       return;\r
2257     }\r
2258     i = fread(buf, 1, sizeof(buf)-1, f);\r
2259     fclose(f);\r
2260     buf[i] = NULLCHAR;\r
2261     p = buf;\r
2262   }\r
2263   ParseIcsTextMenu(strdup(p));\r
2264 }\r
2265 \r
2266 \r
2267 VOID\r
2268 InitMenuChecks()\r
2269 {\r
2270   HMENU hmenu = GetMenu(hwndMain);\r
2271 \r
2272   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2273                         MF_BYCOMMAND|((appData.icsActive &&\r
2274                                        *appData.icsCommPort != NULLCHAR) ?\r
2275                                       MF_ENABLED : MF_GRAYED));\r
2276   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2277                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2278                                      MF_CHECKED : MF_UNCHECKED));\r
2279 }\r
2280 \r
2281 \r
2282 VOID\r
2283 SaveSettings(char* name)\r
2284 {\r
2285   FILE *f;\r
2286   ArgDescriptor *ad;\r
2287   WINDOWPLACEMENT wp;\r
2288   char dir[MSG_SIZ];\r
2289 \r
2290   if (!hwndMain) return;\r
2291 \r
2292   GetCurrentDirectory(MSG_SIZ, dir);\r
2293   SetCurrentDirectory(installDir);\r
2294   f = fopen(name, "w");\r
2295   SetCurrentDirectory(dir);\r
2296   if (f == NULL) {\r
2297     DisplayError(name, errno);\r
2298     return;\r
2299   }\r
2300   fprintf(f, ";\n");\r
2301   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2302   fprintf(f, ";\n");\r
2303   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2304   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2305   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2306   fprintf(f, ";\n");\r
2307 \r
2308   wp.length = sizeof(WINDOWPLACEMENT);\r
2309   GetWindowPlacement(hwndMain, &wp);\r
2310   boardX = wp.rcNormalPosition.left;\r
2311   boardY = wp.rcNormalPosition.top;\r
2312 \r
2313   if (hwndConsole) {\r
2314     GetWindowPlacement(hwndConsole, &wp);\r
2315     wpConsole.x = wp.rcNormalPosition.left;\r
2316     wpConsole.y = wp.rcNormalPosition.top;\r
2317     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2318     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2319   }\r
2320 \r
2321   if (analysisDialog) {\r
2322     GetWindowPlacement(analysisDialog, &wp);\r
2323     analysisX = wp.rcNormalPosition.left;\r
2324     analysisY = wp.rcNormalPosition.top;\r
2325     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2326     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2327   }\r
2328 \r
2329   if (commentDialog) {\r
2330     GetWindowPlacement(commentDialog, &wp);\r
2331     commentX = wp.rcNormalPosition.left;\r
2332     commentY = wp.rcNormalPosition.top;\r
2333     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2334     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2335   }\r
2336 \r
2337   if (editTagsDialog) {\r
2338     GetWindowPlacement(editTagsDialog, &wp);\r
2339     editTagsX = wp.rcNormalPosition.left;\r
2340     editTagsY = wp.rcNormalPosition.top;\r
2341     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2342     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2343   }\r
2344 \r
2345   if (gameListDialog) {\r
2346     GetWindowPlacement(gameListDialog, &wp);\r
2347     wpGameList.x = wp.rcNormalPosition.left;\r
2348     wpGameList.y = wp.rcNormalPosition.top;\r
2349     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2350     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2351   }\r
2352 \r
2353   /* [AS] Move history */\r
2354   wpMoveHistory.visible = MoveHistoryIsUp();\r
2355   \r
2356   if( moveHistoryDialog ) {\r
2357     GetWindowPlacement(moveHistoryDialog, &wp);\r
2358     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2359     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2360     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2361     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2362   }\r
2363 \r
2364   /* [AS] Eval graph */\r
2365   wpEvalGraph.visible = EvalGraphIsUp();\r
2366 \r
2367   if( evalGraphDialog ) {\r
2368     GetWindowPlacement(evalGraphDialog, &wp);\r
2369     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2370     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2371     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2372     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2373   }\r
2374 \r
2375   /* [AS] Engine output */\r
2376   wpEngineOutput.visible = EngineOutputIsUp();\r
2377 \r
2378   if( engineOutputDialog ) {\r
2379     GetWindowPlacement(engineOutputDialog, &wp);\r
2380     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2381     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2382     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2383     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2384   }\r
2385 \r
2386   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2387     if (!ad->save) continue;\r
2388     switch (ad->argType) {\r
2389     case ArgString:\r
2390       {\r
2391         char *p = *(char **)ad->argLoc;\r
2392         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2393           /* Quote multiline values or \-containing values\r
2394              with { } if possible */\r
2395           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2396         } else {\r
2397           /* Else quote with " " */\r
2398           fprintf(f, "/%s=\"", ad->argName);\r
2399           while (*p) {\r
2400             if (*p == '\n') fprintf(f, "\n");\r
2401             else if (*p == '\r') fprintf(f, "\\r");\r
2402             else if (*p == '\t') fprintf(f, "\\t");\r
2403             else if (*p == '\b') fprintf(f, "\\b");\r
2404             else if (*p == '\f') fprintf(f, "\\f");\r
2405             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2406             else if (*p == '\"') fprintf(f, "\\\"");\r
2407             else if (*p == '\\') fprintf(f, "\\\\");\r
2408             else putc(*p, f);\r
2409             p++;\r
2410           }\r
2411           fprintf(f, "\"\n");\r
2412         }\r
2413       }\r
2414       break;\r
2415     case ArgInt:\r
2416     case ArgZ:\r
2417       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2418       break;\r
2419     case ArgX:\r
2420       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2421       break;\r
2422     case ArgY:\r
2423       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2424       break;\r
2425     case ArgFloat:\r
2426       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2427       break;\r
2428     case ArgBoolean:\r
2429       fprintf(f, "/%s=%s\n", ad->argName, \r
2430         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2431       break;\r
2432     case ArgTrue:\r
2433       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2434       break;\r
2435     case ArgFalse:\r
2436       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2437       break;\r
2438     case ArgColor:\r
2439       {\r
2440         COLORREF color = *(COLORREF *)ad->argLoc;\r
2441         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2442           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2443       }\r
2444       break;\r
2445     case ArgAttribs:\r
2446       {\r
2447         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2448         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2449           (ta->effects & CFE_BOLD) ? "b" : "",\r
2450           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2451           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2452           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2453           (ta->effects) ? " " : "",\r
2454           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2455       }\r
2456       break;\r
2457     case ArgFilename:\r
2458       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2459         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2460       } else {\r
2461         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2462       }\r
2463       break;\r
2464     case ArgBoardSize:\r
2465       fprintf(f, "/%s=%s\n", ad->argName,\r
2466               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2467       break;\r
2468     case ArgFont:\r
2469       {\r
2470         int bs;\r
2471         for (bs=0; bs<NUM_SIZES; bs++) {\r
2472           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2473           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2474           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2475             ad->argName, mfp->faceName, mfp->pointSize,\r
2476             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2477             mfp->bold ? "b" : "",\r
2478             mfp->italic ? "i" : "",\r
2479             mfp->underline ? "u" : "",\r
2480             mfp->strikeout ? "s" : "");\r
2481         }\r
2482       }\r
2483       break;\r
2484     case ArgCommSettings:\r
2485       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2486     case ArgNone:\r
2487     case ArgSettingsFilename: ;\r
2488     }\r
2489   }\r
2490   fclose(f);\r
2491 }\r
2492 \r
2493 \r
2494 \r
2495 /*---------------------------------------------------------------------------*\\r
2496  *\r
2497  * GDI board drawing routines\r
2498  *\r
2499 \*---------------------------------------------------------------------------*/\r
2500 \r
2501 /* [AS] Draw square using background texture */\r
2502 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2503 {\r
2504     XFORM   x;\r
2505 \r
2506     if( mode == 0 ) {\r
2507         return; /* Should never happen! */\r
2508     }\r
2509 \r
2510     SetGraphicsMode( dst, GM_ADVANCED );\r
2511 \r
2512     switch( mode ) {\r
2513     case 1:\r
2514         /* Identity */\r
2515         break;\r
2516     case 2:\r
2517         /* X reflection */\r
2518         x.eM11 = -1.0;\r
2519         x.eM12 = 0;\r
2520         x.eM21 = 0;\r
2521         x.eM22 = 1.0;\r
2522         x.eDx = (FLOAT) dw + dx - 1;\r
2523         x.eDy = 0;\r
2524         dx = 0;\r
2525         SetWorldTransform( dst, &x );\r
2526         break;\r
2527     case 3:\r
2528         /* Y reflection */\r
2529         x.eM11 = 1.0;\r
2530         x.eM12 = 0;\r
2531         x.eM21 = 0;\r
2532         x.eM22 = -1.0;\r
2533         x.eDx = 0;\r
2534         x.eDy = (FLOAT) dh + dy - 1;\r
2535         dy = 0;\r
2536         SetWorldTransform( dst, &x );\r
2537         break;\r
2538     case 4:\r
2539         /* X/Y flip */\r
2540         x.eM11 = 0;\r
2541         x.eM12 = 1.0;\r
2542         x.eM21 = 1.0;\r
2543         x.eM22 = 0;\r
2544         x.eDx = (FLOAT) dx;\r
2545         x.eDy = (FLOAT) dy;\r
2546         dx = 0;\r
2547         dy = 0;\r
2548         SetWorldTransform( dst, &x );\r
2549         break;\r
2550     }\r
2551 \r
2552     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2553 \r
2554     x.eM11 = 1.0;\r
2555     x.eM12 = 0;\r
2556     x.eM21 = 0;\r
2557     x.eM22 = 1.0;\r
2558     x.eDx = 0;\r
2559     x.eDy = 0;\r
2560     SetWorldTransform( dst, &x );\r
2561 \r
2562     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2563 }\r
2564 \r
2565 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2566 enum {\r
2567     PM_WP = (int) WhitePawn, \r
2568     PM_WN = (int) WhiteKnight, \r
2569     PM_WB = (int) WhiteBishop, \r
2570     PM_WR = (int) WhiteRook, \r
2571     PM_WQ = (int) WhiteQueen, \r
2572     PM_WF = (int) WhiteFerz, \r
2573     PM_WW = (int) WhiteWazir, \r
2574     PM_WE = (int) WhiteAlfil, \r
2575     PM_WM = (int) WhiteMan, \r
2576     PM_WO = (int) WhiteCannon, \r
2577     PM_WU = (int) WhiteUnicorn, \r
2578     PM_WH = (int) WhiteNightrider, \r
2579     PM_WA = (int) WhiteAngel, \r
2580     PM_WC = (int) WhiteMarshall, \r
2581     PM_WAB = (int) WhiteCardinal, \r
2582     PM_WD = (int) WhiteDragon, \r
2583     PM_WL = (int) WhiteLance, \r
2584     PM_WS = (int) WhiteCobra, \r
2585     PM_WV = (int) WhiteFalcon, \r
2586     PM_WSG = (int) WhiteSilver, \r
2587     PM_WG = (int) WhiteGrasshopper, \r
2588     PM_WK = (int) WhiteKing,\r
2589     PM_BP = (int) BlackPawn, \r
2590     PM_BN = (int) BlackKnight, \r
2591     PM_BB = (int) BlackBishop, \r
2592     PM_BR = (int) BlackRook, \r
2593     PM_BQ = (int) BlackQueen, \r
2594     PM_BF = (int) BlackFerz, \r
2595     PM_BW = (int) BlackWazir, \r
2596     PM_BE = (int) BlackAlfil, \r
2597     PM_BM = (int) BlackMan,\r
2598     PM_BO = (int) BlackCannon, \r
2599     PM_BU = (int) BlackUnicorn, \r
2600     PM_BH = (int) BlackNightrider, \r
2601     PM_BA = (int) BlackAngel, \r
2602     PM_BC = (int) BlackMarshall, \r
2603     PM_BG = (int) BlackGrasshopper, \r
2604     PM_BAB = (int) BlackCardinal,\r
2605     PM_BD = (int) BlackDragon,\r
2606     PM_BL = (int) BlackLance,\r
2607     PM_BS = (int) BlackCobra,\r
2608     PM_BV = (int) BlackFalcon,\r
2609     PM_BSG = (int) BlackSilver,\r
2610     PM_BK = (int) BlackKing\r
2611 };\r
2612 \r
2613 static HFONT hPieceFont = NULL;\r
2614 static HBITMAP hPieceMask[(int) EmptySquare];\r
2615 static HBITMAP hPieceFace[(int) EmptySquare];\r
2616 static int fontBitmapSquareSize = 0;\r
2617 static char pieceToFontChar[(int) EmptySquare] =\r
2618                               { 'p', 'n', 'b', 'r', 'q', \r
2619                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2620                       'k', 'o', 'm', 'v', 't', 'w', \r
2621                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2622                                                               'l' };\r
2623 \r
2624 extern BOOL SetCharTable( char *table, const char * map );\r
2625 /* [HGM] moved to backend.c */\r
2626 \r
2627 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2628 {\r
2629     HBRUSH hbrush;\r
2630     BYTE r1 = GetRValue( color );\r
2631     BYTE g1 = GetGValue( color );\r
2632     BYTE b1 = GetBValue( color );\r
2633     BYTE r2 = r1 / 2;\r
2634     BYTE g2 = g1 / 2;\r
2635     BYTE b2 = b1 / 2;\r
2636     RECT rc;\r
2637 \r
2638     /* Create a uniform background first */\r
2639     hbrush = CreateSolidBrush( color );\r
2640     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2641     FillRect( hdc, &rc, hbrush );\r
2642     DeleteObject( hbrush );\r
2643     \r
2644     if( mode == 1 ) {\r
2645         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2646         int steps = squareSize / 2;\r
2647         int i;\r
2648 \r
2649         for( i=0; i<steps; i++ ) {\r
2650             BYTE r = r1 - (r1-r2) * i / steps;\r
2651             BYTE g = g1 - (g1-g2) * i / steps;\r
2652             BYTE b = b1 - (b1-b2) * i / steps;\r
2653 \r
2654             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2655             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2656             FillRect( hdc, &rc, hbrush );\r
2657             DeleteObject(hbrush);\r
2658         }\r
2659     }\r
2660     else if( mode == 2 ) {\r
2661         /* Diagonal gradient, good more or less for every piece */\r
2662         POINT triangle[3];\r
2663         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2664         HBRUSH hbrush_old;\r
2665         int steps = squareSize;\r
2666         int i;\r
2667 \r
2668         triangle[0].x = squareSize - steps;\r
2669         triangle[0].y = squareSize;\r
2670         triangle[1].x = squareSize;\r
2671         triangle[1].y = squareSize;\r
2672         triangle[2].x = squareSize;\r
2673         triangle[2].y = squareSize - steps;\r
2674 \r
2675         for( i=0; i<steps; i++ ) {\r
2676             BYTE r = r1 - (r1-r2) * i / steps;\r
2677             BYTE g = g1 - (g1-g2) * i / steps;\r
2678             BYTE b = b1 - (b1-b2) * i / steps;\r
2679 \r
2680             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2681             hbrush_old = SelectObject( hdc, hbrush );\r
2682             Polygon( hdc, triangle, 3 );\r
2683             SelectObject( hdc, hbrush_old );\r
2684             DeleteObject(hbrush);\r
2685             triangle[0].x++;\r
2686             triangle[2].y++;\r
2687         }\r
2688 \r
2689         SelectObject( hdc, hpen );\r
2690     }\r
2691 }\r
2692 \r
2693 /*\r
2694     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2695     seems to work ok. The main problem here is to find the "inside" of a chess\r
2696     piece: follow the steps as explained below.\r
2697 */\r
2698 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2699 {\r
2700     HBITMAP hbm;\r
2701     HBITMAP hbm_old;\r
2702     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2703     RECT rc;\r
2704     SIZE sz;\r
2705     POINT pt;\r
2706     int backColor = whitePieceColor; \r
2707     int foreColor = blackPieceColor;\r
2708     \r
2709     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2710         backColor = appData.fontBackColorWhite;\r
2711         foreColor = appData.fontForeColorWhite;\r
2712     }\r
2713     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2714         backColor = appData.fontBackColorBlack;\r
2715         foreColor = appData.fontForeColorBlack;\r
2716     }\r
2717 \r
2718     /* Mask */\r
2719     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2720 \r
2721     hbm_old = SelectObject( hdc, hbm );\r
2722 \r
2723     rc.left = 0;\r
2724     rc.top = 0;\r
2725     rc.right = squareSize;\r
2726     rc.bottom = squareSize;\r
2727 \r
2728     /* Step 1: background is now black */\r
2729     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2730 \r
2731     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2732 \r
2733     pt.x = (squareSize - sz.cx) / 2;\r
2734     pt.y = (squareSize - sz.cy) / 2;\r
2735 \r
2736     SetBkMode( hdc, TRANSPARENT );\r
2737     SetTextColor( hdc, chroma );\r
2738     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2739     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2740 \r
2741     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2742     /* Step 3: the area outside the piece is filled with white */\r
2743 //    FloodFill( hdc, 0, 0, chroma );\r
2744     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2745     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2746     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2747     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2748     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2749     /* \r
2750         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2751         but if the start point is not inside the piece we're lost!\r
2752         There should be a better way to do this... if we could create a region or path\r
2753         from the fill operation we would be fine for example.\r
2754     */\r
2755 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2756     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2757 \r
2758     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2759         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2760         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2761 \r
2762         SelectObject( dc2, bm2 );\r
2763         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2764         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2765         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2766         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2767         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2768 \r
2769         DeleteDC( dc2 );\r
2770         DeleteObject( bm2 );\r
2771     }\r
2772 \r
2773     SetTextColor( hdc, 0 );\r
2774     /* \r
2775         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2776         draw the piece again in black for safety.\r
2777     */\r
2778     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2779 \r
2780     SelectObject( hdc, hbm_old );\r
2781 \r
2782     if( hPieceMask[index] != NULL ) {\r
2783         DeleteObject( hPieceMask[index] );\r
2784     }\r
2785 \r
2786     hPieceMask[index] = hbm;\r
2787 \r
2788     /* Face */\r
2789     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2790 \r
2791     SelectObject( hdc, hbm );\r
2792 \r
2793     {\r
2794         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2795         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2796         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2797 \r
2798         SelectObject( dc1, hPieceMask[index] );\r
2799         SelectObject( dc2, bm2 );\r
2800         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2801         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2802         \r
2803         /* \r
2804             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2805             the piece background and deletes (makes transparent) the rest.\r
2806             Thanks to that mask, we are free to paint the background with the greates\r
2807             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2808             We use this, to make gradients and give the pieces a "roundish" look.\r
2809         */\r
2810         SetPieceBackground( hdc, backColor, 2 );\r
2811         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2812 \r
2813         DeleteDC( dc2 );\r
2814         DeleteDC( dc1 );\r
2815         DeleteObject( bm2 );\r
2816     }\r
2817 \r
2818     SetTextColor( hdc, foreColor );\r
2819     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2820 \r
2821     SelectObject( hdc, hbm_old );\r
2822 \r
2823     if( hPieceFace[index] != NULL ) {\r
2824         DeleteObject( hPieceFace[index] );\r
2825     }\r
2826 \r
2827     hPieceFace[index] = hbm;\r
2828 }\r
2829 \r
2830 static int TranslatePieceToFontPiece( int piece )\r
2831 {\r
2832     switch( piece ) {\r
2833     case BlackPawn:\r
2834         return PM_BP;\r
2835     case BlackKnight:\r
2836         return PM_BN;\r
2837     case BlackBishop:\r
2838         return PM_BB;\r
2839     case BlackRook:\r
2840         return PM_BR;\r
2841     case BlackQueen:\r
2842         return PM_BQ;\r
2843     case BlackKing:\r
2844         return PM_BK;\r
2845     case WhitePawn:\r
2846         return PM_WP;\r
2847     case WhiteKnight:\r
2848         return PM_WN;\r
2849     case WhiteBishop:\r
2850         return PM_WB;\r
2851     case WhiteRook:\r
2852         return PM_WR;\r
2853     case WhiteQueen:\r
2854         return PM_WQ;\r
2855     case WhiteKing:\r
2856         return PM_WK;\r
2857 \r
2858     case BlackAngel:\r
2859         return PM_BA;\r
2860     case BlackMarshall:\r
2861         return PM_BC;\r
2862     case BlackFerz:\r
2863         return PM_BF;\r
2864     case BlackNightrider:\r
2865         return PM_BH;\r
2866     case BlackAlfil:\r
2867         return PM_BE;\r
2868     case BlackWazir:\r
2869         return PM_BW;\r
2870     case BlackUnicorn:\r
2871         return PM_BU;\r
2872     case BlackCannon:\r
2873         return PM_BO;\r
2874     case BlackGrasshopper:\r
2875         return PM_BG;\r
2876     case BlackMan:\r
2877         return PM_BM;\r
2878     case BlackSilver:\r
2879         return PM_BSG;\r
2880     case BlackLance:\r
2881         return PM_BL;\r
2882     case BlackFalcon:\r
2883         return PM_BV;\r
2884     case BlackCobra:\r
2885         return PM_BS;\r
2886     case BlackCardinal:\r
2887         return PM_BAB;\r
2888     case BlackDragon:\r
2889         return PM_BD;\r
2890 \r
2891     case WhiteAngel:\r
2892         return PM_WA;\r
2893     case WhiteMarshall:\r
2894         return PM_WC;\r
2895     case WhiteFerz:\r
2896         return PM_WF;\r
2897     case WhiteNightrider:\r
2898         return PM_WH;\r
2899     case WhiteAlfil:\r
2900         return PM_WE;\r
2901     case WhiteWazir:\r
2902         return PM_WW;\r
2903     case WhiteUnicorn:\r
2904         return PM_WU;\r
2905     case WhiteCannon:\r
2906         return PM_WO;\r
2907     case WhiteGrasshopper:\r
2908         return PM_WG;\r
2909     case WhiteMan:\r
2910         return PM_WM;\r
2911     case WhiteSilver:\r
2912         return PM_WSG;\r
2913     case WhiteLance:\r
2914         return PM_WL;\r
2915     case WhiteFalcon:\r
2916         return PM_WV;\r
2917     case WhiteCobra:\r
2918         return PM_WS;\r
2919     case WhiteCardinal:\r
2920         return PM_WAB;\r
2921     case WhiteDragon:\r
2922         return PM_WD;\r
2923     }\r
2924 \r
2925     return 0;\r
2926 }\r
2927 \r
2928 void CreatePiecesFromFont()\r
2929 {\r
2930     LOGFONT lf;\r
2931     HDC hdc_window = NULL;\r
2932     HDC hdc = NULL;\r
2933     HFONT hfont_old;\r
2934     int fontHeight;\r
2935     int i;\r
2936 \r
2937     if( fontBitmapSquareSize < 0 ) {\r
2938         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2939         return;\r
2940     }\r
2941 \r
2942     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2943         fontBitmapSquareSize = -1;\r
2944         return;\r
2945     }\r
2946 \r
2947     if( fontBitmapSquareSize != squareSize ) {\r
2948         hdc_window = GetDC( hwndMain );\r
2949         hdc = CreateCompatibleDC( hdc_window );\r
2950 \r
2951         if( hPieceFont != NULL ) {\r
2952             DeleteObject( hPieceFont );\r
2953         }\r
2954         else {\r
2955             for( i=0; i<=(int)BlackKing; i++ ) {\r
2956                 hPieceMask[i] = NULL;\r
2957                 hPieceFace[i] = NULL;\r
2958             }\r
2959         }\r
2960 \r
2961         fontHeight = 75;\r
2962 \r
2963         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2964             fontHeight = appData.fontPieceSize;\r
2965         }\r
2966 \r
2967         fontHeight = (fontHeight * squareSize) / 100;\r
2968 \r
2969         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2970         lf.lfWidth = 0;\r
2971         lf.lfEscapement = 0;\r
2972         lf.lfOrientation = 0;\r
2973         lf.lfWeight = FW_NORMAL;\r
2974         lf.lfItalic = 0;\r
2975         lf.lfUnderline = 0;\r
2976         lf.lfStrikeOut = 0;\r
2977         lf.lfCharSet = DEFAULT_CHARSET;\r
2978         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2979         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2980         lf.lfQuality = PROOF_QUALITY;\r
2981         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2982         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2983         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2984 \r
2985         hPieceFont = CreateFontIndirect( &lf );\r
2986 \r
2987         if( hPieceFont == NULL ) {\r
2988             fontBitmapSquareSize = -2;\r
2989         }\r
2990         else {\r
2991             /* Setup font-to-piece character table */\r
2992             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2993                 /* No (or wrong) global settings, try to detect the font */\r
2994                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2995                     /* Alpha */\r
2996                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2997                 }\r
2998                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2999                     /* DiagramTT* family */\r
3000                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3001                 }\r
3002                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3003                     /* Fairy symbols */\r
3004                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3005                 }\r
3006                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3007                     /* Good Companion (Some characters get warped as literal :-( */\r
3008                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3009                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3010                     SetCharTable(pieceToFontChar, s);\r
3011                 }\r
3012                 else {\r
3013                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3014                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3015                 }\r
3016             }\r
3017 \r
3018             /* Create bitmaps */\r
3019             hfont_old = SelectObject( hdc, hPieceFont );\r
3020 #if 0\r
3021             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
3022             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
3023             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
3024             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
3025             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
3026             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
3027             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
3028             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
3029             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
3030             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
3031             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
3032             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
3033 \r
3034             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
3035             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
3036             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
3037             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
3038             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
3039             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
3040             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
3041             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
3042             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
3043             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
3044             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
3045             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
3046             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
3047             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
3048             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
3049             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
3050             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
3051             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
3052             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
3053             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
3054             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
3055             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
3056             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
3057             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
3058             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
3059             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
3060             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
3061             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
3062             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
3063             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
3064             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
3065             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
3066 #else\r
3067             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3068                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3069                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3070 #endif\r
3071             SelectObject( hdc, hfont_old );\r
3072 \r
3073             fontBitmapSquareSize = squareSize;\r
3074         }\r
3075     }\r
3076 \r
3077     if( hdc != NULL ) {\r
3078         DeleteDC( hdc );\r
3079     }\r
3080 \r
3081     if( hdc_window != NULL ) {\r
3082         ReleaseDC( hwndMain, hdc_window );\r
3083     }\r
3084 }\r
3085 \r
3086 HBITMAP\r
3087 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3088 {\r
3089   char name[128];\r
3090 \r
3091   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3092   if (gameInfo.event &&\r
3093       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3094       strcmp(name, "k80s") == 0) {\r
3095     strcpy(name, "tim");\r
3096   }\r
3097   return LoadBitmap(hinst, name);\r
3098 }\r
3099 \r
3100 \r
3101 /* Insert a color into the program's logical palette\r
3102    structure.  This code assumes the given color is\r
3103    the result of the RGB or PALETTERGB macro, and it\r
3104    knows how those macros work (which is documented).\r
3105 */\r
3106 VOID\r
3107 InsertInPalette(COLORREF color)\r
3108 {\r
3109   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3110 \r
3111   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3112     DisplayFatalError("Too many colors", 0, 1);\r
3113     pLogPal->palNumEntries--;\r
3114     return;\r
3115   }\r
3116 \r
3117   pe->peFlags = (char) 0;\r
3118   pe->peRed = (char) (0xFF & color);\r
3119   pe->peGreen = (char) (0xFF & (color >> 8));\r
3120   pe->peBlue = (char) (0xFF & (color >> 16));\r
3121   return;\r
3122 }\r
3123 \r
3124 \r
3125 VOID\r
3126 InitDrawingColors()\r
3127 {\r
3128   if (pLogPal == NULL) {\r
3129     /* Allocate enough memory for a logical palette with\r
3130      * PALETTESIZE entries and set the size and version fields\r
3131      * of the logical palette structure.\r
3132      */\r
3133     pLogPal = (NPLOGPALETTE)\r
3134       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3135                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3136     pLogPal->palVersion    = 0x300;\r
3137   }\r
3138   pLogPal->palNumEntries = 0;\r
3139 \r
3140   InsertInPalette(lightSquareColor);\r
3141   InsertInPalette(darkSquareColor);\r
3142   InsertInPalette(whitePieceColor);\r
3143   InsertInPalette(blackPieceColor);\r
3144   InsertInPalette(highlightSquareColor);\r
3145   InsertInPalette(premoveHighlightColor);\r
3146 \r
3147   /*  create a logical color palette according the information\r
3148    *  in the LOGPALETTE structure.\r
3149    */\r
3150   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3151 \r
3152   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3153   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3154   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3155   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3156   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3157   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3158   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3159   /* [AS] Force rendering of the font-based pieces */\r
3160   if( fontBitmapSquareSize > 0 ) {\r
3161     fontBitmapSquareSize = 0;\r
3162   }\r
3163 }\r
3164 \r
3165 \r
3166 int\r
3167 BoardWidth(int boardSize, int n)\r
3168 { /* [HGM] argument n added to allow different width and height */\r
3169   int lineGap = sizeInfo[boardSize].lineGap;\r
3170 \r
3171   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3172       lineGap = appData.overrideLineGap;\r
3173   }\r
3174 \r
3175   return (n + 1) * lineGap +\r
3176           n * sizeInfo[boardSize].squareSize;\r
3177 }\r
3178 \r
3179 /* Respond to board resize by dragging edge */\r
3180 VOID\r
3181 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3182 {\r
3183   BoardSize newSize = NUM_SIZES - 1;\r
3184   static int recurse = 0;\r
3185   if (IsIconic(hwndMain)) return;\r
3186   if (recurse > 0) return;\r
3187   recurse++;\r
3188   while (newSize > 0) {\r
3189         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3190         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3191            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3192     newSize--;\r
3193   } \r
3194   boardSize = newSize;\r
3195   InitDrawingSizes(boardSize, flags);\r
3196   recurse--;\r
3197 }\r
3198 \r
3199 \r
3200 \r
3201 VOID\r
3202 InitDrawingSizes(BoardSize boardSize, int flags)\r
3203 {\r
3204   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3205   ChessSquare piece;\r
3206   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3207   HDC hdc;\r
3208   SIZE clockSize, messageSize;\r
3209   HFONT oldFont;\r
3210   char buf[MSG_SIZ];\r
3211   char *str;\r
3212   HMENU hmenu = GetMenu(hwndMain);\r
3213   RECT crect, wrect, oldRect;\r
3214   int offby;\r
3215   LOGBRUSH logbrush;\r
3216 \r
3217   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3218   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3219 \r
3220   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3221   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3222 \r
3223   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3224   oldRect.top = boardY;\r
3225   oldRect.right = boardX + winWidth;\r
3226   oldRect.bottom = boardY + winHeight;\r
3227 \r
3228   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3229   smallLayout = sizeInfo[boardSize].smallLayout;\r
3230   squareSize = sizeInfo[boardSize].squareSize;\r
3231   lineGap = sizeInfo[boardSize].lineGap;\r
3232   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3233 \r
3234   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3235       lineGap = appData.overrideLineGap;\r
3236   }\r
3237 \r
3238   if (tinyLayout != oldTinyLayout) {\r
3239     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3240     if (tinyLayout) {\r
3241       style &= ~WS_SYSMENU;\r
3242       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3243                  "&Minimize\tCtrl+F4");\r
3244     } else {\r
3245       style |= WS_SYSMENU;\r
3246       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3247     }\r
3248     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3249 \r
3250     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3251       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3252         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3253     }\r
3254     DrawMenuBar(hwndMain);\r
3255   }\r
3256 \r
3257   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3258   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3259 \r
3260   /* Get text area sizes */\r
3261   hdc = GetDC(hwndMain);\r
3262   if (appData.clockMode) {\r
3263     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3264   } else {\r
3265     sprintf(buf, "White");\r
3266   }\r
3267   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3268   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3269   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3270   str = "We only care about the height here";\r
3271   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3272   SelectObject(hdc, oldFont);\r
3273   ReleaseDC(hwndMain, hdc);\r
3274 \r
3275   /* Compute where everything goes */\r
3276   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3277         /* [HGM] logo: if either logo is on, reserve space for it */\r
3278         logoHeight =  2*clockSize.cy;\r
3279         leftLogoRect.left   = OUTER_MARGIN;\r
3280         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3281         leftLogoRect.top    = OUTER_MARGIN;\r
3282         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3283 \r
3284         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3285         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3286         rightLogoRect.top    = OUTER_MARGIN;\r
3287         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3288 \r
3289 \r
3290     whiteRect.left = leftLogoRect.right;\r
3291     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3292     whiteRect.top = OUTER_MARGIN;\r
3293     whiteRect.bottom = whiteRect.top + logoHeight;\r
3294 \r
3295     blackRect.right = rightLogoRect.left;\r
3296     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3297     blackRect.top = whiteRect.top;\r
3298     blackRect.bottom = whiteRect.bottom;\r
3299   } else {\r
3300     whiteRect.left = OUTER_MARGIN;\r
3301     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3302     whiteRect.top = OUTER_MARGIN;\r
3303     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3304 \r
3305     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3306     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3307     blackRect.top = whiteRect.top;\r
3308     blackRect.bottom = whiteRect.bottom;\r
3309   }\r
3310 \r
3311   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3312   if (appData.showButtonBar) {\r
3313     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3314       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3315   } else {\r
3316     messageRect.right = OUTER_MARGIN + boardWidth;\r
3317   }\r
3318   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3319   messageRect.bottom = messageRect.top + messageSize.cy;\r
3320 \r
3321   boardRect.left = OUTER_MARGIN;\r
3322   boardRect.right = boardRect.left + boardWidth;\r
3323   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3324   boardRect.bottom = boardRect.top + boardHeight;\r
3325 \r
3326   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3327   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3328   oldBoardSize = boardSize;\r
3329   oldTinyLayout = tinyLayout;\r
3330   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3331   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3332     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3333   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3334   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3335   winHeight = winH; //       without disturbing window attachments\r
3336   GetWindowRect(hwndMain, &wrect);\r
3337   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3338                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3339 \r
3340   // [HGM] placement: let attached windows follow size change.\r
3341   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3342   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3343   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3344   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3345   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3346 \r
3347   /* compensate if menu bar wrapped */\r
3348   GetClientRect(hwndMain, &crect);\r
3349   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3350   winHeight += offby;\r
3351   switch (flags) {\r
3352   case WMSZ_TOPLEFT:\r
3353     SetWindowPos(hwndMain, NULL, \r
3354                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3355                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3356     break;\r
3357 \r
3358   case WMSZ_TOPRIGHT:\r
3359   case WMSZ_TOP:\r
3360     SetWindowPos(hwndMain, NULL, \r
3361                  wrect.left, wrect.bottom - winHeight, \r
3362                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3363     break;\r
3364 \r
3365   case WMSZ_BOTTOMLEFT:\r
3366   case WMSZ_LEFT:\r
3367     SetWindowPos(hwndMain, NULL, \r
3368                  wrect.right - winWidth, wrect.top, \r
3369                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3370     break;\r
3371 \r
3372   case WMSZ_BOTTOMRIGHT:\r
3373   case WMSZ_BOTTOM:\r
3374   case WMSZ_RIGHT:\r
3375   default:\r
3376     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3377                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3378     break;\r
3379   }\r
3380 \r
3381   hwndPause = NULL;\r
3382   for (i = 0; i < N_BUTTONS; i++) {\r
3383     if (buttonDesc[i].hwnd != NULL) {\r
3384       DestroyWindow(buttonDesc[i].hwnd);\r
3385       buttonDesc[i].hwnd = NULL;\r
3386     }\r
3387     if (appData.showButtonBar) {\r
3388       buttonDesc[i].hwnd =\r
3389         CreateWindow("BUTTON", buttonDesc[i].label,\r
3390                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3391                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3392                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3393                      (HMENU) buttonDesc[i].id,\r
3394                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3395       if (tinyLayout) {\r
3396         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3397                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3398                     MAKELPARAM(FALSE, 0));\r
3399       }\r
3400       if (buttonDesc[i].id == IDM_Pause)\r
3401         hwndPause = buttonDesc[i].hwnd;\r
3402       buttonDesc[i].wndproc = (WNDPROC)\r
3403         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3404     }\r
3405   }\r
3406   if (gridPen != NULL) DeleteObject(gridPen);\r
3407   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3408   if (premovePen != NULL) DeleteObject(premovePen);\r
3409   if (lineGap != 0) {\r
3410     logbrush.lbStyle = BS_SOLID;\r
3411     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3412     gridPen =\r
3413       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3414                    lineGap, &logbrush, 0, NULL);\r
3415     logbrush.lbColor = highlightSquareColor;\r
3416     highlightPen =\r
3417       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3418                    lineGap, &logbrush, 0, NULL);\r
3419 \r
3420     logbrush.lbColor = premoveHighlightColor; \r
3421     premovePen =\r
3422       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3423                    lineGap, &logbrush, 0, NULL);\r
3424 \r
3425     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3426     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3427       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3428       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3429         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3430       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3431         BOARD_WIDTH * (squareSize + lineGap);\r
3432       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3433     }\r
3434     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3435       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3436       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3437         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3438         lineGap / 2 + (i * (squareSize + lineGap));\r
3439       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3440         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3441       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3442     }\r
3443   }\r
3444 \r
3445   /* [HGM] Licensing requirement */\r
3446 #ifdef GOTHIC\r
3447   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3448 #endif\r
3449 #ifdef FALCON\r
3450   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3451 #endif\r
3452   GothicPopUp( "", VariantNormal);\r
3453 \r
3454 \r
3455 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3456 \r
3457   /* Load piece bitmaps for this board size */\r
3458   for (i=0; i<=2; i++) {\r
3459     for (piece = WhitePawn;\r
3460          (int) piece < (int) BlackPawn;\r
3461          piece = (ChessSquare) ((int) piece + 1)) {\r
3462       if (pieceBitmap[i][piece] != NULL)\r
3463         DeleteObject(pieceBitmap[i][piece]);\r
3464     }\r
3465   }\r
3466 \r
3467   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3468   // Orthodox Chess pieces\r
3469   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3470   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3471   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3472   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3473   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3474   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3475   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3476   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3477   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3478   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3479   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3480   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3481   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3482   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3483   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3484   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3485     // in Shogi, Hijack the unused Queen for Lance\r
3486     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3487     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3488     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3489   } else {\r
3490     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3491     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3492     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3493   }\r
3494 \r
3495   if(squareSize <= 72 && squareSize >= 33) { \r
3496     /* A & C are available in most sizes now */\r
3497     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3498       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3499       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3500       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3501       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3502       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3503       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3504       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3505       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3506       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3507       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3508       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3509       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3510     } else { // Smirf-like\r
3511       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3512       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3513       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3514     }\r
3515     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3516       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3517       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3518       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3519     } else { // WinBoard standard\r
3520       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3521       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3522       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3523     }\r
3524   }\r
3525 \r
3526 \r
3527   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3528     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3529     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3530     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3531     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3532     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3533     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3534     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3535     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3536     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3537     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3538     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3539     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3540     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3541     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3542     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3543     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3544     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3545     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3546     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3547     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3548     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3549     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3550     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3551     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3552     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3553     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3554     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3555     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3556     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3557     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3558 \r
3559     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3560       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3561       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3562       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3563       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3564       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3565       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3566       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3567       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3568       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3569       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3570       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3571       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3572     } else {\r
3573       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3574       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3575       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3576       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3577       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3578       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3579       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3580       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3581       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3582       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3583       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3584       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3585     }\r
3586 \r
3587   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3588     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3589     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3590     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3591     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3592     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3593     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3594     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3595     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3596     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3597     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3598     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3599     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3600     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3601     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3602   }\r
3603 \r
3604 \r
3605   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3606   /* special Shogi support in this size */\r
3607   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3608       for (piece = WhitePawn;\r
3609            (int) piece < (int) BlackPawn;\r
3610            piece = (ChessSquare) ((int) piece + 1)) {\r
3611         if (pieceBitmap[i][piece] != NULL)\r
3612           DeleteObject(pieceBitmap[i][piece]);\r
3613       }\r
3614     }\r
3615   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3616   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3617   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3618   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3619   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3620   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3621   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3622   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3623   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3624   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3625   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3626   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3627   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3628   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3629   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3630   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3631   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3632   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3633   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3634   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3635   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3636   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3637   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3638   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3639   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3640   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3641   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3642   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3643   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3644   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3645   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3646   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3647   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3648   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3649   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3650   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3651   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3652   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3653   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3654   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3655   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3656   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3657   minorSize = 0;\r
3658   }\r
3659 }\r
3660 \r
3661 HBITMAP\r
3662 PieceBitmap(ChessSquare p, int kind)\r
3663 {\r
3664   if ((int) p >= (int) BlackPawn)\r
3665     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3666 \r
3667   return pieceBitmap[kind][(int) p];\r
3668 }\r
3669 \r
3670 /***************************************************************/\r
3671 \r
3672 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3673 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3674 /*\r
3675 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3676 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3677 */\r
3678 \r
3679 VOID\r
3680 SquareToPos(int row, int column, int * x, int * y)\r
3681 {\r
3682   if (flipView) {\r
3683     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3684     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3685   } else {\r
3686     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3687     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3688   }\r
3689 }\r
3690 \r
3691 VOID\r
3692 DrawCoordsOnDC(HDC hdc)\r
3693 {\r
3694   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
3695   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
3696   char str[2] = { NULLCHAR, NULLCHAR };\r
3697   int oldMode, oldAlign, x, y, start, i;\r
3698   HFONT oldFont;\r
3699   HBRUSH oldBrush;\r
3700 \r
3701   if (!appData.showCoords)\r
3702     return;\r
3703 \r
3704   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3705 \r
3706   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3707   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3708   oldAlign = GetTextAlign(hdc);\r
3709   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3710 \r
3711   y = boardRect.top + lineGap;\r
3712   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3713 \r
3714   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3715   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3716     str[0] = files[start + i];\r
3717     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3718     y += squareSize + lineGap;\r
3719   }\r
3720 \r
3721   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3722 \r
3723   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3724   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3725     str[0] = ranks[start + i];\r
3726     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3727     x += squareSize + lineGap;\r
3728   }    \r
3729 \r
3730   SelectObject(hdc, oldBrush);\r
3731   SetBkMode(hdc, oldMode);\r
3732   SetTextAlign(hdc, oldAlign);\r
3733   SelectObject(hdc, oldFont);\r
3734 }\r
3735 \r
3736 VOID\r
3737 DrawGridOnDC(HDC hdc)\r
3738 {\r
3739   HPEN oldPen;\r
3740  \r
3741   if (lineGap != 0) {\r
3742     oldPen = SelectObject(hdc, gridPen);\r
3743     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3744     SelectObject(hdc, oldPen);\r
3745   }\r
3746 }\r
3747 \r
3748 #define HIGHLIGHT_PEN 0\r
3749 #define PREMOVE_PEN   1\r
3750 \r
3751 VOID\r
3752 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3753 {\r
3754   int x1, y1;\r
3755   HPEN oldPen, hPen;\r
3756   if (lineGap == 0) return;\r
3757   if (flipView) {\r
3758     x1 = boardRect.left +\r
3759       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3760     y1 = boardRect.top +\r
3761       lineGap/2 + y * (squareSize + lineGap);\r
3762   } else {\r
3763     x1 = boardRect.left +\r
3764       lineGap/2 + x * (squareSize + lineGap);\r
3765     y1 = boardRect.top +\r
3766       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3767   }\r
3768   hPen = pen ? premovePen : highlightPen;\r
3769   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3770   MoveToEx(hdc, x1, y1, NULL);\r
3771   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3772   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3773   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3774   LineTo(hdc, x1, y1);\r
3775   SelectObject(hdc, oldPen);\r
3776 }\r
3777 \r
3778 VOID\r
3779 DrawHighlightsOnDC(HDC hdc)\r
3780 {\r
3781   int i;\r
3782   for (i=0; i<2; i++) {\r
3783     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3784       DrawHighlightOnDC(hdc, TRUE,\r
3785                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3786                         HIGHLIGHT_PEN);\r
3787   }\r
3788   for (i=0; i<2; i++) {\r
3789     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3790         premoveHighlightInfo.sq[i].y >= 0) {\r
3791         DrawHighlightOnDC(hdc, TRUE,\r
3792                           premoveHighlightInfo.sq[i].x, \r
3793                           premoveHighlightInfo.sq[i].y,\r
3794                           PREMOVE_PEN);\r
3795     }\r
3796   }\r
3797 }\r
3798 \r
3799 /* Note: sqcolor is used only in monoMode */\r
3800 /* Note that this code is largely duplicated in woptions.c,\r
3801    function DrawSampleSquare, so that needs to be updated too */\r
3802 VOID\r
3803 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3804 {\r
3805   HBITMAP oldBitmap;\r
3806   HBRUSH oldBrush;\r
3807   int tmpSize;\r
3808 \r
3809   if (appData.blindfold) return;\r
3810 \r
3811   /* [AS] Use font-based pieces if needed */\r
3812   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3813     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3814     CreatePiecesFromFont();\r
3815 \r
3816     if( fontBitmapSquareSize == squareSize ) {\r
3817         int index = TranslatePieceToFontPiece(piece);\r
3818 \r
3819         SelectObject( tmphdc, hPieceMask[ index ] );\r
3820 \r
3821         BitBlt( hdc,\r
3822             x, y,\r
3823             squareSize, squareSize,\r
3824             tmphdc,\r
3825             0, 0,\r
3826             SRCAND );\r
3827 \r
3828         SelectObject( tmphdc, hPieceFace[ index ] );\r
3829 \r
3830         BitBlt( hdc,\r
3831             x, y,\r
3832             squareSize, squareSize,\r
3833             tmphdc,\r
3834             0, 0,\r
3835             SRCPAINT );\r
3836 \r
3837         return;\r
3838     }\r
3839   }\r
3840 \r
3841   if (appData.monoMode) {\r
3842     SelectObject(tmphdc, PieceBitmap(piece, \r
3843       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3844     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3845            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3846   } else {\r
3847     tmpSize = squareSize;\r
3848     if(minorSize &&\r
3849         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3850          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3851       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3852       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3853       x += (squareSize - minorSize)>>1;\r
3854       y += squareSize - minorSize - 2;\r
3855       tmpSize = minorSize;\r
3856     }\r
3857     if (color || appData.allWhite ) {\r
3858       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3859       if( color )\r
3860               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3861       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3862       if(appData.upsideDown && color==flipView)\r
3863         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3864       else\r
3865         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3866 #if 0\r
3867       /* Use black piece color for outline of white pieces */\r
3868       /* Not sure this looks really good (though xboard does it).\r
3869          Maybe better to have another selectable color, default black */\r
3870       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3871       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3872       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3873 #else\r
3874       /* Use black for outline of white pieces */\r
3875       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3876       if(appData.upsideDown && color==flipView)\r
3877         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3878       else\r
3879         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3880 #endif\r
3881     } else {\r
3882 #if 0\r
3883       /* Use white piece color for details of black pieces */\r
3884       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3885          WHITE_PIECE ones aren't always the right shape. */\r
3886       /* Not sure this looks really good (though xboard does it).\r
3887          Maybe better to have another selectable color, default medium gray? */\r
3888       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3889       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3890       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3891       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3892       SelectObject(hdc, blackPieceBrush);\r
3893       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3894 #else\r
3895       /* Use square color for details of black pieces */\r
3896       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3897       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3898       if(appData.upsideDown && !flipView)\r
3899         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3900       else\r
3901         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3902 #endif\r
3903     }\r
3904     SelectObject(hdc, oldBrush);\r
3905     SelectObject(tmphdc, oldBitmap);\r
3906   }\r
3907 }\r
3908 \r
3909 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3910 int GetBackTextureMode( int algo )\r
3911 {\r
3912     int result = BACK_TEXTURE_MODE_DISABLED;\r
3913 \r
3914     switch( algo ) \r
3915     {\r
3916         case BACK_TEXTURE_MODE_PLAIN:\r
3917             result = 1; /* Always use identity map */\r
3918             break;\r
3919         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3920             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3921             break;\r
3922     }\r
3923 \r
3924     return result;\r
3925 }\r
3926 \r
3927 /* \r
3928     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3929     to handle redraws cleanly (as random numbers would always be different).\r
3930 */\r
3931 VOID RebuildTextureSquareInfo()\r
3932 {\r
3933     BITMAP bi;\r
3934     int lite_w = 0;\r
3935     int lite_h = 0;\r
3936     int dark_w = 0;\r
3937     int dark_h = 0;\r
3938     int row;\r
3939     int col;\r
3940 \r
3941     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3942 \r
3943     if( liteBackTexture != NULL ) {\r
3944         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3945             lite_w = bi.bmWidth;\r
3946             lite_h = bi.bmHeight;\r
3947         }\r
3948     }\r
3949 \r
3950     if( darkBackTexture != NULL ) {\r
3951         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3952             dark_w = bi.bmWidth;\r
3953             dark_h = bi.bmHeight;\r
3954         }\r
3955     }\r
3956 \r
3957     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3958         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3959             if( (col + row) & 1 ) {\r
3960                 /* Lite square */\r
3961                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3962                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3963                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3964                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3965                 }\r
3966             }\r
3967             else {\r
3968                 /* Dark square */\r
3969                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3970                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3971                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3972                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3973                 }\r
3974             }\r
3975         }\r
3976     }\r
3977 }\r
3978 \r
3979 /* [AS] Arrow highlighting support */\r
3980 \r
3981 static int A_WIDTH = 5; /* Width of arrow body */\r
3982 \r
3983 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3984 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3985 \r
3986 static double Sqr( double x )\r
3987 {\r
3988     return x*x;\r
3989 }\r
3990 \r
3991 static int Round( double x )\r
3992 {\r
3993     return (int) (x + 0.5);\r
3994 }\r
3995 \r
3996 /* Draw an arrow between two points using current settings */\r
3997 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3998 {\r
3999     POINT arrow[7];\r
4000     double dx, dy, j, k, x, y;\r
4001 \r
4002     if( d_x == s_x ) {\r
4003         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4004 \r
4005         arrow[0].x = s_x + A_WIDTH;\r
4006         arrow[0].y = s_y;\r
4007 \r
4008         arrow[1].x = s_x + A_WIDTH;\r
4009         arrow[1].y = d_y - h;\r
4010 \r
4011         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
4012         arrow[2].y = d_y - h;\r
4013 \r
4014         arrow[3].x = d_x;\r
4015         arrow[3].y = d_y;\r
4016 \r
4017         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
4018         arrow[4].y = d_y - h;\r
4019 \r
4020         arrow[5].x = s_x - A_WIDTH;\r
4021         arrow[5].y = d_y - h;\r
4022 \r
4023         arrow[6].x = s_x - A_WIDTH;\r
4024         arrow[6].y = s_y;\r
4025     }\r
4026     else if( d_y == s_y ) {\r
4027         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4028 \r
4029         arrow[0].x = s_x;\r
4030         arrow[0].y = s_y + A_WIDTH;\r
4031 \r
4032         arrow[1].x = d_x - w;\r
4033         arrow[1].y = s_y + A_WIDTH;\r
4034 \r
4035         arrow[2].x = d_x - w;\r
4036         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
4037 \r
4038         arrow[3].x = d_x;\r
4039         arrow[3].y = d_y;\r
4040 \r
4041         arrow[4].x = d_x - w;\r
4042         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
4043 \r
4044         arrow[5].x = d_x - w;\r
4045         arrow[5].y = s_y - A_WIDTH;\r
4046 \r
4047         arrow[6].x = s_x;\r
4048         arrow[6].y = s_y - A_WIDTH;\r
4049     }\r
4050     else {\r
4051         /* [AS] Needed a lot of paper for this! :-) */\r
4052         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4053         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4054   \r
4055         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4056 \r
4057         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4058 \r
4059         x = s_x;\r
4060         y = s_y;\r
4061 \r
4062         arrow[0].x = Round(x - j);\r
4063         arrow[0].y = Round(y + j*dx);\r
4064 \r
4065         arrow[1].x = Round(x + j);\r
4066         arrow[1].y = Round(y - j*dx);\r
4067 \r
4068         if( d_x > s_x ) {\r
4069             x = (double) d_x - k;\r
4070             y = (double) d_y - k*dy;\r
4071         }\r
4072         else {\r
4073             x = (double) d_x + k;\r
4074             y = (double) d_y + k*dy;\r
4075         }\r
4076 \r
4077         arrow[2].x = Round(x + j);\r
4078         arrow[2].y = Round(y - j*dx);\r
4079 \r
4080         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4081         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4082 \r
4083         arrow[4].x = d_x;\r
4084         arrow[4].y = d_y;\r
4085 \r
4086         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4087         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4088 \r
4089         arrow[6].x = Round(x - j);\r
4090         arrow[6].y = Round(y + j*dx);\r
4091     }\r
4092 \r
4093     Polygon( hdc, arrow, 7 );\r
4094 }\r
4095 \r
4096 /* [AS] Draw an arrow between two squares */\r
4097 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4098 {\r
4099     int s_x, s_y, d_x, d_y;\r
4100     HPEN hpen;\r
4101     HPEN holdpen;\r
4102     HBRUSH hbrush;\r
4103     HBRUSH holdbrush;\r
4104     LOGBRUSH stLB;\r
4105 \r
4106     if( s_col == d_col && s_row == d_row ) {\r
4107         return;\r
4108     }\r
4109 \r
4110     /* Get source and destination points */\r
4111     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4112     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4113 \r
4114     if( d_y > s_y ) {\r
4115         d_y += squareSize / 4;\r
4116     }\r
4117     else if( d_y < s_y ) {\r
4118         d_y += 3 * squareSize / 4;\r
4119     }\r
4120     else {\r
4121         d_y += squareSize / 2;\r
4122     }\r
4123 \r
4124     if( d_x > s_x ) {\r
4125         d_x += squareSize / 4;\r
4126     }\r
4127     else if( d_x < s_x ) {\r
4128         d_x += 3 * squareSize / 4;\r
4129     }\r
4130     else {\r
4131         d_x += squareSize / 2;\r
4132     }\r
4133 \r
4134     s_x += squareSize / 2;\r
4135     s_y += squareSize / 2;\r
4136 \r
4137     /* Adjust width */\r
4138     A_WIDTH = squareSize / 14;\r
4139 \r
4140     /* Draw */\r
4141     stLB.lbStyle = BS_SOLID;\r
4142     stLB.lbColor = appData.highlightArrowColor;\r
4143     stLB.lbHatch = 0;\r
4144 \r
4145     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4146     holdpen = SelectObject( hdc, hpen );\r
4147     hbrush = CreateBrushIndirect( &stLB );\r
4148     holdbrush = SelectObject( hdc, hbrush );\r
4149 \r
4150     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4151 \r
4152     SelectObject( hdc, holdpen );\r
4153     SelectObject( hdc, holdbrush );\r
4154     DeleteObject( hpen );\r
4155     DeleteObject( hbrush );\r
4156 }\r
4157 \r
4158 BOOL HasHighlightInfo()\r
4159 {\r
4160     BOOL result = FALSE;\r
4161 \r
4162     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4163         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4164     {\r
4165         result = TRUE;\r
4166     }\r
4167 \r
4168     return result;\r
4169 }\r
4170 \r
4171 BOOL IsDrawArrowEnabled()\r
4172 {\r
4173     BOOL result = FALSE;\r
4174 \r
4175     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4176         result = TRUE;\r
4177     }\r
4178 \r
4179     return result;\r
4180 }\r
4181 \r
4182 VOID DrawArrowHighlight( HDC hdc )\r
4183 {\r
4184     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4185         DrawArrowBetweenSquares( hdc,\r
4186             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4187             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4188     }\r
4189 }\r
4190 \r
4191 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4192 {\r
4193     HRGN result = NULL;\r
4194 \r
4195     if( HasHighlightInfo() ) {\r
4196         int x1, y1, x2, y2;\r
4197         int sx, sy, dx, dy;\r
4198 \r
4199         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4200         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4201 \r
4202         sx = MIN( x1, x2 );\r
4203         sy = MIN( y1, y2 );\r
4204         dx = MAX( x1, x2 ) + squareSize;\r
4205         dy = MAX( y1, y2 ) + squareSize;\r
4206 \r
4207         result = CreateRectRgn( sx, sy, dx, dy );\r
4208     }\r
4209 \r
4210     return result;\r
4211 }\r
4212 \r
4213 /*\r
4214     Warning: this function modifies the behavior of several other functions. \r
4215     \r
4216     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4217     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4218     repaint is scattered all over the place, which is not good for features such as\r
4219     "arrow highlighting" that require a full repaint of the board.\r
4220 \r
4221     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4222     user interaction, when speed is not so important) but especially to avoid errors\r
4223     in the displayed graphics.\r
4224 \r
4225     In such patched places, I always try refer to this function so there is a single\r
4226     place to maintain knowledge.\r
4227     \r
4228     To restore the original behavior, just return FALSE unconditionally.\r
4229 */\r
4230 BOOL IsFullRepaintPreferrable()\r
4231 {\r
4232     BOOL result = FALSE;\r
4233 \r
4234     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4235         /* Arrow may appear on the board */\r
4236         result = TRUE;\r
4237     }\r
4238 \r
4239     return result;\r
4240 }\r
4241 \r
4242 /* \r
4243     This function is called by DrawPosition to know whether a full repaint must\r
4244     be forced or not.\r
4245 \r
4246     Only DrawPosition may directly call this function, which makes use of \r
4247     some state information. Other function should call DrawPosition specifying \r
4248     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4249 */\r
4250 BOOL DrawPositionNeedsFullRepaint()\r
4251 {\r
4252     BOOL result = FALSE;\r
4253 \r
4254     /* \r
4255         Probably a slightly better policy would be to trigger a full repaint\r
4256         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4257         but animation is fast enough that it's difficult to notice.\r
4258     */\r
4259     if( animInfo.piece == EmptySquare ) {\r
4260         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4261             result = TRUE;\r
4262         }\r
4263     }\r
4264 \r
4265     return result;\r
4266 }\r
4267 \r
4268 VOID\r
4269 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4270 {\r
4271   int row, column, x, y, square_color, piece_color;\r
4272   ChessSquare piece;\r
4273   HBRUSH oldBrush;\r
4274   HDC texture_hdc = NULL;\r
4275 \r
4276   /* [AS] Initialize background textures if needed */\r
4277   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4278       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4279       if( backTextureSquareSize != squareSize \r
4280        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4281           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4282           backTextureSquareSize = squareSize;\r
4283           RebuildTextureSquareInfo();\r
4284       }\r
4285 \r
4286       texture_hdc = CreateCompatibleDC( hdc );\r
4287   }\r
4288 \r
4289   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4290     for (column = 0; column < BOARD_WIDTH; column++) {\r
4291   \r
4292       SquareToPos(row, column, &x, &y);\r
4293 \r
4294       piece = board[row][column];\r
4295 \r
4296       square_color = ((column + row) % 2) == 1;\r
4297       if( gameInfo.variant == VariantXiangqi ) {\r
4298           square_color = !InPalace(row, column);\r
4299           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4300           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4301       }\r
4302       piece_color = (int) piece < (int) BlackPawn;\r
4303 \r
4304 \r
4305       /* [HGM] holdings file: light square or black */\r
4306       if(column == BOARD_LEFT-2) {\r
4307             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4308                 square_color = 1;\r
4309             else {\r
4310                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4311                 continue;\r
4312             }\r
4313       } else\r
4314       if(column == BOARD_RGHT + 1 ) {\r
4315             if( row < gameInfo.holdingsSize )\r
4316                 square_color = 1;\r
4317             else {\r
4318                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4319                 continue;\r
4320             }\r
4321       }\r
4322       if(column == BOARD_LEFT-1 ) /* left align */\r
4323             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4324       else if( column == BOARD_RGHT) /* right align */\r
4325             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4326       else\r
4327       if (appData.monoMode) {\r
4328         if (piece == EmptySquare) {\r
4329           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4330                  square_color ? WHITENESS : BLACKNESS);\r
4331         } else {\r
4332           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4333         }\r
4334       } \r
4335       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4336           /* [AS] Draw the square using a texture bitmap */\r
4337           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4338           int r = row, c = column; // [HGM] do not flip board in flipView\r
4339           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4340 \r
4341           DrawTile( x, y, \r
4342               squareSize, squareSize, \r
4343               hdc, \r
4344               texture_hdc,\r
4345               backTextureSquareInfo[r][c].mode,\r
4346               backTextureSquareInfo[r][c].x,\r
4347               backTextureSquareInfo[r][c].y );\r
4348 \r
4349           SelectObject( texture_hdc, hbm );\r
4350 \r
4351           if (piece != EmptySquare) {\r
4352               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4353           }\r
4354       }\r
4355       else {\r
4356         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4357 \r
4358         oldBrush = SelectObject(hdc, brush );\r
4359         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4360         SelectObject(hdc, oldBrush);\r
4361         if (piece != EmptySquare)\r
4362           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4363       }\r
4364     }\r
4365   }\r
4366 \r
4367   if( texture_hdc != NULL ) {\r
4368     DeleteDC( texture_hdc );\r
4369   }\r
4370 }\r
4371 \r
4372 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4373 void fputDW(FILE *f, int x)\r
4374 {\r
4375         fputc(x     & 255, f);\r
4376         fputc(x>>8  & 255, f);\r
4377         fputc(x>>16 & 255, f);\r
4378         fputc(x>>24 & 255, f);\r
4379 }\r
4380 \r
4381 #define MAX_CLIPS 200   /* more than enough */\r
4382 \r
4383 VOID\r
4384 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4385 {\r
4386 //  HBITMAP bufferBitmap;\r
4387   BITMAP bi;\r
4388 //  RECT Rect;\r
4389   HDC tmphdc;\r
4390   HBITMAP hbm;\r
4391   int w = 100, h = 50;\r
4392 \r
4393   if(logo == NULL) return;\r
4394 //  GetClientRect(hwndMain, &Rect);\r
4395 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4396 //                                      Rect.bottom-Rect.top+1);\r
4397   tmphdc = CreateCompatibleDC(hdc);\r
4398   hbm = SelectObject(tmphdc, logo);\r
4399   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4400             w = bi.bmWidth;\r
4401             h = bi.bmHeight;\r
4402   }\r
4403   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4404                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4405   SelectObject(tmphdc, hbm);\r
4406   DeleteDC(tmphdc);\r
4407 }\r
4408 \r
4409 VOID\r
4410 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4411 {\r
4412   static Board lastReq, lastDrawn;\r
4413   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4414   static int lastDrawnFlipView = 0;\r
4415   static int lastReqValid = 0, lastDrawnValid = 0;\r
4416   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4417   HDC tmphdc;\r
4418   HDC hdcmem;\r
4419   HBITMAP bufferBitmap;\r
4420   HBITMAP oldBitmap;\r
4421   RECT Rect;\r
4422   HRGN clips[MAX_CLIPS];\r
4423   ChessSquare dragged_piece = EmptySquare;\r
4424 \r
4425   /* I'm undecided on this - this function figures out whether a full\r
4426    * repaint is necessary on its own, so there's no real reason to have the\r
4427    * caller tell it that.  I think this can safely be set to FALSE - but\r
4428    * if we trust the callers not to request full repaints unnessesarily, then\r
4429    * we could skip some clipping work.  In other words, only request a full\r
4430    * redraw when the majority of pieces have changed positions (ie. flip, \r
4431    * gamestart and similar)  --Hawk\r
4432    */\r
4433   Boolean fullrepaint = repaint;\r
4434 \r
4435   if( DrawPositionNeedsFullRepaint() ) {\r
4436       fullrepaint = TRUE;\r
4437   }\r
4438 \r
4439 #if 0\r
4440   if( fullrepaint ) {\r
4441       static int repaint_count = 0;\r
4442       char buf[128];\r
4443 \r
4444       repaint_count++;\r
4445       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4446       OutputDebugString( buf );\r
4447   }\r
4448 #endif\r
4449 \r
4450   if (board == NULL) {\r
4451     if (!lastReqValid) {\r
4452       return;\r
4453     }\r
4454     board = lastReq;\r
4455   } else {\r
4456     CopyBoard(lastReq, board);\r
4457     lastReqValid = 1;\r
4458   }\r
4459 \r
4460   if (doingSizing) {\r
4461     return;\r
4462   }\r
4463 \r
4464   if (IsIconic(hwndMain)) {\r
4465     return;\r
4466   }\r
4467 \r
4468   if (hdc == NULL) {\r
4469     hdc = GetDC(hwndMain);\r
4470     if (!appData.monoMode) {\r
4471       SelectPalette(hdc, hPal, FALSE);\r
4472       RealizePalette(hdc);\r
4473     }\r
4474     releaseDC = TRUE;\r
4475   } else {\r
4476     releaseDC = FALSE;\r
4477   }\r
4478 \r
4479 #if 0\r
4480   fprintf(debugFP, "*******************************\n"\r
4481                    "repaint = %s\n"\r
4482                    "dragInfo.from (%d,%d)\n"\r
4483                    "dragInfo.start (%d,%d)\n"\r
4484                    "dragInfo.pos (%d,%d)\n"\r
4485                    "dragInfo.lastpos (%d,%d)\n", \r
4486                     repaint ? "TRUE" : "FALSE",\r
4487                     dragInfo.from.x, dragInfo.from.y, \r
4488                     dragInfo.start.x, dragInfo.start.y,\r
4489                     dragInfo.pos.x, dragInfo.pos.y,\r
4490                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4491   fprintf(debugFP, "prev:  ");\r
4492   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4493     for (column = 0; column < BOARD_WIDTH; column++) {\r
4494       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4495     }\r
4496   }\r
4497   fprintf(debugFP, "\n");\r
4498   fprintf(debugFP, "board: ");\r
4499   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4500     for (column = 0; column < BOARD_WIDTH; column++) {\r
4501       fprintf(debugFP, "%d ", board[row][column]);\r
4502     }\r
4503   }\r
4504   fprintf(debugFP, "\n");\r
4505   fflush(debugFP);\r
4506 #endif\r
4507 \r
4508   /* Create some work-DCs */\r
4509   hdcmem = CreateCompatibleDC(hdc);\r
4510   tmphdc = CreateCompatibleDC(hdc);\r
4511 \r
4512   /* If dragging is in progress, we temporarely remove the piece */\r
4513   /* [HGM] or temporarily decrease count if stacked              */\r
4514   /*       !! Moved to before board compare !!                   */\r
4515   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4516     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4517     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4518             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4519         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4520     } else \r
4521     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4522             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4523         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4524     } else \r
4525         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4526   }\r
4527 \r
4528   /* Figure out which squares need updating by comparing the \r
4529    * newest board with the last drawn board and checking if\r
4530    * flipping has changed.\r
4531    */\r
4532   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4533     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4534       for (column = 0; column < BOARD_WIDTH; column++) {\r
4535         if (lastDrawn[row][column] != board[row][column]) {\r
4536           SquareToPos(row, column, &x, &y);\r
4537           clips[num_clips++] =\r
4538             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4539         }\r
4540       }\r
4541     }\r
4542     for (i=0; i<2; i++) {\r
4543       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4544           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4545         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4546             lastDrawnHighlight.sq[i].y >= 0) {\r
4547           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4548                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4549           clips[num_clips++] =\r
4550             CreateRectRgn(x - lineGap, y - lineGap, \r
4551                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4552         }\r
4553         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4554           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4555           clips[num_clips++] =\r
4556             CreateRectRgn(x - lineGap, y - lineGap, \r
4557                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4558         }\r
4559       }\r
4560     }\r
4561     for (i=0; i<2; i++) {\r
4562       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4563           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4564         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4565             lastDrawnPremove.sq[i].y >= 0) {\r
4566           SquareToPos(lastDrawnPremove.sq[i].y,\r
4567                       lastDrawnPremove.sq[i].x, &x, &y);\r
4568           clips[num_clips++] =\r
4569             CreateRectRgn(x - lineGap, y - lineGap, \r
4570                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4571         }\r
4572         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4573             premoveHighlightInfo.sq[i].y >= 0) {\r
4574           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4575                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4576           clips[num_clips++] =\r
4577             CreateRectRgn(x - lineGap, y - lineGap, \r
4578                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4579         }\r
4580       }\r
4581     }\r
4582   } else {\r
4583     fullrepaint = TRUE;\r
4584   }\r
4585 \r
4586   /* Create a buffer bitmap - this is the actual bitmap\r
4587    * being written to.  When all the work is done, we can\r
4588    * copy it to the real DC (the screen).  This avoids\r
4589    * the problems with flickering.\r
4590    */\r
4591   GetClientRect(hwndMain, &Rect);\r
4592   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4593                                         Rect.bottom-Rect.top+1);\r
4594   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4595   if (!appData.monoMode) {\r
4596     SelectPalette(hdcmem, hPal, FALSE);\r
4597   }\r
4598 \r
4599   /* Create clips for dragging */\r
4600   if (!fullrepaint) {\r
4601     if (dragInfo.from.x >= 0) {\r
4602       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4603       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4604     }\r
4605     if (dragInfo.start.x >= 0) {\r
4606       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4607       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4608     }\r
4609     if (dragInfo.pos.x >= 0) {\r
4610       x = dragInfo.pos.x - squareSize / 2;\r
4611       y = dragInfo.pos.y - squareSize / 2;\r
4612       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4613     }\r
4614     if (dragInfo.lastpos.x >= 0) {\r
4615       x = dragInfo.lastpos.x - squareSize / 2;\r
4616       y = dragInfo.lastpos.y - squareSize / 2;\r
4617       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4618     }\r
4619   }\r
4620 \r
4621   /* Are we animating a move?  \r
4622    * If so, \r
4623    *   - remove the piece from the board (temporarely)\r
4624    *   - calculate the clipping region\r
4625    */\r
4626   if (!fullrepaint) {\r
4627     if (animInfo.piece != EmptySquare) {\r
4628       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4629       x = boardRect.left + animInfo.lastpos.x;\r
4630       y = boardRect.top + animInfo.lastpos.y;\r
4631       x2 = boardRect.left + animInfo.pos.x;\r
4632       y2 = boardRect.top + animInfo.pos.y;\r
4633       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4634       /* Slight kludge.  The real problem is that after AnimateMove is\r
4635          done, the position on the screen does not match lastDrawn.\r
4636          This currently causes trouble only on e.p. captures in\r
4637          atomic, where the piece moves to an empty square and then\r
4638          explodes.  The old and new positions both had an empty square\r
4639          at the destination, but animation has drawn a piece there and\r
4640          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4641       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4642     }\r
4643   }\r
4644 \r
4645   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4646   if (num_clips == 0)\r
4647     fullrepaint = TRUE;\r
4648 \r
4649   /* Set clipping on the memory DC */\r
4650   if (!fullrepaint) {\r
4651     SelectClipRgn(hdcmem, clips[0]);\r
4652     for (x = 1; x < num_clips; x++) {\r
4653       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4654         abort();  // this should never ever happen!\r
4655     }\r
4656   }\r
4657 \r
4658   /* Do all the drawing to the memory DC */\r
4659   if(explodeInfo.radius) { // [HGM] atomic\r
4660         HBRUSH oldBrush;\r
4661         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4662         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4663         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4664         x += squareSize/2;\r
4665         y += squareSize/2;\r
4666         if(!fullrepaint) {\r
4667           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4668           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4669         }\r
4670         DrawGridOnDC(hdcmem);\r
4671         DrawHighlightsOnDC(hdcmem);\r
4672         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4673         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4674         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4675         SelectObject(hdcmem, oldBrush);\r
4676   } else {\r
4677     DrawGridOnDC(hdcmem);\r
4678     DrawHighlightsOnDC(hdcmem);\r
4679     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4680   }\r
4681   if(logoHeight) {\r
4682         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4683         if(appData.autoLogo) {\r
4684           \r
4685           switch(gameMode) { // pick logos based on game mode\r
4686             case IcsObserving:\r
4687                 whiteLogo = second.programLogo; // ICS logo\r
4688                 blackLogo = second.programLogo;\r
4689             default:\r
4690                 break;\r
4691             case IcsPlayingWhite:\r
4692                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4693                 blackLogo = second.programLogo; // ICS logo\r
4694                 break;\r
4695             case IcsPlayingBlack:\r
4696                 whiteLogo = second.programLogo; // ICS logo\r
4697                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4698                 break;\r
4699             case TwoMachinesPlay:\r
4700                 if(first.twoMachinesColor[0] == 'b') {\r
4701                     whiteLogo = second.programLogo;\r
4702                     blackLogo = first.programLogo;\r
4703                 }\r
4704                 break;\r
4705             case MachinePlaysWhite:\r
4706                 blackLogo = userLogo;\r
4707                 break;\r
4708             case MachinePlaysBlack:\r
4709                 whiteLogo = userLogo;\r
4710                 blackLogo = first.programLogo;\r
4711           }\r
4712         }\r
4713         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4714         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4715   }\r
4716 \r
4717   if( appData.highlightMoveWithArrow ) {\r
4718     DrawArrowHighlight(hdcmem);\r
4719   }\r
4720 \r
4721   DrawCoordsOnDC(hdcmem);\r
4722 \r
4723   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4724                  /* to make sure lastDrawn contains what is actually drawn */\r
4725 \r
4726   /* Put the dragged piece back into place and draw it (out of place!) */\r
4727     if (dragged_piece != EmptySquare) {\r
4728     /* [HGM] or restack */\r
4729     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4730                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4731     else\r
4732     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4733                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4734     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4735     x = dragInfo.pos.x - squareSize / 2;\r
4736     y = dragInfo.pos.y - squareSize / 2;\r
4737     DrawPieceOnDC(hdcmem, dragged_piece,\r
4738                   ((int) dragged_piece < (int) BlackPawn), \r
4739                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4740   }   \r
4741   \r
4742   /* Put the animated piece back into place and draw it */\r
4743   if (animInfo.piece != EmptySquare) {\r
4744     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4745     x = boardRect.left + animInfo.pos.x;\r
4746     y = boardRect.top + animInfo.pos.y;\r
4747     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4748                   ((int) animInfo.piece < (int) BlackPawn),\r
4749                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4750   }\r
4751 \r
4752   /* Release the bufferBitmap by selecting in the old bitmap \r
4753    * and delete the memory DC\r
4754    */\r
4755   SelectObject(hdcmem, oldBitmap);\r
4756   DeleteDC(hdcmem);\r
4757 \r
4758   /* Set clipping on the target DC */\r
4759   if (!fullrepaint) {\r
4760     SelectClipRgn(hdc, clips[0]);\r
4761     for (x = 1; x < num_clips; x++) {\r
4762       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4763         abort();   // this should never ever happen!\r
4764     } \r
4765   }\r
4766 \r
4767   /* Copy the new bitmap onto the screen in one go.\r
4768    * This way we avoid any flickering\r
4769    */\r
4770   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4771   BitBlt(hdc, boardRect.left, boardRect.top,\r
4772          boardRect.right - boardRect.left,\r
4773          boardRect.bottom - boardRect.top,\r
4774          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4775   if(saveDiagFlag) { \r
4776     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4777     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4778 \r
4779     GetObject(bufferBitmap, sizeof(b), &b);\r
4780     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4781         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4782         bih.biWidth = b.bmWidth;\r
4783         bih.biHeight = b.bmHeight;\r
4784         bih.biPlanes = 1;\r
4785         bih.biBitCount = b.bmBitsPixel;\r
4786         bih.biCompression = 0;\r
4787         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4788         bih.biXPelsPerMeter = 0;\r
4789         bih.biYPelsPerMeter = 0;\r
4790         bih.biClrUsed = 0;\r
4791         bih.biClrImportant = 0;\r
4792 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4793 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4794         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4795 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4796 \r
4797 #if 1\r
4798         wb = b.bmWidthBytes;\r
4799         // count colors\r
4800         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4801                 int k = ((int*) pData)[i];\r
4802                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4803                 if(j >= 16) break;\r
4804                 color[j] = k;\r
4805                 if(j >= nrColors) nrColors = j+1;\r
4806         }\r
4807         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4808                 INT p = 0;\r
4809                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4810                     for(w=0; w<(wb>>2); w+=2) {\r
4811                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4812                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4813                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4814                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4815                         pData[p++] = m | j<<4;\r
4816                     }\r
4817                     while(p&3) pData[p++] = 0;\r
4818                 }\r
4819                 fac = 3;\r
4820                 wb = ((wb+31)>>5)<<2;\r
4821         }\r
4822         // write BITMAPFILEHEADER\r
4823         fprintf(diagFile, "BM");\r
4824         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4825         fputDW(diagFile, 0);\r
4826         fputDW(diagFile, 0x36 + (fac?64:0));\r
4827         // write BITMAPINFOHEADER\r
4828         fputDW(diagFile, 40);\r
4829         fputDW(diagFile, b.bmWidth);\r
4830         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4831         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4832         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4833         fputDW(diagFile, 0);\r
4834         fputDW(diagFile, 0);\r
4835         fputDW(diagFile, 0);\r
4836         fputDW(diagFile, 0);\r
4837         fputDW(diagFile, 0);\r
4838         fputDW(diagFile, 0);\r
4839         // write color table\r
4840         if(fac)\r
4841         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4842         // write bitmap data\r
4843         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4844                 fputc(pData[i], diagFile);\r
4845 #endif\r
4846      }\r
4847   }\r
4848 \r
4849   SelectObject(tmphdc, oldBitmap);\r
4850 \r
4851   /* Massive cleanup */\r
4852   for (x = 0; x < num_clips; x++)\r
4853     DeleteObject(clips[x]);\r
4854 \r
4855   DeleteDC(tmphdc);\r
4856   DeleteObject(bufferBitmap);\r
4857 \r
4858   if (releaseDC) \r
4859     ReleaseDC(hwndMain, hdc);\r
4860   \r
4861   if (lastDrawnFlipView != flipView) {\r
4862     if (flipView)\r
4863       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4864     else\r
4865       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4866   }\r
4867 \r
4868 /*  CopyBoard(lastDrawn, board);*/\r
4869   lastDrawnHighlight = highlightInfo;\r
4870   lastDrawnPremove   = premoveHighlightInfo;\r
4871   lastDrawnFlipView = flipView;\r
4872   lastDrawnValid = 1;\r
4873 }\r
4874 \r
4875 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4876 int\r
4877 SaveDiagram(f)\r
4878      FILE *f;\r
4879 {\r
4880     saveDiagFlag = 1; diagFile = f;\r
4881     HDCDrawPosition(NULL, TRUE, NULL);\r
4882 \r
4883     saveDiagFlag = 0;\r
4884 \r
4885 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4886     \r
4887     fclose(f);\r
4888     return TRUE;\r
4889 }\r
4890 \r
4891 \r
4892 /*---------------------------------------------------------------------------*\\r
4893 | CLIENT PAINT PROCEDURE\r
4894 |   This is the main event-handler for the WM_PAINT message.\r
4895 |\r
4896 \*---------------------------------------------------------------------------*/\r
4897 VOID\r
4898 PaintProc(HWND hwnd)\r
4899 {\r
4900   HDC         hdc;\r
4901   PAINTSTRUCT ps;\r
4902   HFONT       oldFont;\r
4903 \r
4904   if((hdc = BeginPaint(hwnd, &ps))) {\r
4905     if (IsIconic(hwnd)) {\r
4906       DrawIcon(hdc, 2, 2, iconCurrent);\r
4907     } else {\r
4908       if (!appData.monoMode) {\r
4909         SelectPalette(hdc, hPal, FALSE);\r
4910         RealizePalette(hdc);\r
4911       }\r
4912       HDCDrawPosition(hdc, 1, NULL);\r
4913       oldFont =\r
4914         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4915       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4916                  ETO_CLIPPED|ETO_OPAQUE,\r
4917                  &messageRect, messageText, strlen(messageText), NULL);\r
4918       SelectObject(hdc, oldFont);\r
4919       DisplayBothClocks();\r
4920     }\r
4921     EndPaint(hwnd,&ps);\r
4922   }\r
4923 \r
4924   return;\r
4925 }\r
4926 \r
4927 \r
4928 /*\r
4929  * If the user selects on a border boundary, return -1; if off the board,\r
4930  *   return -2.  Otherwise map the event coordinate to the square.\r
4931  * The offset boardRect.left or boardRect.top must already have been\r
4932  *   subtracted from x.\r
4933  */\r
4934 int\r
4935 EventToSquare(int x)\r
4936 {\r
4937   if (x <= 0)\r
4938     return -2;\r
4939   if (x < lineGap)\r
4940     return -1;\r
4941   x -= lineGap;\r
4942   if ((x % (squareSize + lineGap)) >= squareSize)\r
4943     return -1;\r
4944   x /= (squareSize + lineGap);\r
4945   if (x >= BOARD_SIZE)\r
4946     return -2;\r
4947   return x;\r
4948 }\r
4949 \r
4950 typedef struct {\r
4951   char piece;\r
4952   int command;\r
4953   char* name;\r
4954 } DropEnable;\r
4955 \r
4956 DropEnable dropEnables[] = {\r
4957   { 'P', DP_Pawn, "Pawn" },\r
4958   { 'N', DP_Knight, "Knight" },\r
4959   { 'B', DP_Bishop, "Bishop" },\r
4960   { 'R', DP_Rook, "Rook" },\r
4961   { 'Q', DP_Queen, "Queen" },\r
4962 };\r
4963 \r
4964 VOID\r
4965 SetupDropMenu(HMENU hmenu)\r
4966 {\r
4967   int i, count, enable;\r
4968   char *p;\r
4969   extern char white_holding[], black_holding[];\r
4970   char item[MSG_SIZ];\r
4971 \r
4972   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4973     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4974                dropEnables[i].piece);\r
4975     count = 0;\r
4976     while (p && *p++ == dropEnables[i].piece) count++;\r
4977     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4978     enable = count > 0 || !appData.testLegality\r
4979       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4980                       && !appData.icsActive);\r
4981     ModifyMenu(hmenu, dropEnables[i].command,\r
4982                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4983                dropEnables[i].command, item);\r
4984   }\r
4985 }\r
4986 \r
4987 /* Event handler for mouse messages */\r
4988 VOID\r
4989 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4990 {\r
4991   int x, y;\r
4992   POINT pt;\r
4993   static int recursive = 0;\r
4994   HMENU hmenu;\r
4995 //  BOOLEAN needsRedraw = FALSE;\r
4996   BOOLEAN saveAnimate;\r
4997   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4998   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4999   ChessMove moveType;\r
5000 \r
5001   if (recursive) {\r
5002     if (message == WM_MBUTTONUP) {\r
5003       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
5004          to the middle button: we simulate pressing the left button too!\r
5005          */\r
5006       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
5007       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
5008     }\r
5009     return;\r
5010   }\r
5011   recursive++;\r
5012   \r
5013   pt.x = LOWORD(lParam);\r
5014   pt.y = HIWORD(lParam);\r
5015   x = EventToSquare(pt.x - boardRect.left);\r
5016   y = EventToSquare(pt.y - boardRect.top);\r
5017   if (!flipView && y >= 0) {\r
5018     y = BOARD_HEIGHT - 1 - y;\r
5019   }\r
5020   if (flipView && x >= 0) {\r
5021     x = BOARD_WIDTH - 1 - x;\r
5022   }\r
5023 \r
5024   switch (message) {\r
5025   case WM_LBUTTONDOWN:\r
5026     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
5027         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
5028         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
5029         if(gameInfo.holdingsWidth && \r
5030                 (WhiteOnMove(currentMove) \r
5031                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
5032                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
5033             // click in right holdings, for determining promotion piece\r
5034             ChessSquare p = boards[currentMove][y][x];\r
5035             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
5036             if(p != EmptySquare) {\r
5037                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
5038                 fromX = fromY = -1;\r
5039                 break;\r
5040             }\r
5041         }\r
5042         DrawPosition(FALSE, boards[currentMove]);\r
5043         break;\r
5044     }\r
5045     ErrorPopDown();\r
5046     sameAgain = FALSE;\r
5047     if (y == -2) {\r
5048       /* Downclick vertically off board; check if on clock */\r
5049       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5050         if (gameMode == EditPosition) {\r
5051           SetWhiteToPlayEvent();\r
5052         } else if (gameMode == IcsPlayingBlack ||\r
5053                    gameMode == MachinePlaysWhite) {\r
5054           CallFlagEvent();\r
5055         } else if (gameMode == EditGame) {\r
5056           AdjustClock(flipClock, -1);\r
5057         }\r
5058       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5059         if (gameMode == EditPosition) {\r
5060           SetBlackToPlayEvent();\r
5061         } else if (gameMode == IcsPlayingWhite ||\r
5062                    gameMode == MachinePlaysBlack) {\r
5063           CallFlagEvent();\r
5064         } else if (gameMode == EditGame) {\r
5065           AdjustClock(!flipClock, -1);\r
5066         }\r
5067       }\r
5068       if (!appData.highlightLastMove) {\r
5069         ClearHighlights();\r
5070         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
5071       }\r
5072       fromX = fromY = -1;\r
5073       dragInfo.start.x = dragInfo.start.y = -1;\r
5074       dragInfo.from = dragInfo.start;\r
5075       break;\r
5076     } else if (x < 0 || y < 0\r
5077       /* [HGM] block clicks between board and holdings */\r
5078               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
5079               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
5080               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
5081         /* EditPosition, empty square, or different color piece;\r
5082            click-click move is possible */\r
5083                                ) {\r
5084       break;\r
5085     } else if (fromX == x && fromY == y) {\r
5086       /* Downclick on same square again */\r
5087       ClearHighlights();\r
5088       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5089       sameAgain = TRUE;  \r
5090     } else if (fromX != -1 &&\r
5091                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5092                                                                         ) {\r
5093       /* Downclick on different square. */\r
5094       /* [HGM] if on holdings file, should count as new first click ! */\r
5095       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5096         toX = x;\r
5097         toY = y;\r
5098         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5099            to make sure move is legal before showing promotion popup */\r
5100         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5101         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5102                 fromX = fromY = -1; \r
5103                 ClearHighlights();\r
5104                 DrawPosition(FALSE, boards[currentMove]);\r
5105                 break; \r
5106         } else \r
5107         if(moveType != ImpossibleMove) {\r
5108           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5109           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5110             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5111               appData.alwaysPromoteToQueen)) {\r
5112                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5113                   if (!appData.highlightLastMove) {\r
5114                       ClearHighlights();\r
5115                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5116                   }\r
5117           } else\r
5118           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5119                   SetHighlights(fromX, fromY, toX, toY);\r
5120                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5121                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5122                      If promotion to Q is legal, all are legal! */\r
5123                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5124                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5125                     // kludge to temporarily execute move on display, wthout promotng yet\r
5126                     promotionChoice = TRUE;\r
5127                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5128                     boards[currentMove][toY][toX] = p;\r
5129                     DrawPosition(FALSE, boards[currentMove]);\r
5130                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5131                     boards[currentMove][toY][toX] = q;\r
5132                   } else\r
5133                   PromotionPopup(hwnd);\r
5134           } else {       /* not a promotion */\r
5135              if (appData.animate || appData.highlightLastMove) {\r
5136                  SetHighlights(fromX, fromY, toX, toY);\r
5137              } else {\r
5138                  ClearHighlights();\r
5139              }\r
5140              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5141              fromX = fromY = -1;\r
5142              if (appData.animate && !appData.highlightLastMove) {\r
5143                   ClearHighlights();\r
5144                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5145              }\r
5146           }\r
5147           break;\r
5148         }\r
5149         if (gotPremove) {\r
5150             /* [HGM] it seemed that braces were missing here */\r
5151             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5152             fromX = fromY = -1;\r
5153             break;\r
5154         }\r
5155       }\r
5156       ClearHighlights();\r
5157       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5158     }\r
5159     /* First downclick, or restart on a square with same color piece */\r
5160     if (!frozen && OKToStartUserMove(x, y)) {\r
5161       fromX = x;\r
5162       fromY = y;\r
5163       dragInfo.lastpos = pt;\r
5164       dragInfo.from.x = fromX;\r
5165       dragInfo.from.y = fromY;\r
5166       dragInfo.start = dragInfo.from;\r
5167       SetCapture(hwndMain);\r
5168     } else {\r
5169       fromX = fromY = -1;\r
5170       dragInfo.start.x = dragInfo.start.y = -1;\r
5171       dragInfo.from = dragInfo.start;\r
5172       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5173     }\r
5174     break;\r
5175 \r
5176   case WM_LBUTTONUP:\r
5177     ReleaseCapture();\r
5178     if (fromX == -1) break;\r
5179     if (x == fromX && y == fromY) {\r
5180       dragInfo.from.x = dragInfo.from.y = -1;\r
5181       /* Upclick on same square */\r
5182       if (sameAgain) {\r
5183         /* Clicked same square twice: abort click-click move */\r
5184         fromX = fromY = -1;\r
5185         gotPremove = 0;\r
5186         ClearPremoveHighlights();\r
5187       } else {\r
5188         /* First square clicked: start click-click move */\r
5189         SetHighlights(fromX, fromY, -1, -1);\r
5190       }\r
5191       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5192     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5193       /* Errant click; ignore */\r
5194       break;\r
5195     } else {\r
5196       /* Finish drag move. */\r
5197     if (appData.debugMode) {\r
5198         fprintf(debugFP, "release\n");\r
5199     }\r
5200       dragInfo.from.x = dragInfo.from.y = -1;\r
5201       toX = x;\r
5202       toY = y;\r
5203       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5204       appData.animate = appData.animate && !appData.animateDragging;\r
5205       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5206       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5207                 fromX = fromY = -1; \r
5208                 ClearHighlights();\r
5209                 DrawPosition(FALSE, boards[currentMove]);\r
5210                 appData.animate = saveAnimate;\r
5211                 break; \r
5212       } else \r
5213       if(moveType != ImpossibleMove) {\r
5214           /* [HGM] use move type to determine if move is promotion.\r
5215              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5216           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5217             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5218               appData.alwaysPromoteToQueen)) \r
5219                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5220           else \r
5221           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5222                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5223                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5224                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5225                     // kludge to temporarily execute move on display, wthout promotng yet\r
5226                     promotionChoice = TRUE;\r
5227                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5228                     boards[currentMove][toY][toX] = p;\r
5229                     DrawPosition(FALSE, boards[currentMove]);\r
5230                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5231                     boards[currentMove][toY][toX] = q;\r
5232                     appData.animate = saveAnimate;\r
5233                     break;\r
5234                   } else\r
5235                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5236           } else {\r
5237             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5238                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5239                                         moveType == WhiteCapturesEnPassant || \r
5240                                         moveType == BlackCapturesEnPassant   ) )\r
5241                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5242             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5243           }\r
5244       }\r
5245       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5246       appData.animate = saveAnimate;\r
5247       fromX = fromY = -1;\r
5248       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5249         ClearHighlights();\r
5250       }\r
5251       if (appData.animate || appData.animateDragging ||\r
5252           appData.highlightDragging || gotPremove) {\r
5253         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5254       }\r
5255     }\r
5256     dragInfo.start.x = dragInfo.start.y = -1; \r
5257     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5258     break;\r
5259 \r
5260   case WM_MOUSEMOVE:\r
5261     if ((appData.animateDragging || appData.highlightDragging)\r
5262         && (wParam & MK_LBUTTON)\r
5263         && dragInfo.from.x >= 0) \r
5264     {\r
5265       BOOL full_repaint = FALSE;\r
5266 \r
5267       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5268       if (appData.animateDragging) {\r
5269         dragInfo.pos = pt;\r
5270       }\r
5271       if (appData.highlightDragging) {\r
5272         SetHighlights(fromX, fromY, x, y);\r
5273         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5274             full_repaint = TRUE;\r
5275         }\r
5276       }\r
5277       \r
5278       DrawPosition( full_repaint, NULL);\r
5279       \r
5280       dragInfo.lastpos = dragInfo.pos;\r
5281     }\r
5282     break;\r
5283 \r
5284   case WM_MOUSEWHEEL: // [DM]\r
5285     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5286        /* Mouse Wheel is being rolled forward\r
5287         * Play moves forward\r
5288         */\r
5289        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5290                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5291        /* Mouse Wheel is being rolled backward\r
5292         * Play moves backward\r
5293         */\r
5294        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5295                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5296     }\r
5297     break;\r
5298 \r
5299   case WM_MBUTTONDOWN:\r
5300   case WM_RBUTTONDOWN:\r
5301     ErrorPopDown();\r
5302     ReleaseCapture();\r
5303     fromX = fromY = -1;\r
5304     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5305     dragInfo.start.x = dragInfo.start.y = -1;\r
5306     dragInfo.from = dragInfo.start;\r
5307     dragInfo.lastpos = dragInfo.pos;\r
5308     if (appData.highlightDragging) {\r
5309       ClearHighlights();\r
5310     }\r
5311     if(y == -2) {\r
5312       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5313       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5314           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5315       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5316           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5317       }\r
5318     }\r
5319     DrawPosition(TRUE, NULL);\r
5320 \r
5321     switch (gameMode) {\r
5322     case EditPosition:\r
5323     case IcsExamining:\r
5324       if (x < 0 || y < 0) break;\r
5325       fromX = x;\r
5326       fromY = y;\r
5327       if (message == WM_MBUTTONDOWN) {\r
5328         buttonCount = 3;  /* even if system didn't think so */\r
5329         if (wParam & MK_SHIFT) \r
5330           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5331         else\r
5332           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5333       } else { /* message == WM_RBUTTONDOWN */\r
5334 #if 0\r
5335         if (buttonCount == 3) {\r
5336           if (wParam & MK_SHIFT) \r
5337             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5338           else\r
5339             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5340         } else {\r
5341           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5342         }\r
5343 #else\r
5344         /* Just have one menu, on the right button.  Windows users don't\r
5345            think to try the middle one, and sometimes other software steals\r
5346            it, or it doesn't really exist. */\r
5347         if(gameInfo.variant != VariantShogi)\r
5348             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5349         else\r
5350             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5351 #endif\r
5352       }\r
5353       break;\r
5354     case IcsPlayingWhite:\r
5355     case IcsPlayingBlack:\r
5356     case EditGame:\r
5357     case MachinePlaysWhite:\r
5358     case MachinePlaysBlack:\r
5359       if (appData.testLegality &&\r
5360           gameInfo.variant != VariantBughouse &&\r
5361           gameInfo.variant != VariantCrazyhouse) break;\r
5362       if (x < 0 || y < 0) break;\r
5363       fromX = x;\r
5364       fromY = y;\r
5365       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5366       SetupDropMenu(hmenu);\r
5367       MenuPopup(hwnd, pt, hmenu, -1);\r
5368       break;\r
5369     default:\r
5370       break;\r
5371     }\r
5372     break;\r
5373   }\r
5374 \r
5375   recursive--;\r
5376 }\r
5377 \r
5378 /* Preprocess messages for buttons in main window */\r
5379 LRESULT CALLBACK\r
5380 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5381 {\r
5382   int id = GetWindowLong(hwnd, GWL_ID);\r
5383   int i, dir;\r
5384 \r
5385   for (i=0; i<N_BUTTONS; i++) {\r
5386     if (buttonDesc[i].id == id) break;\r
5387   }\r
5388   if (i == N_BUTTONS) return 0;\r
5389   switch (message) {\r
5390   case WM_KEYDOWN:\r
5391     switch (wParam) {\r
5392     case VK_LEFT:\r
5393     case VK_RIGHT:\r
5394       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5395       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5396       return TRUE;\r
5397     }\r
5398     break;\r
5399   case WM_CHAR:\r
5400     switch (wParam) {\r
5401     case '\r':\r
5402       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5403       return TRUE;\r
5404     default:\r
5405       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5406         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5407         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5408         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5409         SetFocus(h);\r
5410         SendMessage(h, WM_CHAR, wParam, lParam);\r
5411         return TRUE;\r
5412       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5413         PopUpMoveDialog((char)wParam);\r
5414       }\r
5415       break;\r
5416     }\r
5417     break;\r
5418   }\r
5419   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5420 }\r
5421 \r
5422 /* Process messages for Promotion dialog box */\r
5423 LRESULT CALLBACK\r
5424 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5425 {\r
5426   char promoChar;\r
5427 \r
5428   switch (message) {\r
5429   case WM_INITDIALOG: /* message: initialize dialog box */\r
5430     /* Center the dialog over the application window */\r
5431     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5432     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5433       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5434        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5435                SW_SHOW : SW_HIDE);\r
5436     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5437     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5438        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5439          PieceToChar(WhiteAngel) != '~') ||\r
5440         (PieceToChar(BlackAngel) >= 'A' &&\r
5441          PieceToChar(BlackAngel) != '~')   ) ?\r
5442                SW_SHOW : SW_HIDE);\r
5443     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5444        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5445          PieceToChar(WhiteMarshall) != '~') ||\r
5446         (PieceToChar(BlackMarshall) >= 'A' &&\r
5447          PieceToChar(BlackMarshall) != '~')   ) ?\r
5448                SW_SHOW : SW_HIDE);\r
5449     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5450     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5451        gameInfo.variant != VariantShogi ?\r
5452                SW_SHOW : SW_HIDE);\r
5453     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5454        gameInfo.variant != VariantShogi ?\r
5455                SW_SHOW : SW_HIDE);\r
5456     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5457        gameInfo.variant == VariantShogi ?\r
5458                SW_SHOW : SW_HIDE);\r
5459     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5460        gameInfo.variant == VariantShogi ?\r
5461                SW_SHOW : SW_HIDE);\r
5462     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5463        gameInfo.variant == VariantSuper ?\r
5464                SW_SHOW : SW_HIDE);\r
5465     return TRUE;\r
5466 \r
5467   case WM_COMMAND: /* message: received a command */\r
5468     switch (LOWORD(wParam)) {\r
5469     case IDCANCEL:\r
5470       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5471       ClearHighlights();\r
5472       DrawPosition(FALSE, NULL);\r
5473       return TRUE;\r
5474     case PB_King:\r
5475       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5476       break;\r
5477     case PB_Queen:\r
5478       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5479       break;\r
5480     case PB_Rook:\r
5481       promoChar = PieceToChar(BlackRook);\r
5482       break;\r
5483     case PB_Bishop:\r
5484       promoChar = PieceToChar(BlackBishop);\r
5485       break;\r
5486     case PB_Chancellor:\r
5487       promoChar = PieceToChar(BlackMarshall);\r
5488       break;\r
5489     case PB_Archbishop:\r
5490       promoChar = PieceToChar(BlackAngel);\r
5491       break;\r
5492     case PB_Knight:\r
5493       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5494       break;\r
5495     default:\r
5496       return FALSE;\r
5497     }\r
5498     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5499     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5500        only show the popup when we are already sure the move is valid or\r
5501        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5502        will figure out it is a promotion from the promoChar. */\r
5503     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5504     if (!appData.highlightLastMove) {\r
5505       ClearHighlights();\r
5506       DrawPosition(FALSE, NULL);\r
5507     }\r
5508     return TRUE;\r
5509   }\r
5510   return FALSE;\r
5511 }\r
5512 \r
5513 /* Pop up promotion dialog */\r
5514 VOID\r
5515 PromotionPopup(HWND hwnd)\r
5516 {\r
5517   FARPROC lpProc;\r
5518 \r
5519   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5520   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5521     hwnd, (DLGPROC)lpProc);\r
5522   FreeProcInstance(lpProc);\r
5523 }\r
5524 \r
5525 /* Toggle ShowThinking */\r
5526 VOID\r
5527 ToggleShowThinking()\r
5528 {\r
5529   appData.showThinking = !appData.showThinking;\r
5530   ShowThinkingEvent();\r
5531 }\r
5532 \r
5533 VOID\r
5534 LoadGameDialog(HWND hwnd, char* title)\r
5535 {\r
5536   UINT number = 0;\r
5537   FILE *f;\r
5538   char fileTitle[MSG_SIZ];\r
5539   f = OpenFileDialog(hwnd, "rb", "",\r
5540                      appData.oldSaveStyle ? "gam" : "pgn",\r
5541                      GAME_FILT,\r
5542                      title, &number, fileTitle, NULL);\r
5543   if (f != NULL) {\r
5544     cmailMsgLoaded = FALSE;\r
5545     if (number == 0) {\r
5546       int error = GameListBuild(f);\r
5547       if (error) {\r
5548         DisplayError("Cannot build game list", error);\r
5549       } else if (!ListEmpty(&gameList) &&\r
5550                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5551         GameListPopUp(f, fileTitle);\r
5552         return;\r
5553       }\r
5554       GameListDestroy();\r
5555       number = 1;\r
5556     }\r
5557     LoadGame(f, number, fileTitle, FALSE);\r
5558   }\r
5559 }\r
5560 \r
5561 VOID\r
5562 ChangedConsoleFont()\r
5563 {\r
5564   CHARFORMAT cfmt;\r
5565   CHARRANGE tmpsel, sel;\r
5566   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5567   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5568   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5569   PARAFORMAT paraf;\r
5570 \r
5571   cfmt.cbSize = sizeof(CHARFORMAT);\r
5572   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5573   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5574   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5575    * size.  This was undocumented in the version of MSVC++ that I had\r
5576    * when I wrote the code, but is apparently documented now.\r
5577    */\r
5578   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5579   cfmt.bCharSet = f->lf.lfCharSet;\r
5580   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5581   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5582   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5583   /* Why are the following seemingly needed too? */\r
5584   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5585   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5586   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5587   tmpsel.cpMin = 0;\r
5588   tmpsel.cpMax = -1; /*999999?*/\r
5589   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5590   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5591   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5592    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5593    */\r
5594   paraf.cbSize = sizeof(paraf);\r
5595   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5596   paraf.dxStartIndent = 0;\r
5597   paraf.dxOffset = WRAP_INDENT;\r
5598   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5599   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5600 }\r
5601 \r
5602 /*---------------------------------------------------------------------------*\\r
5603  *\r
5604  * Window Proc for main window\r
5605  *\r
5606 \*---------------------------------------------------------------------------*/\r
5607 \r
5608 /* Process messages for main window, etc. */\r
5609 LRESULT CALLBACK\r
5610 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5611 {\r
5612   FARPROC lpProc;\r
5613   int wmId, wmEvent;\r
5614   char *defName;\r
5615   FILE *f;\r
5616   UINT number;\r
5617   char fileTitle[MSG_SIZ];\r
5618   char buf[MSG_SIZ];\r
5619   static SnapData sd;\r
5620 \r
5621   switch (message) {\r
5622 \r
5623   case WM_PAINT: /* message: repaint portion of window */\r
5624     PaintProc(hwnd);\r
5625     break;\r
5626 \r
5627   case WM_ERASEBKGND:\r
5628     if (IsIconic(hwnd)) {\r
5629       /* Cheat; change the message */\r
5630       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5631     } else {\r
5632       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5633     }\r
5634     break;\r
5635 \r
5636   case WM_LBUTTONDOWN:\r
5637   case WM_MBUTTONDOWN:\r
5638   case WM_RBUTTONDOWN:\r
5639   case WM_LBUTTONUP:\r
5640   case WM_MBUTTONUP:\r
5641   case WM_RBUTTONUP:\r
5642   case WM_MOUSEMOVE:\r
5643   case WM_MOUSEWHEEL:\r
5644     MouseEvent(hwnd, message, wParam, lParam);\r
5645     break;\r
5646 \r
5647   JAWS_KB_NAVIGATION\r
5648 \r
5649   case WM_CHAR:\r
5650     \r
5651     JAWS_ALT_INTERCEPT\r
5652 \r
5653     if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) { \r
5654         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5655         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5656         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5657         SetFocus(h);\r
5658         SendMessage(h, message, wParam, lParam);\r
5659     } else if(lParam != KF_REPEAT) {\r
5660         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5661                 PopUpMoveDialog((char)wParam);\r
5662         } else if((char)wParam == 003) CopyGameToClipboard();\r
5663          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5664     }\r
5665 \r
5666     break;\r
5667 \r
5668   case WM_PALETTECHANGED:\r
5669     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5670       int nnew;\r
5671       HDC hdc = GetDC(hwndMain);\r
5672       SelectPalette(hdc, hPal, TRUE);\r
5673       nnew = RealizePalette(hdc);\r
5674       if (nnew > 0) {\r
5675         paletteChanged = TRUE;\r
5676 #if 0\r
5677         UpdateColors(hdc);\r
5678 #else\r
5679         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5680 #endif\r
5681       }\r
5682       ReleaseDC(hwnd, hdc);\r
5683     }\r
5684     break;\r
5685 \r
5686   case WM_QUERYNEWPALETTE:\r
5687     if (!appData.monoMode /*&& paletteChanged*/) {\r
5688       int nnew;\r
5689       HDC hdc = GetDC(hwndMain);\r
5690       paletteChanged = FALSE;\r
5691       SelectPalette(hdc, hPal, FALSE);\r
5692       nnew = RealizePalette(hdc);\r
5693       if (nnew > 0) {\r
5694         InvalidateRect(hwnd, &boardRect, FALSE);\r
5695       }\r
5696       ReleaseDC(hwnd, hdc);\r
5697       return TRUE;\r
5698     }\r
5699     return FALSE;\r
5700 \r
5701   case WM_COMMAND: /* message: command from application menu */\r
5702     wmId    = LOWORD(wParam);\r
5703     wmEvent = HIWORD(wParam);\r
5704 \r
5705     switch (wmId) {\r
5706     case IDM_NewGame:\r
5707       ResetGameEvent();\r
5708       AnalysisPopDown();\r
5709       SAY("new game enter a move to play against the computer with white");\r
5710       break;\r
5711 \r
5712     case IDM_NewGameFRC:\r
5713       if( NewGameFRC() == 0 ) {\r
5714         ResetGameEvent();\r
5715         AnalysisPopDown();\r
5716       }\r
5717       break;\r
5718 \r
5719     case IDM_NewVariant:\r
5720       NewVariantPopup(hwnd);\r
5721       break;\r
5722 \r
5723     case IDM_LoadGame:\r
5724       LoadGameDialog(hwnd, "Load Game from File");\r
5725       break;\r
5726 \r
5727     case IDM_LoadNextGame:\r
5728       ReloadGame(1);\r
5729       break;\r
5730 \r
5731     case IDM_LoadPrevGame:\r
5732       ReloadGame(-1);\r
5733       break;\r
5734 \r
5735     case IDM_ReloadGame:\r
5736       ReloadGame(0);\r
5737       break;\r
5738 \r
5739     case IDM_LoadPosition:\r
5740       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5741         Reset(FALSE, TRUE);\r
5742       }\r
5743       number = 1;\r
5744       f = OpenFileDialog(hwnd, "rb", "",\r
5745                          appData.oldSaveStyle ? "pos" : "fen",\r
5746                          POSITION_FILT,\r
5747                          "Load Position from File", &number, fileTitle, NULL);\r
5748       if (f != NULL) {\r
5749         LoadPosition(f, number, fileTitle);\r
5750       }\r
5751       break;\r
5752 \r
5753     case IDM_LoadNextPosition:\r
5754       ReloadPosition(1);\r
5755       break;\r
5756 \r
5757     case IDM_LoadPrevPosition:\r
5758       ReloadPosition(-1);\r
5759       break;\r
5760 \r
5761     case IDM_ReloadPosition:\r
5762       ReloadPosition(0);\r
5763       break;\r
5764 \r
5765     case IDM_SaveGame:\r
5766       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5767       f = OpenFileDialog(hwnd, "a", defName,\r
5768                          appData.oldSaveStyle ? "gam" : "pgn",\r
5769                          GAME_FILT,\r
5770                          "Save Game to File", NULL, fileTitle, NULL);\r
5771       if (f != NULL) {\r
5772         SaveGame(f, 0, "");\r
5773       }\r
5774       break;\r
5775 \r
5776     case IDM_SavePosition:\r
5777       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5778       f = OpenFileDialog(hwnd, "a", defName,\r
5779                          appData.oldSaveStyle ? "pos" : "fen",\r
5780                          POSITION_FILT,\r
5781                          "Save Position to File", NULL, fileTitle, NULL);\r
5782       if (f != NULL) {\r
5783         SavePosition(f, 0, "");\r
5784       }\r
5785       break;\r
5786 \r
5787     case IDM_SaveDiagram:\r
5788       defName = "diagram";\r
5789       f = OpenFileDialog(hwnd, "wb", defName,\r
5790                          "bmp",\r
5791                          DIAGRAM_FILT,\r
5792                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5793       if (f != NULL) {\r
5794         SaveDiagram(f);\r
5795       }\r
5796       break;\r
5797 \r
5798     case IDM_CopyGame:\r
5799       CopyGameToClipboard();\r
5800       break;\r
5801 \r
5802     case IDM_PasteGame:\r
5803       PasteGameFromClipboard();\r
5804       break;\r
5805 \r
5806     case IDM_CopyGameListToClipboard:\r
5807       CopyGameListToClipboard();\r
5808       break;\r
5809 \r
5810     /* [AS] Autodetect FEN or PGN data */\r
5811     case IDM_PasteAny:\r
5812       PasteGameOrFENFromClipboard();\r
5813       break;\r
5814 \r
5815     /* [AS] Move history */\r
5816     case IDM_ShowMoveHistory:\r
5817         if( MoveHistoryIsUp() ) {\r
5818             MoveHistoryPopDown();\r
5819         }\r
5820         else {\r
5821             MoveHistoryPopUp();\r
5822         }\r
5823         break;\r
5824 \r
5825     /* [AS] Eval graph */\r
5826     case IDM_ShowEvalGraph:\r
5827         if( EvalGraphIsUp() ) {\r
5828             EvalGraphPopDown();\r
5829         }\r
5830         else {\r
5831             EvalGraphPopUp();\r
5832             SetFocus(hwndMain);\r
5833         }\r
5834         break;\r
5835 \r
5836     /* [AS] Engine output */\r
5837     case IDM_ShowEngineOutput:\r
5838         if( EngineOutputIsUp() ) {\r
5839             EngineOutputPopDown();\r
5840         }\r
5841         else {\r
5842             EngineOutputPopUp();\r
5843         }\r
5844         break;\r
5845 \r
5846     /* [AS] User adjudication */\r
5847     case IDM_UserAdjudication_White:\r
5848         UserAdjudicationEvent( +1 );\r
5849         break;\r
5850 \r
5851     case IDM_UserAdjudication_Black:\r
5852         UserAdjudicationEvent( -1 );\r
5853         break;\r
5854 \r
5855     case IDM_UserAdjudication_Draw:\r
5856         UserAdjudicationEvent( 0 );\r
5857         break;\r
5858 \r
5859     /* [AS] Game list options dialog */\r
5860     case IDM_GameListOptions:\r
5861       GameListOptions();\r
5862       break;\r
5863 \r
5864     case IDM_CopyPosition:\r
5865       CopyFENToClipboard();\r
5866       break;\r
5867 \r
5868     case IDM_PastePosition:\r
5869       PasteFENFromClipboard();\r
5870       break;\r
5871 \r
5872     case IDM_MailMove:\r
5873       MailMoveEvent();\r
5874       break;\r
5875 \r
5876     case IDM_ReloadCMailMsg:\r
5877       Reset(TRUE, TRUE);\r
5878       ReloadCmailMsgEvent(FALSE);\r
5879       break;\r
5880 \r
5881     case IDM_Minimize:\r
5882       ShowWindow(hwnd, SW_MINIMIZE);\r
5883       break;\r
5884 \r
5885     case IDM_Exit:\r
5886       ExitEvent(0);\r
5887       break;\r
5888 \r
5889     case IDM_MachineWhite:\r
5890       MachineWhiteEvent();\r
5891       /*\r
5892        * refresh the tags dialog only if it's visible\r
5893        */\r
5894       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5895           char *tags;\r
5896           tags = PGNTags(&gameInfo);\r
5897           TagsPopUp(tags, CmailMsg());\r
5898           free(tags);\r
5899       }\r
5900       SAY("computer starts playing white");\r
5901       break;\r
5902 \r
5903     case IDM_MachineBlack:\r
5904       MachineBlackEvent();\r
5905       /*\r
5906        * refresh the tags dialog only if it's visible\r
5907        */\r
5908       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5909           char *tags;\r
5910           tags = PGNTags(&gameInfo);\r
5911           TagsPopUp(tags, CmailMsg());\r
5912           free(tags);\r
5913       }\r
5914       SAY("computer starts playing black");\r
5915       break;\r
5916 \r
5917     case IDM_TwoMachines:\r
5918       TwoMachinesEvent();\r
5919       /*\r
5920        * refresh the tags dialog only if it's visible\r
5921        */\r
5922       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5923           char *tags;\r
5924           tags = PGNTags(&gameInfo);\r
5925           TagsPopUp(tags, CmailMsg());\r
5926           free(tags);\r
5927       }\r
5928       SAY("programs start playing each other");\r
5929       break;\r
5930 \r
5931     case IDM_AnalysisMode:\r
5932       if (!first.analysisSupport) {\r
5933         sprintf(buf, "%s does not support analysis", first.tidy);\r
5934         DisplayError(buf, 0);\r
5935       } else {\r
5936         SAY("analyzing current position");\r
5937         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5938         if (appData.icsActive) {\r
5939                if (gameMode != IcsObserving) {\r
5940                        sprintf(buf, "You are not observing a game");\r
5941                        DisplayError(buf, 0);\r
5942                        /* secure check */\r
5943                        if (appData.icsEngineAnalyze) {\r
5944                                if (appData.debugMode) \r
5945                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5946                                ExitAnalyzeMode();\r
5947                                ModeHighlight();\r
5948                                break;\r
5949                        }\r
5950                        break;\r
5951                } else {\r
5952                        /* if enable, user want disable icsEngineAnalyze */\r
5953                        if (appData.icsEngineAnalyze) {\r
5954                                ExitAnalyzeMode();\r
5955                                ModeHighlight();\r
5956                                break;\r
5957                        }\r
5958                        appData.icsEngineAnalyze = TRUE;\r
5959                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5960                }\r
5961         } \r
5962         if (!appData.showThinking) ToggleShowThinking();\r
5963         AnalyzeModeEvent();\r
5964       }\r
5965       break;\r
5966 \r
5967     case IDM_AnalyzeFile:\r
5968       if (!first.analysisSupport) {\r
5969         char buf[MSG_SIZ];\r
5970         sprintf(buf, "%s does not support analysis", first.tidy);\r
5971         DisplayError(buf, 0);\r
5972       } else {\r
5973         if (!appData.showThinking) ToggleShowThinking();\r
5974         AnalyzeFileEvent();\r
5975         LoadGameDialog(hwnd, "Analyze Game from File");\r
5976         AnalysisPeriodicEvent(1);\r
5977       }\r
5978       break;\r
5979 \r
5980     case IDM_IcsClient:\r
5981       IcsClientEvent();\r
5982       break;\r
5983 \r
5984     case IDM_EditGame:\r
5985       EditGameEvent();\r
5986       SAY("edit game");\r
5987       break;\r
5988 \r
5989     case IDM_EditPosition:\r
5990       EditPositionEvent();\r
5991       SAY("to set up a position type a FEN");\r
5992       break;\r
5993 \r
5994     case IDM_Training:\r
5995       TrainingEvent();\r
5996       break;\r
5997 \r
5998     case IDM_ShowGameList:\r
5999       ShowGameListProc();\r
6000       break;\r
6001 \r
6002     case IDM_EditTags:\r
6003       EditTagsProc();\r
6004       break;\r
6005 \r
6006     case IDM_EditComment:\r
6007       if (commentDialogUp && editComment) {\r
6008         CommentPopDown();\r
6009       } else {\r
6010         EditCommentEvent();\r
6011       }\r
6012       break;\r
6013 \r
6014     case IDM_Pause:\r
6015       PauseEvent();\r
6016       break;\r
6017 \r
6018     case IDM_Accept:\r
6019       AcceptEvent();\r
6020       break;\r
6021 \r
6022     case IDM_Decline:\r
6023       DeclineEvent();\r
6024       break;\r
6025 \r
6026     case IDM_Rematch:\r
6027       RematchEvent();\r
6028       break;\r
6029 \r
6030     case IDM_CallFlag:\r
6031       CallFlagEvent();\r
6032       break;\r
6033 \r
6034     case IDM_Draw:\r
6035       DrawEvent();\r
6036       break;\r
6037 \r
6038     case IDM_Adjourn:\r
6039       AdjournEvent();\r
6040       break;\r
6041 \r
6042     case IDM_Abort:\r
6043       AbortEvent();\r
6044       break;\r
6045 \r
6046     case IDM_Resign:\r
6047       ResignEvent();\r
6048       break;\r
6049 \r
6050     case IDM_StopObserving:\r
6051       StopObservingEvent();\r
6052       break;\r
6053 \r
6054     case IDM_StopExamining:\r
6055       StopExaminingEvent();\r
6056       break;\r
6057 \r
6058     case IDM_TypeInMove:\r
6059       PopUpMoveDialog('\000');\r
6060       break;\r
6061 \r
6062     case IDM_TypeInName:\r
6063       PopUpNameDialog('\000');\r
6064       break;\r
6065 \r
6066     case IDM_Backward:\r
6067       BackwardEvent();\r
6068       SetFocus(hwndMain);\r
6069       break;\r
6070 \r
6071     JAWS_MENU_ITEMS\r
6072 \r
6073     case IDM_Forward:\r
6074       ForwardEvent();\r
6075       SetFocus(hwndMain);\r
6076       break;\r
6077 \r
6078     case IDM_ToStart:\r
6079       ToStartEvent();\r
6080       SetFocus(hwndMain);\r
6081       break;\r
6082 \r
6083     case IDM_ToEnd:\r
6084       ToEndEvent();\r
6085       SetFocus(hwndMain);\r
6086       break;\r
6087 \r
6088     case IDM_Revert:\r
6089       RevertEvent();\r
6090       break;\r
6091 \r
6092     case IDM_TruncateGame:\r
6093       TruncateGameEvent();\r
6094       break;\r
6095 \r
6096     case IDM_MoveNow:\r
6097       MoveNowEvent();\r
6098       break;\r
6099 \r
6100     case IDM_RetractMove:\r
6101       RetractMoveEvent();\r
6102       break;\r
6103 \r
6104     case IDM_FlipView:\r
6105       flipView = !flipView;\r
6106       DrawPosition(FALSE, NULL);\r
6107       break;\r
6108 \r
6109     case IDM_FlipClock:\r
6110       flipClock = !flipClock;\r
6111       DisplayBothClocks();\r
6112       DrawPosition(FALSE, NULL);\r
6113       break;\r
6114 \r
6115     case IDM_GeneralOptions:\r
6116       GeneralOptionsPopup(hwnd);\r
6117       DrawPosition(TRUE, NULL);\r
6118       break;\r
6119 \r
6120     case IDM_BoardOptions:\r
6121       BoardOptionsPopup(hwnd);\r
6122       break;\r
6123 \r
6124     case IDM_EnginePlayOptions:\r
6125       EnginePlayOptionsPopup(hwnd);\r
6126       break;\r
6127 \r
6128     case IDM_OptionsUCI:\r
6129       UciOptionsPopup(hwnd);\r
6130       break;\r
6131 \r
6132     case IDM_IcsOptions:\r
6133       IcsOptionsPopup(hwnd);\r
6134       break;\r
6135 \r
6136     case IDM_Fonts:\r
6137       FontsOptionsPopup(hwnd);\r
6138       break;\r
6139 \r
6140     case IDM_Sounds:\r
6141       SoundOptionsPopup(hwnd);\r
6142       break;\r
6143 \r
6144     case IDM_CommPort:\r
6145       CommPortOptionsPopup(hwnd);\r
6146       break;\r
6147 \r
6148     case IDM_LoadOptions:\r
6149       LoadOptionsPopup(hwnd);\r
6150       break;\r
6151 \r
6152     case IDM_SaveOptions:\r
6153       SaveOptionsPopup(hwnd);\r
6154       break;\r
6155 \r
6156     case IDM_TimeControl:\r
6157       TimeControlOptionsPopup(hwnd);\r
6158       break;\r
6159 \r
6160     case IDM_SaveSettings:\r
6161       SaveSettings(settingsFileName);\r
6162       break;\r
6163 \r
6164     case IDM_SaveSettingsOnExit:\r
6165       saveSettingsOnExit = !saveSettingsOnExit;\r
6166       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6167                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6168                                          MF_CHECKED : MF_UNCHECKED));\r
6169       break;\r
6170 \r
6171     case IDM_Hint:\r
6172       HintEvent();\r
6173       break;\r
6174 \r
6175     case IDM_Book:\r
6176       BookEvent();\r
6177       break;\r
6178 \r
6179     case IDM_AboutGame:\r
6180       AboutGameEvent();\r
6181       break;\r
6182 \r
6183     case IDM_Debug:\r
6184       appData.debugMode = !appData.debugMode;\r
6185       if (appData.debugMode) {\r
6186         char dir[MSG_SIZ];\r
6187         GetCurrentDirectory(MSG_SIZ, dir);\r
6188         SetCurrentDirectory(installDir);\r
6189         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6190         SetCurrentDirectory(dir);\r
6191         setbuf(debugFP, NULL);\r
6192       } else {\r
6193         fclose(debugFP);\r
6194         debugFP = NULL;\r
6195       }\r
6196       break;\r
6197 \r
6198     case IDM_HELPCONTENTS:\r
6199       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6200           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6201           MessageBox (GetFocus(),\r
6202                     "Unable to activate help",\r
6203                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6204       }\r
6205       break;\r
6206 \r
6207     case IDM_HELPSEARCH:\r
6208         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6209             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6210         MessageBox (GetFocus(),\r
6211                     "Unable to activate help",\r
6212                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6213       }\r
6214       break;\r
6215 \r
6216     case IDM_HELPHELP:\r
6217       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6218         MessageBox (GetFocus(),\r
6219                     "Unable to activate help",\r
6220                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6221       }\r
6222       break;\r
6223 \r
6224     case IDM_ABOUT:\r
6225       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6226       DialogBox(hInst, \r
6227         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6228         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6229       FreeProcInstance(lpProc);\r
6230       break;\r
6231 \r
6232     case IDM_DirectCommand1:\r
6233       AskQuestionEvent("Direct Command",\r
6234                        "Send to chess program:", "", "1");\r
6235       break;\r
6236     case IDM_DirectCommand2:\r
6237       AskQuestionEvent("Direct Command",\r
6238                        "Send to second chess program:", "", "2");\r
6239       break;\r
6240 \r
6241     case EP_WhitePawn:\r
6242       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6243       fromX = fromY = -1;\r
6244       break;\r
6245 \r
6246     case EP_WhiteKnight:\r
6247       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6248       fromX = fromY = -1;\r
6249       break;\r
6250 \r
6251     case EP_WhiteBishop:\r
6252       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6253       fromX = fromY = -1;\r
6254       break;\r
6255 \r
6256     case EP_WhiteRook:\r
6257       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6258       fromX = fromY = -1;\r
6259       break;\r
6260 \r
6261     case EP_WhiteQueen:\r
6262       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6263       fromX = fromY = -1;\r
6264       break;\r
6265 \r
6266     case EP_WhiteFerz:\r
6267       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6268       fromX = fromY = -1;\r
6269       break;\r
6270 \r
6271     case EP_WhiteWazir:\r
6272       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6273       fromX = fromY = -1;\r
6274       break;\r
6275 \r
6276     case EP_WhiteAlfil:\r
6277       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6278       fromX = fromY = -1;\r
6279       break;\r
6280 \r
6281     case EP_WhiteCannon:\r
6282       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6283       fromX = fromY = -1;\r
6284       break;\r
6285 \r
6286     case EP_WhiteCardinal:\r
6287       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6288       fromX = fromY = -1;\r
6289       break;\r
6290 \r
6291     case EP_WhiteMarshall:\r
6292       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6293       fromX = fromY = -1;\r
6294       break;\r
6295 \r
6296     case EP_WhiteKing:\r
6297       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6298       fromX = fromY = -1;\r
6299       break;\r
6300 \r
6301     case EP_BlackPawn:\r
6302       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6303       fromX = fromY = -1;\r
6304       break;\r
6305 \r
6306     case EP_BlackKnight:\r
6307       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6308       fromX = fromY = -1;\r
6309       break;\r
6310 \r
6311     case EP_BlackBishop:\r
6312       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6313       fromX = fromY = -1;\r
6314       break;\r
6315 \r
6316     case EP_BlackRook:\r
6317       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6318       fromX = fromY = -1;\r
6319       break;\r
6320 \r
6321     case EP_BlackQueen:\r
6322       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6323       fromX = fromY = -1;\r
6324       break;\r
6325 \r
6326     case EP_BlackFerz:\r
6327       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6328       fromX = fromY = -1;\r
6329       break;\r
6330 \r
6331     case EP_BlackWazir:\r
6332       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6333       fromX = fromY = -1;\r
6334       break;\r
6335 \r
6336     case EP_BlackAlfil:\r
6337       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6338       fromX = fromY = -1;\r
6339       break;\r
6340 \r
6341     case EP_BlackCannon:\r
6342       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6343       fromX = fromY = -1;\r
6344       break;\r
6345 \r
6346     case EP_BlackCardinal:\r
6347       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6348       fromX = fromY = -1;\r
6349       break;\r
6350 \r
6351     case EP_BlackMarshall:\r
6352       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6353       fromX = fromY = -1;\r
6354       break;\r
6355 \r
6356     case EP_BlackKing:\r
6357       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6358       fromX = fromY = -1;\r
6359       break;\r
6360 \r
6361     case EP_EmptySquare:\r
6362       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6363       fromX = fromY = -1;\r
6364       break;\r
6365 \r
6366     case EP_ClearBoard:\r
6367       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6368       fromX = fromY = -1;\r
6369       break;\r
6370 \r
6371     case EP_White:\r
6372       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6373       fromX = fromY = -1;\r
6374       break;\r
6375 \r
6376     case EP_Black:\r
6377       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6378       fromX = fromY = -1;\r
6379       break;\r
6380 \r
6381     case EP_Promote:\r
6382       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6383       fromX = fromY = -1;\r
6384       break;\r
6385 \r
6386     case EP_Demote:\r
6387       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6388       fromX = fromY = -1;\r
6389       break;\r
6390 \r
6391     case DP_Pawn:\r
6392       DropMenuEvent(WhitePawn, fromX, fromY);\r
6393       fromX = fromY = -1;\r
6394       break;\r
6395 \r
6396     case DP_Knight:\r
6397       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6398       fromX = fromY = -1;\r
6399       break;\r
6400 \r
6401     case DP_Bishop:\r
6402       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6403       fromX = fromY = -1;\r
6404       break;\r
6405 \r
6406     case DP_Rook:\r
6407       DropMenuEvent(WhiteRook, fromX, fromY);\r
6408       fromX = fromY = -1;\r
6409       break;\r
6410 \r
6411     case DP_Queen:\r
6412       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6413       fromX = fromY = -1;\r
6414       break;\r
6415 \r
6416     default:\r
6417       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6418     }\r
6419     break;\r
6420 \r
6421   case WM_TIMER:\r
6422     switch (wParam) {\r
6423     case CLOCK_TIMER_ID:\r
6424       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6425       clockTimerEvent = 0;\r
6426       DecrementClocks(); /* call into back end */\r
6427       break;\r
6428     case LOAD_GAME_TIMER_ID:\r
6429       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6430       loadGameTimerEvent = 0;\r
6431       AutoPlayGameLoop(); /* call into back end */\r
6432       break;\r
6433     case ANALYSIS_TIMER_ID:\r
6434       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6435                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6436         AnalysisPeriodicEvent(0);\r
6437       } else {\r
6438         KillTimer(hwnd, analysisTimerEvent);\r
6439         analysisTimerEvent = 0;\r
6440       }\r
6441       break;\r
6442     case DELAYED_TIMER_ID:\r
6443       KillTimer(hwnd, delayedTimerEvent);\r
6444       delayedTimerEvent = 0;\r
6445       delayedTimerCallback();\r
6446       break;\r
6447     }\r
6448     break;\r
6449 \r
6450   case WM_USER_Input:\r
6451     InputEvent(hwnd, message, wParam, lParam);\r
6452     break;\r
6453 \r
6454   /* [AS] Also move "attached" child windows */\r
6455   case WM_WINDOWPOSCHANGING:\r
6456 \r
6457     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6458         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6459 \r
6460         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6461             /* Window is moving */\r
6462             RECT rcMain;\r
6463 \r
6464 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6465             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6466             rcMain.right  = boardX + winWidth;\r
6467             rcMain.top    = boardY;\r
6468             rcMain.bottom = boardY + winHeight;\r
6469             \r
6470             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6471             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6472             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6473             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6474             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6475             boardX = lpwp->x;\r
6476             boardY = lpwp->y;\r
6477         }\r
6478     }\r
6479     break;\r
6480 \r
6481   /* [AS] Snapping */\r
6482   case WM_ENTERSIZEMOVE:\r
6483     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6484     if (hwnd == hwndMain) {\r
6485       doingSizing = TRUE;\r
6486       lastSizing = 0;\r
6487     }\r
6488     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6489     break;\r
6490 \r
6491   case WM_SIZING:\r
6492     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6493     if (hwnd == hwndMain) {\r
6494       lastSizing = wParam;\r
6495     }\r
6496     break;\r
6497 \r
6498   case WM_MOVING:\r
6499     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6500       return OnMoving( &sd, hwnd, wParam, lParam );\r
6501 \r
6502   case WM_EXITSIZEMOVE:\r
6503     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6504     if (hwnd == hwndMain) {\r
6505       RECT client;\r
6506       doingSizing = FALSE;\r
6507       InvalidateRect(hwnd, &boardRect, FALSE);\r
6508       GetClientRect(hwnd, &client);\r
6509       ResizeBoard(client.right, client.bottom, lastSizing);\r
6510       lastSizing = 0;\r
6511       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6512     }\r
6513     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6514     break;\r
6515 \r
6516   case WM_DESTROY: /* message: window being destroyed */\r
6517     PostQuitMessage(0);\r
6518     break;\r
6519 \r
6520   case WM_CLOSE:\r
6521     if (hwnd == hwndMain) {\r
6522       ExitEvent(0);\r
6523     }\r
6524     break;\r
6525 \r
6526   default:      /* Passes it on if unprocessed */\r
6527     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6528   }\r
6529   return 0;\r
6530 }\r
6531 \r
6532 /*---------------------------------------------------------------------------*\\r
6533  *\r
6534  * Misc utility routines\r
6535  *\r
6536 \*---------------------------------------------------------------------------*/\r
6537 \r
6538 /*\r
6539  * Decent random number generator, at least not as bad as Windows\r
6540  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6541  */\r
6542 unsigned int randstate;\r
6543 \r
6544 int\r
6545 myrandom(void)\r
6546 {\r
6547   randstate = randstate * 1664525 + 1013904223;\r
6548   return (int) randstate & 0x7fffffff;\r
6549 }\r
6550 \r
6551 void\r
6552 mysrandom(unsigned int seed)\r
6553 {\r
6554   randstate = seed;\r
6555 }\r
6556 \r
6557 \r
6558 /* \r
6559  * returns TRUE if user selects a different color, FALSE otherwise \r
6560  */\r
6561 \r
6562 BOOL\r
6563 ChangeColor(HWND hwnd, COLORREF *which)\r
6564 {\r
6565   static BOOL firstTime = TRUE;\r
6566   static DWORD customColors[16];\r
6567   CHOOSECOLOR cc;\r
6568   COLORREF newcolor;\r
6569   int i;\r
6570   ColorClass ccl;\r
6571 \r
6572   if (firstTime) {\r
6573     /* Make initial colors in use available as custom colors */\r
6574     /* Should we put the compiled-in defaults here instead? */\r
6575     i = 0;\r
6576     customColors[i++] = lightSquareColor & 0xffffff;\r
6577     customColors[i++] = darkSquareColor & 0xffffff;\r
6578     customColors[i++] = whitePieceColor & 0xffffff;\r
6579     customColors[i++] = blackPieceColor & 0xffffff;\r
6580     customColors[i++] = highlightSquareColor & 0xffffff;\r
6581     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6582 \r
6583     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6584       customColors[i++] = textAttribs[ccl].color;\r
6585     }\r
6586     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6587     firstTime = FALSE;\r
6588   }\r
6589 \r
6590   cc.lStructSize = sizeof(cc);\r
6591   cc.hwndOwner = hwnd;\r
6592   cc.hInstance = NULL;\r
6593   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6594   cc.lpCustColors = (LPDWORD) customColors;\r
6595   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6596 \r
6597   if (!ChooseColor(&cc)) return FALSE;\r
6598 \r
6599   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6600   if (newcolor == *which) return FALSE;\r
6601   *which = newcolor;\r
6602   return TRUE;\r
6603 \r
6604   /*\r
6605   InitDrawingColors();\r
6606   InvalidateRect(hwnd, &boardRect, FALSE);\r
6607   */\r
6608 }\r
6609 \r
6610 BOOLEAN\r
6611 MyLoadSound(MySound *ms)\r
6612 {\r
6613   BOOL ok = FALSE;\r
6614   struct stat st;\r
6615   FILE *f;\r
6616 \r
6617   if (ms->data) free(ms->data);\r
6618   ms->data = NULL;\r
6619 \r
6620   switch (ms->name[0]) {\r
6621   case NULLCHAR:\r
6622     /* Silence */\r
6623     ok = TRUE;\r
6624     break;\r
6625   case '$':\r
6626     /* System sound from Control Panel.  Don't preload here. */\r
6627     ok = TRUE;\r
6628     break;\r
6629   case '!':\r
6630     if (ms->name[1] == NULLCHAR) {\r
6631       /* "!" alone = silence */\r
6632       ok = TRUE;\r
6633     } else {\r
6634       /* Builtin wave resource.  Error if not found. */\r
6635       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6636       if (h == NULL) break;\r
6637       ms->data = (void *)LoadResource(hInst, h);\r
6638       if (h == NULL) break;\r
6639       ok = TRUE;\r
6640     }\r
6641     break;\r
6642   default:\r
6643     /* .wav file.  Error if not found. */\r
6644     f = fopen(ms->name, "rb");\r
6645     if (f == NULL) break;\r
6646     if (fstat(fileno(f), &st) < 0) break;\r
6647     ms->data = malloc(st.st_size);\r
6648     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6649     fclose(f);\r
6650     ok = TRUE;\r
6651     break;\r
6652   }\r
6653   if (!ok) {\r
6654     char buf[MSG_SIZ];\r
6655     sprintf(buf, "Error loading sound %s", ms->name);\r
6656     DisplayError(buf, GetLastError());\r
6657   }\r
6658   return ok;\r
6659 }\r
6660 \r
6661 BOOLEAN\r
6662 MyPlaySound(MySound *ms)\r
6663 {\r
6664   BOOLEAN ok = FALSE;\r
6665         if(appData.debugMode) fprintf(debugFP, "make sound %s %x %d\n", ms->name, ms, ms->name[0]);\r
6666   switch (ms->name[0]) {\r
6667   case NULLCHAR:\r
6668         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6669     /* Silence */\r
6670     ok = TRUE;\r
6671     break;\r
6672   case '$':\r
6673     /* System sound from Control Panel (deprecated feature).\r
6674        "$" alone or an unset sound name gets default beep (still in use). */\r
6675     if (ms->name[1]) {\r
6676       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6677     }\r
6678     if (!ok) ok = MessageBeep(MB_OK);\r
6679     break; \r
6680   case '!':\r
6681     /* Builtin wave resource, or "!" alone for silence */\r
6682     if (ms->name[1]) {\r
6683       if (ms->data == NULL) return FALSE;\r
6684       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6685     } else {\r
6686       ok = TRUE;\r
6687     }\r
6688     break;\r
6689   default:\r
6690     /* .wav file.  Error if not found. */\r
6691     if (ms->data == NULL) return FALSE;\r
6692     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6693     break;\r
6694   }\r
6695   /* Don't print an error: this can happen innocently if the sound driver\r
6696      is busy; for instance, if another instance of WinBoard is playing\r
6697      a sound at about the same time. */\r
6698 #if 0\r
6699   if (!ok) {\r
6700     char buf[MSG_SIZ];\r
6701     sprintf(buf, "Error playing sound %s", ms->name);\r
6702     DisplayError(buf, GetLastError());\r
6703   }\r
6704 #endif\r
6705   return ok;\r
6706 }\r
6707 \r
6708 \r
6709 LRESULT CALLBACK\r
6710 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6711 {\r
6712   BOOL ok;\r
6713   OPENFILENAME *ofn;\r
6714   static UINT *number; /* gross that this is static */\r
6715 \r
6716   switch (message) {\r
6717   case WM_INITDIALOG: /* message: initialize dialog box */\r
6718     /* Center the dialog over the application window */\r
6719     ofn = (OPENFILENAME *) lParam;\r
6720     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6721       number = (UINT *) ofn->lCustData;\r
6722       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6723     } else {\r
6724       number = NULL;\r
6725     }\r
6726     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6727     return FALSE;  /* Allow for further processing */\r
6728 \r
6729   case WM_COMMAND:\r
6730     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6731       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6732     }\r
6733     return FALSE;  /* Allow for further processing */\r
6734   }\r
6735   return FALSE;\r
6736 }\r
6737 \r
6738 UINT APIENTRY\r
6739 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6740 {\r
6741   static UINT *number;\r
6742   OPENFILENAME *ofname;\r
6743   OFNOTIFY *ofnot;\r
6744   switch (uiMsg) {\r
6745   case WM_INITDIALOG:\r
6746     ofname = (OPENFILENAME *)lParam;\r
6747     number = (UINT *)(ofname->lCustData);\r
6748     break;\r
6749   case WM_NOTIFY:\r
6750     ofnot = (OFNOTIFY *)lParam;\r
6751     if (ofnot->hdr.code == CDN_FILEOK) {\r
6752       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6753     }\r
6754     break;\r
6755   }\r
6756   return 0;\r
6757 }\r
6758 \r
6759 \r
6760 FILE *\r
6761 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6762                char *nameFilt, char *dlgTitle, UINT *number,\r
6763                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6764 {\r
6765   OPENFILENAME openFileName;\r
6766   char buf1[MSG_SIZ];\r
6767   FILE *f;\r
6768 \r
6769   if (fileName == NULL) fileName = buf1;\r
6770   if (defName == NULL) {\r
6771     strcpy(fileName, "*.");\r
6772     strcat(fileName, defExt);\r
6773   } else {\r
6774     strcpy(fileName, defName);\r
6775   }\r
6776   if (fileTitle) strcpy(fileTitle, "");\r
6777   if (number) *number = 0;\r
6778 \r
6779   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6780   openFileName.hwndOwner         = hwnd;\r
6781   openFileName.hInstance         = (HANDLE) hInst;\r
6782   openFileName.lpstrFilter       = nameFilt;\r
6783   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6784   openFileName.nMaxCustFilter    = 0L;\r
6785   openFileName.nFilterIndex      = 1L;\r
6786   openFileName.lpstrFile         = fileName;\r
6787   openFileName.nMaxFile          = MSG_SIZ;\r
6788   openFileName.lpstrFileTitle    = fileTitle;\r
6789   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6790   openFileName.lpstrInitialDir   = NULL;\r
6791   openFileName.lpstrTitle        = dlgTitle;\r
6792   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6793     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6794     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6795     | (oldDialog ? 0 : OFN_EXPLORER);\r
6796   openFileName.nFileOffset       = 0;\r
6797   openFileName.nFileExtension    = 0;\r
6798   openFileName.lpstrDefExt       = defExt;\r
6799   openFileName.lCustData         = (LONG) number;\r
6800   openFileName.lpfnHook          = oldDialog ?\r
6801     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6802   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6803 \r
6804   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6805                         GetOpenFileName(&openFileName)) {\r
6806     /* open the file */\r
6807     f = fopen(openFileName.lpstrFile, write);\r
6808     if (f == NULL) {\r
6809       MessageBox(hwnd, "File open failed", NULL,\r
6810                  MB_OK|MB_ICONEXCLAMATION);\r
6811       return NULL;\r
6812     }\r
6813   } else {\r
6814     int err = CommDlgExtendedError();\r
6815     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6816     return FALSE;\r
6817   }\r
6818   return f;\r
6819 }\r
6820 \r
6821 \r
6822 \r
6823 VOID APIENTRY\r
6824 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6825 {\r
6826   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6827 \r
6828   /*\r
6829    * Get the first pop-up menu in the menu template. This is the\r
6830    * menu that TrackPopupMenu displays.\r
6831    */\r
6832   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6833 \r
6834   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6835 \r
6836   /*\r
6837    * TrackPopup uses screen coordinates, so convert the\r
6838    * coordinates of the mouse click to screen coordinates.\r
6839    */\r
6840   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6841 \r
6842   /* Draw and track the floating pop-up menu. */\r
6843   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6844                  pt.x, pt.y, 0, hwnd, NULL);\r
6845 \r
6846   /* Destroy the menu.*/\r
6847   DestroyMenu(hmenu);\r
6848 }\r
6849    \r
6850 typedef struct {\r
6851   HWND hDlg, hText;\r
6852   int sizeX, sizeY, newSizeX, newSizeY;\r
6853   HDWP hdwp;\r
6854 } ResizeEditPlusButtonsClosure;\r
6855 \r
6856 BOOL CALLBACK\r
6857 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6858 {\r
6859   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6860   RECT rect;\r
6861   POINT pt;\r
6862 \r
6863   if (hChild == cl->hText) return TRUE;\r
6864   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6865   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6866   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6867   ScreenToClient(cl->hDlg, &pt);\r
6868   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6869     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6870   return TRUE;\r
6871 }\r
6872 \r
6873 /* Resize a dialog that has a (rich) edit field filling most of\r
6874    the top, with a row of buttons below */\r
6875 VOID\r
6876 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6877 {\r
6878   RECT rectText;\r
6879   int newTextHeight, newTextWidth;\r
6880   ResizeEditPlusButtonsClosure cl;\r
6881   \r
6882   /*if (IsIconic(hDlg)) return;*/\r
6883   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6884   \r
6885   cl.hdwp = BeginDeferWindowPos(8);\r
6886 \r
6887   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6888   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6889   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6890   if (newTextHeight < 0) {\r
6891     newSizeY += -newTextHeight;\r
6892     newTextHeight = 0;\r
6893   }\r
6894   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6895     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6896 \r
6897   cl.hDlg = hDlg;\r
6898   cl.hText = hText;\r
6899   cl.sizeX = sizeX;\r
6900   cl.sizeY = sizeY;\r
6901   cl.newSizeX = newSizeX;\r
6902   cl.newSizeY = newSizeY;\r
6903   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6904 \r
6905   EndDeferWindowPos(cl.hdwp);\r
6906 }\r
6907 \r
6908 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6909 {\r
6910     RECT    rChild, rParent;\r
6911     int     wChild, hChild, wParent, hParent;\r
6912     int     wScreen, hScreen, xNew, yNew;\r
6913     HDC     hdc;\r
6914 \r
6915     /* Get the Height and Width of the child window */\r
6916     GetWindowRect (hwndChild, &rChild);\r
6917     wChild = rChild.right - rChild.left;\r
6918     hChild = rChild.bottom - rChild.top;\r
6919 \r
6920     /* Get the Height and Width of the parent window */\r
6921     GetWindowRect (hwndParent, &rParent);\r
6922     wParent = rParent.right - rParent.left;\r
6923     hParent = rParent.bottom - rParent.top;\r
6924 \r
6925     /* Get the display limits */\r
6926     hdc = GetDC (hwndChild);\r
6927     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6928     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6929     ReleaseDC(hwndChild, hdc);\r
6930 \r
6931     /* Calculate new X position, then adjust for screen */\r
6932     xNew = rParent.left + ((wParent - wChild) /2);\r
6933     if (xNew < 0) {\r
6934         xNew = 0;\r
6935     } else if ((xNew+wChild) > wScreen) {\r
6936         xNew = wScreen - wChild;\r
6937     }\r
6938 \r
6939     /* Calculate new Y position, then adjust for screen */\r
6940     if( mode == 0 ) {\r
6941         yNew = rParent.top  + ((hParent - hChild) /2);\r
6942     }\r
6943     else {\r
6944         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6945     }\r
6946 \r
6947     if (yNew < 0) {\r
6948         yNew = 0;\r
6949     } else if ((yNew+hChild) > hScreen) {\r
6950         yNew = hScreen - hChild;\r
6951     }\r
6952 \r
6953     /* Set it, and return */\r
6954     return SetWindowPos (hwndChild, NULL,\r
6955                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6956 }\r
6957 \r
6958 /* Center one window over another */\r
6959 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6960 {\r
6961     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6962 }\r
6963 \r
6964 /*---------------------------------------------------------------------------*\\r
6965  *\r
6966  * Startup Dialog functions\r
6967  *\r
6968 \*---------------------------------------------------------------------------*/\r
6969 void\r
6970 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6971 {\r
6972   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6973 \r
6974   while (*cd != NULL) {\r
6975     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6976     cd++;\r
6977   }\r
6978 }\r
6979 \r
6980 void\r
6981 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6982 {\r
6983   char buf1[ARG_MAX];\r
6984   int len;\r
6985 \r
6986   if (str[0] == '@') {\r
6987     FILE* f = fopen(str + 1, "r");\r
6988     if (f == NULL) {\r
6989       DisplayFatalError(str + 1, errno, 2);\r
6990       return;\r
6991     }\r
6992     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6993     fclose(f);\r
6994     buf1[len] = NULLCHAR;\r
6995     str = buf1;\r
6996   }\r
6997 \r
6998   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6999 \r
7000   for (;;) {\r
7001     char buf[MSG_SIZ];\r
7002     char *end = strchr(str, '\n');\r
7003     if (end == NULL) return;\r
7004     memcpy(buf, str, end - str);\r
7005     buf[end - str] = NULLCHAR;\r
7006     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
7007     str = end + 1;\r
7008   }\r
7009 }\r
7010 \r
7011 void\r
7012 SetStartupDialogEnables(HWND hDlg)\r
7013 {\r
7014   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
7015     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7016     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
7017   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7018     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
7019   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
7020     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
7021   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
7022     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
7023   EnableWindow(GetDlgItem(hDlg, IDOK),\r
7024     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7025     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
7026     IsDlgButtonChecked(hDlg, OPT_View));\r
7027 }\r
7028 \r
7029 char *\r
7030 QuoteForFilename(char *filename)\r
7031 {\r
7032   int dquote, space;\r
7033   dquote = strchr(filename, '"') != NULL;\r
7034   space = strchr(filename, ' ') != NULL;\r
7035   if (dquote || space) {\r
7036     if (dquote) {\r
7037       return "'";\r
7038     } else {\r
7039       return "\"";\r
7040     }\r
7041   } else {\r
7042     return "";\r
7043   }\r
7044 }\r
7045 \r
7046 VOID\r
7047 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
7048 {\r
7049   char buf[MSG_SIZ];\r
7050   char *q;\r
7051 \r
7052   InitComboStringsFromOption(hwndCombo, nthnames);\r
7053   q = QuoteForFilename(nthcp);\r
7054   sprintf(buf, "%s%s%s", q, nthcp, q);\r
7055   if (*nthdir != NULLCHAR) {\r
7056     q = QuoteForFilename(nthdir);\r
7057     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
7058   }\r
7059   if (*nthcp == NULLCHAR) {\r
7060     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7061   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7062     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7063     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7064   }\r
7065 }\r
7066 \r
7067 LRESULT CALLBACK\r
7068 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7069 {\r
7070   char buf[MSG_SIZ];\r
7071   HANDLE hwndCombo;\r
7072   char *p;\r
7073 \r
7074   switch (message) {\r
7075   case WM_INITDIALOG:\r
7076     /* Center the dialog */\r
7077     CenterWindow (hDlg, GetDesktopWindow());\r
7078     /* Initialize the dialog items */\r
7079     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7080                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7081                   firstChessProgramNames);\r
7082     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7083                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7084                   secondChessProgramNames);\r
7085     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7086     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7087     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7088     if (*appData.icsHelper != NULLCHAR) {\r
7089       char *q = QuoteForFilename(appData.icsHelper);\r
7090       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7091     }\r
7092     if (*appData.icsHost == NULLCHAR) {\r
7093       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7094       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7095     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7096       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7097       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7098     }\r
7099 \r
7100     if (appData.icsActive) {\r
7101       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7102     }\r
7103     else if (appData.noChessProgram) {\r
7104       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7105     }\r
7106     else {\r
7107       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7108     }\r
7109 \r
7110     SetStartupDialogEnables(hDlg);\r
7111     return TRUE;\r
7112 \r
7113   case WM_COMMAND:\r
7114     switch (LOWORD(wParam)) {\r
7115     case IDOK:\r
7116       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7117         strcpy(buf, "/fcp=");\r
7118         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7119         p = buf;\r
7120         ParseArgs(StringGet, &p);\r
7121         strcpy(buf, "/scp=");\r
7122         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7123         p = buf;\r
7124         ParseArgs(StringGet, &p);\r
7125         appData.noChessProgram = FALSE;\r
7126         appData.icsActive = FALSE;\r
7127       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7128         strcpy(buf, "/ics /icshost=");\r
7129         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7130         p = buf;\r
7131         ParseArgs(StringGet, &p);\r
7132         if (appData.zippyPlay) {\r
7133           strcpy(buf, "/fcp=");\r
7134           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7135           p = buf;\r
7136           ParseArgs(StringGet, &p);\r
7137         }\r
7138       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7139         appData.noChessProgram = TRUE;\r
7140         appData.icsActive = FALSE;\r
7141       } else {\r
7142         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7143                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7144         return TRUE;\r
7145       }\r
7146       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7147         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7148         p = buf;\r
7149         ParseArgs(StringGet, &p);\r
7150       }\r
7151       EndDialog(hDlg, TRUE);\r
7152       return TRUE;\r
7153 \r
7154     case IDCANCEL:\r
7155       ExitEvent(0);\r
7156       return TRUE;\r
7157 \r
7158     case IDM_HELPCONTENTS:\r
7159       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7160         MessageBox (GetFocus(),\r
7161                     "Unable to activate help",\r
7162                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7163       }\r
7164       break;\r
7165 \r
7166     default:\r
7167       SetStartupDialogEnables(hDlg);\r
7168       break;\r
7169     }\r
7170     break;\r
7171   }\r
7172   return FALSE;\r
7173 }\r
7174 \r
7175 /*---------------------------------------------------------------------------*\\r
7176  *\r
7177  * About box dialog functions\r
7178  *\r
7179 \*---------------------------------------------------------------------------*/\r
7180 \r
7181 /* Process messages for "About" dialog box */\r
7182 LRESULT CALLBACK\r
7183 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7184 {\r
7185   switch (message) {\r
7186   case WM_INITDIALOG: /* message: initialize dialog box */\r
7187     /* Center the dialog over the application window */\r
7188     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7189     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7190     return (TRUE);\r
7191 \r
7192   case WM_COMMAND: /* message: received a command */\r
7193     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7194         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7195       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7196       return (TRUE);\r
7197     }\r
7198     break;\r
7199   }\r
7200   return (FALSE);\r
7201 }\r
7202 \r
7203 /*---------------------------------------------------------------------------*\\r
7204  *\r
7205  * Comment Dialog functions\r
7206  *\r
7207 \*---------------------------------------------------------------------------*/\r
7208 \r
7209 LRESULT CALLBACK\r
7210 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7211 {\r
7212   static HANDLE hwndText = NULL;\r
7213   int len, newSizeX, newSizeY, flags;\r
7214   static int sizeX, sizeY;\r
7215   char *str;\r
7216   RECT rect;\r
7217   MINMAXINFO *mmi;\r
7218 \r
7219   switch (message) {\r
7220   case WM_INITDIALOG: /* message: initialize dialog box */\r
7221     /* Initialize the dialog items */\r
7222     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7223     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7224     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7225     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7226     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7227     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7228     SetWindowText(hDlg, commentTitle);\r
7229     if (editComment) {\r
7230       SetFocus(hwndText);\r
7231     } else {\r
7232       SetFocus(GetDlgItem(hDlg, IDOK));\r
7233     }\r
7234     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7235                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7236                 MAKELPARAM(FALSE, 0));\r
7237     /* Size and position the dialog */\r
7238     if (!commentDialog) {\r
7239       commentDialog = hDlg;\r
7240       flags = SWP_NOZORDER;\r
7241       GetClientRect(hDlg, &rect);\r
7242       sizeX = rect.right;\r
7243       sizeY = rect.bottom;\r
7244       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7245           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7246         WINDOWPLACEMENT wp;\r
7247         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7248         wp.length = sizeof(WINDOWPLACEMENT);\r
7249         wp.flags = 0;\r
7250         wp.showCmd = SW_SHOW;\r
7251         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7252         wp.rcNormalPosition.left = commentX;\r
7253         wp.rcNormalPosition.right = commentX + commentW;\r
7254         wp.rcNormalPosition.top = commentY;\r
7255         wp.rcNormalPosition.bottom = commentY + commentH;\r
7256         SetWindowPlacement(hDlg, &wp);\r
7257 \r
7258         GetClientRect(hDlg, &rect);\r
7259         newSizeX = rect.right;\r
7260         newSizeY = rect.bottom;\r
7261         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7262                               newSizeX, newSizeY);\r
7263         sizeX = newSizeX;\r
7264         sizeY = newSizeY;\r
7265       }\r
7266     }\r
7267     return FALSE;\r
7268 \r
7269   case WM_COMMAND: /* message: received a command */\r
7270     switch (LOWORD(wParam)) {\r
7271     case IDOK:\r
7272       if (editComment) {\r
7273         char *p, *q;\r
7274         /* Read changed options from the dialog box */\r
7275         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7276         len = GetWindowTextLength(hwndText);\r
7277         str = (char *) malloc(len + 1);\r
7278         GetWindowText(hwndText, str, len + 1);\r
7279         p = q = str;\r
7280         while (*q) {\r
7281           if (*q == '\r')\r
7282             q++;\r
7283           else\r
7284             *p++ = *q++;\r
7285         }\r
7286         *p = NULLCHAR;\r
7287         ReplaceComment(commentIndex, str);\r
7288         free(str);\r
7289       }\r
7290       CommentPopDown();\r
7291       return TRUE;\r
7292 \r
7293     case IDCANCEL:\r
7294     case OPT_CancelComment:\r
7295       CommentPopDown();\r
7296       return TRUE;\r
7297 \r
7298     case OPT_ClearComment:\r
7299       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7300       break;\r
7301 \r
7302     case OPT_EditComment:\r
7303       EditCommentEvent();\r
7304       return TRUE;\r
7305 \r
7306     default:\r
7307       break;\r
7308     }\r
7309     break;\r
7310 \r
7311   case WM_SIZE:\r
7312     newSizeX = LOWORD(lParam);\r
7313     newSizeY = HIWORD(lParam);\r
7314     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7315     sizeX = newSizeX;\r
7316     sizeY = newSizeY;\r
7317     break;\r
7318 \r
7319   case WM_GETMINMAXINFO:\r
7320     /* Prevent resizing window too small */\r
7321     mmi = (MINMAXINFO *) lParam;\r
7322     mmi->ptMinTrackSize.x = 100;\r
7323     mmi->ptMinTrackSize.y = 100;\r
7324     break;\r
7325   }\r
7326   return FALSE;\r
7327 }\r
7328 \r
7329 VOID\r
7330 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7331 {\r
7332   FARPROC lpProc;\r
7333   char *p, *q;\r
7334 \r
7335   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7336 \r
7337   if (str == NULL) str = "";\r
7338   p = (char *) malloc(2 * strlen(str) + 2);\r
7339   q = p;\r
7340   while (*str) {\r
7341     if (*str == '\n') *q++ = '\r';\r
7342     *q++ = *str++;\r
7343   }\r
7344   *q = NULLCHAR;\r
7345   if (commentText != NULL) free(commentText);\r
7346 \r
7347   commentIndex = index;\r
7348   commentTitle = title;\r
7349   commentText = p;\r
7350   editComment = edit;\r
7351 \r
7352   if (commentDialog) {\r
7353     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7354     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7355   } else {\r
7356     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7357     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7358                  hwndMain, (DLGPROC)lpProc);\r
7359     FreeProcInstance(lpProc);\r
7360   }\r
7361   commentDialogUp = TRUE;\r
7362 }\r
7363 \r
7364 \r
7365 /*---------------------------------------------------------------------------*\\r
7366  *\r
7367  * Type-in move dialog functions\r
7368  * \r
7369 \*---------------------------------------------------------------------------*/\r
7370 \r
7371 LRESULT CALLBACK\r
7372 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7373 {\r
7374   char move[MSG_SIZ];\r
7375   HWND hInput;\r
7376   ChessMove moveType;\r
7377   int fromX, fromY, toX, toY;\r
7378   char promoChar;\r
7379 \r
7380   switch (message) {\r
7381   case WM_INITDIALOG:\r
7382     move[0] = (char) lParam;\r
7383     move[1] = NULLCHAR;\r
7384     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7385     hInput = GetDlgItem(hDlg, OPT_Move);\r
7386     SetWindowText(hInput, move);\r
7387     SetFocus(hInput);\r
7388     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7389     return FALSE;\r
7390 \r
7391   case WM_COMMAND:\r
7392     switch (LOWORD(wParam)) {\r
7393     case IDOK:\r
7394       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7395       { int n; Board board;\r
7396         // [HGM] FENedit\r
7397         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7398                 EditPositionPasteFEN(move);\r
7399                 EndDialog(hDlg, TRUE);\r
7400                 return TRUE;\r
7401         }\r
7402         // [HGM] movenum: allow move number to be typed in any mode\r
7403         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7404           currentMove = 2*n-1;\r
7405           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7406           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7407           EndDialog(hDlg, TRUE);\r
7408           DrawPosition(TRUE, boards[currentMove]);\r
7409           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7410           else DisplayMessage("", "");\r
7411           return TRUE;\r
7412         }\r
7413       }\r
7414       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7415         gameMode != Training) {\r
7416         DisplayMoveError("Displayed move is not current");\r
7417       } else {\r
7418 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7419         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7420           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7421         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7422         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7423           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7424           if (gameMode != Training)\r
7425               forwardMostMove = currentMove;\r
7426           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7427         } else {\r
7428           DisplayMoveError("Could not parse move");\r
7429         }\r
7430       }\r
7431       EndDialog(hDlg, TRUE);\r
7432       return TRUE;\r
7433     case IDCANCEL:\r
7434       EndDialog(hDlg, FALSE);\r
7435       return TRUE;\r
7436     default:\r
7437       break;\r
7438     }\r
7439     break;\r
7440   }\r
7441   return FALSE;\r
7442 }\r
7443 \r
7444 VOID\r
7445 PopUpMoveDialog(char firstchar)\r
7446 {\r
7447     FARPROC lpProc;\r
7448     \r
7449     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7450         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7451         gameMode == AnalyzeMode || gameMode == EditGame || \r
7452         gameMode == EditPosition || gameMode == IcsExamining ||\r
7453         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7454         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7455                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7456                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7457         gameMode == Training) {\r
7458       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7459       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7460         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7461       FreeProcInstance(lpProc);\r
7462     }\r
7463 }\r
7464 \r
7465 /*---------------------------------------------------------------------------*\\r
7466  *\r
7467  * Type-in name dialog functions\r
7468  * \r
7469 \*---------------------------------------------------------------------------*/\r
7470 \r
7471 LRESULT CALLBACK\r
7472 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7473 {\r
7474   char move[MSG_SIZ];\r
7475   HWND hInput;\r
7476 \r
7477   switch (message) {\r
7478   case WM_INITDIALOG:\r
7479     move[0] = (char) lParam;\r
7480     move[1] = NULLCHAR;\r
7481     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7482     hInput = GetDlgItem(hDlg, OPT_Name);\r
7483     SetWindowText(hInput, move);\r
7484     SetFocus(hInput);\r
7485     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7486     return FALSE;\r
7487 \r
7488   case WM_COMMAND:\r
7489     switch (LOWORD(wParam)) {\r
7490     case IDOK:\r
7491       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7492       appData.userName = strdup(move);\r
7493       SetUserLogo();\r
7494 \r
7495       EndDialog(hDlg, TRUE);\r
7496       return TRUE;\r
7497     case IDCANCEL:\r
7498       EndDialog(hDlg, FALSE);\r
7499       return TRUE;\r
7500     default:\r
7501       break;\r
7502     }\r
7503     break;\r
7504   }\r
7505   return FALSE;\r
7506 }\r
7507 \r
7508 VOID\r
7509 PopUpNameDialog(char firstchar)\r
7510 {\r
7511     FARPROC lpProc;\r
7512     \r
7513       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7514       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7515         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7516       FreeProcInstance(lpProc);\r
7517 }\r
7518 \r
7519 /*---------------------------------------------------------------------------*\\r
7520  *\r
7521  *  Error dialogs\r
7522  * \r
7523 \*---------------------------------------------------------------------------*/\r
7524 \r
7525 /* Nonmodal error box */\r
7526 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7527                              WPARAM wParam, LPARAM lParam);\r
7528 \r
7529 VOID\r
7530 ErrorPopUp(char *title, char *content)\r
7531 {\r
7532   FARPROC lpProc;\r
7533   char *p, *q;\r
7534   BOOLEAN modal = hwndMain == NULL;\r
7535 \r
7536   p = content;\r
7537   q = errorMessage;\r
7538   while (*p) {\r
7539     if (*p == '\n') {\r
7540       if (modal) {\r
7541         *q++ = ' ';\r
7542         p++;\r
7543       } else {\r
7544         *q++ = '\r';\r
7545         *q++ = *p++;\r
7546       }\r
7547     } else {\r
7548       *q++ = *p++;\r
7549     }\r
7550   }\r
7551   *q = NULLCHAR;\r
7552   strncpy(errorTitle, title, sizeof(errorTitle));\r
7553   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7554   \r
7555   if (modal) {\r
7556     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7557   } else {\r
7558     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7559     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7560                  hwndMain, (DLGPROC)lpProc);\r
7561     FreeProcInstance(lpProc);\r
7562   }\r
7563 }\r
7564 \r
7565 VOID\r
7566 ErrorPopDown()\r
7567 {\r
7568   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7569   if (errorDialog == NULL) return;\r
7570   DestroyWindow(errorDialog);\r
7571   errorDialog = NULL;\r
7572 }\r
7573 \r
7574 LRESULT CALLBACK\r
7575 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7576 {\r
7577   HANDLE hwndText;\r
7578   RECT rChild;\r
7579 \r
7580   switch (message) {\r
7581   case WM_INITDIALOG:\r
7582     GetWindowRect(hDlg, &rChild);\r
7583 \r
7584     /*\r
7585     SetWindowPos(hDlg, NULL, rChild.left,\r
7586       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7587       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7588     */\r
7589 \r
7590     /* \r
7591         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7592         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7593         and it doesn't work when you resize the dialog.\r
7594         For now, just give it a default position.\r
7595     */\r
7596     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7597 \r
7598     errorDialog = hDlg;\r
7599     SetWindowText(hDlg, errorTitle);\r
7600     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7601     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7602     return FALSE;\r
7603 \r
7604   case WM_COMMAND:\r
7605     switch (LOWORD(wParam)) {\r
7606     case IDOK:\r
7607     case IDCANCEL:\r
7608       if (errorDialog == hDlg) errorDialog = NULL;\r
7609       DestroyWindow(hDlg);\r
7610       return TRUE;\r
7611 \r
7612     default:\r
7613       break;\r
7614     }\r
7615     break;\r
7616   }\r
7617   return FALSE;\r
7618 }\r
7619 \r
7620 #ifdef GOTHIC\r
7621 HWND gothicDialog = NULL;\r
7622 \r
7623 LRESULT CALLBACK\r
7624 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7625 {\r
7626   HANDLE hwndText;\r
7627   RECT rChild;\r
7628   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7629 \r
7630   switch (message) {\r
7631   case WM_INITDIALOG:\r
7632     GetWindowRect(hDlg, &rChild);\r
7633 \r
7634     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7635                                                              SWP_NOZORDER);\r
7636 \r
7637     /* \r
7638         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7639         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7640         and it doesn't work when you resize the dialog.\r
7641         For now, just give it a default position.\r
7642     */\r
7643     gothicDialog = hDlg;\r
7644     SetWindowText(hDlg, errorTitle);\r
7645     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7646     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7647     return FALSE;\r
7648 \r
7649   case WM_COMMAND:\r
7650     switch (LOWORD(wParam)) {\r
7651     case IDOK:\r
7652     case IDCANCEL:\r
7653       if (errorDialog == hDlg) errorDialog = NULL;\r
7654       DestroyWindow(hDlg);\r
7655       return TRUE;\r
7656 \r
7657     default:\r
7658       break;\r
7659     }\r
7660     break;\r
7661   }\r
7662   return FALSE;\r
7663 }\r
7664 \r
7665 VOID\r
7666 GothicPopUp(char *title, VariantClass variant)\r
7667 {\r
7668   FARPROC lpProc;\r
7669   static char *lastTitle;\r
7670 \r
7671   strncpy(errorTitle, title, sizeof(errorTitle));\r
7672   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7673 \r
7674   if(lastTitle != title && gothicDialog != NULL) {\r
7675     DestroyWindow(gothicDialog);\r
7676     gothicDialog = NULL;\r
7677   }\r
7678   if(variant != VariantNormal && gothicDialog == NULL) {\r
7679     title = lastTitle;\r
7680     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7681     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7682                  hwndMain, (DLGPROC)lpProc);\r
7683     FreeProcInstance(lpProc);\r
7684   }\r
7685 }\r
7686 #endif\r
7687 \r
7688 /*---------------------------------------------------------------------------*\\r
7689  *\r
7690  *  Ics Interaction console functions\r
7691  *\r
7692 \*---------------------------------------------------------------------------*/\r
7693 \r
7694 #define HISTORY_SIZE 64\r
7695 static char *history[HISTORY_SIZE];\r
7696 int histIn = 0, histP = 0;\r
7697 \r
7698 VOID\r
7699 SaveInHistory(char *cmd)\r
7700 {\r
7701   if (history[histIn] != NULL) {\r
7702     free(history[histIn]);\r
7703     history[histIn] = NULL;\r
7704   }\r
7705   if (*cmd == NULLCHAR) return;\r
7706   history[histIn] = StrSave(cmd);\r
7707   histIn = (histIn + 1) % HISTORY_SIZE;\r
7708   if (history[histIn] != NULL) {\r
7709     free(history[histIn]);\r
7710     history[histIn] = NULL;\r
7711   }\r
7712   histP = histIn;\r
7713 }\r
7714 \r
7715 char *\r
7716 PrevInHistory(char *cmd)\r
7717 {\r
7718   int newhp;\r
7719   if (histP == histIn) {\r
7720     if (history[histIn] != NULL) free(history[histIn]);\r
7721     history[histIn] = StrSave(cmd);\r
7722   }\r
7723   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7724   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7725   histP = newhp;\r
7726   return history[histP];\r
7727 }\r
7728 \r
7729 char *\r
7730 NextInHistory()\r
7731 {\r
7732   if (histP == histIn) return NULL;\r
7733   histP = (histP + 1) % HISTORY_SIZE;\r
7734   return history[histP];\r
7735 }\r
7736 \r
7737 typedef struct {\r
7738   char *item;\r
7739   char *command;\r
7740   BOOLEAN getname;\r
7741   BOOLEAN immediate;\r
7742 } IcsTextMenuEntry;\r
7743 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7744 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7745 \r
7746 void\r
7747 ParseIcsTextMenu(char *icsTextMenuString)\r
7748 {\r
7749 //  int flags = 0;\r
7750   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7751   char *p = icsTextMenuString;\r
7752   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7753     free(e->item);\r
7754     e->item = NULL;\r
7755     if (e->command != NULL) {\r
7756       free(e->command);\r
7757       e->command = NULL;\r
7758     }\r
7759     e++;\r
7760   }\r
7761   e = icsTextMenuEntry;\r
7762   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7763     if (*p == ';' || *p == '\n') {\r
7764       e->item = strdup("-");\r
7765       e->command = NULL;\r
7766       p++;\r
7767     } else if (*p == '-') {\r
7768       e->item = strdup("-");\r
7769       e->command = NULL;\r
7770       p++;\r
7771       if (*p) p++;\r
7772     } else {\r
7773       char *q, *r, *s, *t;\r
7774       char c;\r
7775       q = strchr(p, ',');\r
7776       if (q == NULL) break;\r
7777       *q = NULLCHAR;\r
7778       r = strchr(q + 1, ',');\r
7779       if (r == NULL) break;\r
7780       *r = NULLCHAR;\r
7781       s = strchr(r + 1, ',');\r
7782       if (s == NULL) break;\r
7783       *s = NULLCHAR;\r
7784       c = ';';\r
7785       t = strchr(s + 1, c);\r
7786       if (t == NULL) {\r
7787         c = '\n';\r
7788         t = strchr(s + 1, c);\r
7789       }\r
7790       if (t != NULL) *t = NULLCHAR;\r
7791       e->item = strdup(p);\r
7792       e->command = strdup(q + 1);\r
7793       e->getname = *(r + 1) != '0';\r
7794       e->immediate = *(s + 1) != '0';\r
7795       *q = ',';\r
7796       *r = ',';\r
7797       *s = ',';\r
7798       if (t == NULL) break;\r
7799       *t = c;\r
7800       p = t + 1;\r
7801     }\r
7802     e++;\r
7803   } \r
7804 }\r
7805 \r
7806 HMENU\r
7807 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7808 {\r
7809   HMENU hmenu, h;\r
7810   int i = 0;\r
7811   hmenu = LoadMenu(hInst, "TextMenu");\r
7812   h = GetSubMenu(hmenu, 0);\r
7813   while (e->item) {\r
7814     if (strcmp(e->item, "-") == 0) {\r
7815       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7816     } else {\r
7817       if (e->item[0] == '|') {\r
7818         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7819                    IDM_CommandX + i, &e->item[1]);\r
7820       } else {\r
7821         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7822       }\r
7823     }\r
7824     e++;\r
7825     i++;\r
7826   } \r
7827   return hmenu;\r
7828 }\r
7829 \r
7830 WNDPROC consoleTextWindowProc;\r
7831 \r
7832 void\r
7833 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7834 {\r
7835   char buf[MSG_SIZ], name[MSG_SIZ];\r
7836   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7837   CHARRANGE sel;\r
7838 \r
7839   if (!getname) {\r
7840     SetWindowText(hInput, command);\r
7841     if (immediate) {\r
7842       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7843     } else {\r
7844       sel.cpMin = 999999;\r
7845       sel.cpMax = 999999;\r
7846       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7847       SetFocus(hInput);\r
7848     }\r
7849     return;\r
7850   }    \r
7851   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7852   if (sel.cpMin == sel.cpMax) {\r
7853     /* Expand to surrounding word */\r
7854     TEXTRANGE tr;\r
7855     do {\r
7856       tr.chrg.cpMax = sel.cpMin;\r
7857       tr.chrg.cpMin = --sel.cpMin;\r
7858       if (sel.cpMin < 0) break;\r
7859       tr.lpstrText = name;\r
7860       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7861     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7862     sel.cpMin++;\r
7863 \r
7864     do {\r
7865       tr.chrg.cpMin = sel.cpMax;\r
7866       tr.chrg.cpMax = ++sel.cpMax;\r
7867       tr.lpstrText = name;\r
7868       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7869     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7870     sel.cpMax--;\r
7871 \r
7872     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7873       MessageBeep(MB_ICONEXCLAMATION);\r
7874       return;\r
7875     }\r
7876     tr.chrg = sel;\r
7877     tr.lpstrText = name;\r
7878     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7879   } else {\r
7880     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7881       MessageBeep(MB_ICONEXCLAMATION);\r
7882       return;\r
7883     }\r
7884     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7885   }\r
7886   if (immediate) {\r
7887     sprintf(buf, "%s %s", command, name);\r
7888     SetWindowText(hInput, buf);\r
7889     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7890   } else {\r
7891     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7892     SetWindowText(hInput, buf);\r
7893     sel.cpMin = 999999;\r
7894     sel.cpMax = 999999;\r
7895     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7896     SetFocus(hInput);\r
7897   }\r
7898 }\r
7899 \r
7900 LRESULT CALLBACK \r
7901 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7902 {\r
7903   HWND hInput;\r
7904   CHARRANGE sel;\r
7905 \r
7906   switch (message) {\r
7907   case WM_KEYDOWN:\r
7908     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7909     switch (wParam) {\r
7910     case VK_PRIOR:\r
7911       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7912       return 0;\r
7913     case VK_NEXT:\r
7914       sel.cpMin = 999999;\r
7915       sel.cpMax = 999999;\r
7916       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7917       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7918       return 0;\r
7919     }\r
7920     break;\r
7921   case WM_CHAR:\r
7922    if(wParam != '\022') {\r
7923     if (wParam == '\t') {\r
7924       if (GetKeyState(VK_SHIFT) < 0) {\r
7925         /* shifted */\r
7926         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7927         if (buttonDesc[0].hwnd) {\r
7928           SetFocus(buttonDesc[0].hwnd);\r
7929         } else {\r
7930           SetFocus(hwndMain);\r
7931         }\r
7932       } else {\r
7933         /* unshifted */\r
7934         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7935       }\r
7936     } else {\r
7937       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7938       JAWS_DELETE( SetFocus(hInput); )\r
7939       SendMessage(hInput, message, wParam, lParam);\r
7940     }\r
7941     return 0;\r
7942    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7943   case WM_RBUTTONUP:\r
7944     if (GetKeyState(VK_SHIFT) & ~1) {\r
7945       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7946         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7947     } else {\r
7948       POINT pt;\r
7949       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7950       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7951       if (sel.cpMin == sel.cpMax) {\r
7952         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7953         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7954       }\r
7955       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7956         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7957       }\r
7958       pt.x = LOWORD(lParam);\r
7959       pt.y = HIWORD(lParam);\r
7960       MenuPopup(hwnd, pt, hmenu, -1);\r
7961     }\r
7962     return 0;\r
7963   case WM_PASTE:\r
7964     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7965     SetFocus(hInput);\r
7966     return SendMessage(hInput, message, wParam, lParam);\r
7967   case WM_MBUTTONDOWN:\r
7968     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7969   case WM_RBUTTONDOWN:\r
7970     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7971       /* Move selection here if it was empty */\r
7972       POINT pt;\r
7973       pt.x = LOWORD(lParam);\r
7974       pt.y = HIWORD(lParam);\r
7975       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7976       if (sel.cpMin == sel.cpMax) {\r
7977         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7978         sel.cpMax = sel.cpMin;\r
7979         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7980       }\r
7981       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7982     }\r
7983     return 0;\r
7984   case WM_COMMAND:\r
7985     switch (LOWORD(wParam)) {\r
7986     case IDM_QuickPaste:\r
7987       {\r
7988         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7989         if (sel.cpMin == sel.cpMax) {\r
7990           MessageBeep(MB_ICONEXCLAMATION);\r
7991           return 0;\r
7992         }\r
7993         SendMessage(hwnd, WM_COPY, 0, 0);\r
7994         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7995         SendMessage(hInput, WM_PASTE, 0, 0);\r
7996         SetFocus(hInput);\r
7997         return 0;\r
7998       }\r
7999     case IDM_Cut:\r
8000       SendMessage(hwnd, WM_CUT, 0, 0);\r
8001       return 0;\r
8002     case IDM_Paste:\r
8003       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8004       return 0;\r
8005     case IDM_Copy:\r
8006       SendMessage(hwnd, WM_COPY, 0, 0);\r
8007       return 0;\r
8008     default:\r
8009       {\r
8010         int i = LOWORD(wParam) - IDM_CommandX;\r
8011         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
8012             icsTextMenuEntry[i].command != NULL) {\r
8013           CommandX(hwnd, icsTextMenuEntry[i].command,\r
8014                    icsTextMenuEntry[i].getname,\r
8015                    icsTextMenuEntry[i].immediate);\r
8016           return 0;\r
8017         }\r
8018       }\r
8019       break;\r
8020     }\r
8021     break;\r
8022   }\r
8023   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
8024 }\r
8025 \r
8026 WNDPROC consoleInputWindowProc;\r
8027 \r
8028 LRESULT CALLBACK\r
8029 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8030 {\r
8031   char buf[MSG_SIZ];\r
8032   char *p;\r
8033   static BOOL sendNextChar = FALSE;\r
8034   static BOOL quoteNextChar = FALSE;\r
8035   InputSource *is = consoleInputSource;\r
8036   CHARFORMAT cf;\r
8037   CHARRANGE sel;\r
8038 \r
8039   switch (message) {\r
8040   case WM_CHAR:\r
8041     if (!appData.localLineEditing || sendNextChar) {\r
8042       is->buf[0] = (CHAR) wParam;\r
8043       is->count = 1;\r
8044       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8045       sendNextChar = FALSE;\r
8046       return 0;\r
8047     }\r
8048     if (quoteNextChar) {\r
8049       buf[0] = (char) wParam;\r
8050       buf[1] = NULLCHAR;\r
8051       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
8052       quoteNextChar = FALSE;\r
8053       return 0;\r
8054     }\r
8055     switch (wParam) {\r
8056     case '\r':   /* Enter key */\r
8057       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
8058       if (consoleEcho) SaveInHistory(is->buf);\r
8059       is->buf[is->count++] = '\n';\r
8060       is->buf[is->count] = NULLCHAR;\r
8061       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8062       if (consoleEcho) {\r
8063         ConsoleOutput(is->buf, is->count, TRUE);\r
8064       } else if (appData.localLineEditing) {\r
8065         ConsoleOutput("\n", 1, TRUE);\r
8066       }\r
8067       /* fall thru */\r
8068     case '\033': /* Escape key */\r
8069       SetWindowText(hwnd, "");\r
8070       cf.cbSize = sizeof(CHARFORMAT);\r
8071       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8072       if (consoleEcho) {\r
8073         cf.crTextColor = textAttribs[ColorNormal].color;\r
8074       } else {\r
8075         cf.crTextColor = COLOR_ECHOOFF;\r
8076       }\r
8077       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8078       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8079       return 0;\r
8080     case '\t':   /* Tab key */\r
8081       if (GetKeyState(VK_SHIFT) < 0) {\r
8082         /* shifted */\r
8083         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8084       } else {\r
8085         /* unshifted */\r
8086         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8087         if (buttonDesc[0].hwnd) {\r
8088           SetFocus(buttonDesc[0].hwnd);\r
8089         } else {\r
8090           SetFocus(hwndMain);\r
8091         }\r
8092       }\r
8093       return 0;\r
8094     case '\023': /* Ctrl+S */\r
8095       sendNextChar = TRUE;\r
8096       return 0;\r
8097     case '\021': /* Ctrl+Q */\r
8098       quoteNextChar = TRUE;\r
8099       return 0;\r
8100     JAWS_REPLAY\r
8101     default:\r
8102       break;\r
8103     }\r
8104     break;\r
8105   case WM_KEYDOWN:\r
8106     switch (wParam) {\r
8107     case VK_UP:\r
8108       GetWindowText(hwnd, buf, MSG_SIZ);\r
8109       p = PrevInHistory(buf);\r
8110       if (p != NULL) {\r
8111         SetWindowText(hwnd, p);\r
8112         sel.cpMin = 999999;\r
8113         sel.cpMax = 999999;\r
8114         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8115         return 0;\r
8116       }\r
8117       break;\r
8118     case VK_DOWN:\r
8119       p = NextInHistory();\r
8120       if (p != NULL) {\r
8121         SetWindowText(hwnd, p);\r
8122         sel.cpMin = 999999;\r
8123         sel.cpMax = 999999;\r
8124         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8125         return 0;\r
8126       }\r
8127       break;\r
8128     case VK_HOME:\r
8129     case VK_END:\r
8130       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8131       /* fall thru */\r
8132     case VK_PRIOR:\r
8133     case VK_NEXT:\r
8134       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8135       return 0;\r
8136     }\r
8137     break;\r
8138   case WM_MBUTTONDOWN:\r
8139     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8140       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8141     break;\r
8142   case WM_RBUTTONUP:\r
8143     if (GetKeyState(VK_SHIFT) & ~1) {\r
8144       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8145         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8146     } else {\r
8147       POINT pt;\r
8148       HMENU hmenu;\r
8149       hmenu = LoadMenu(hInst, "InputMenu");\r
8150       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8151       if (sel.cpMin == sel.cpMax) {\r
8152         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8153         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8154       }\r
8155       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8156         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8157       }\r
8158       pt.x = LOWORD(lParam);\r
8159       pt.y = HIWORD(lParam);\r
8160       MenuPopup(hwnd, pt, hmenu, -1);\r
8161     }\r
8162     return 0;\r
8163   case WM_COMMAND:\r
8164     switch (LOWORD(wParam)) { \r
8165     case IDM_Undo:\r
8166       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8167       return 0;\r
8168     case IDM_SelectAll:\r
8169       sel.cpMin = 0;\r
8170       sel.cpMax = -1; /*999999?*/\r
8171       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8172       return 0;\r
8173     case IDM_Cut:\r
8174       SendMessage(hwnd, WM_CUT, 0, 0);\r
8175       return 0;\r
8176     case IDM_Paste:\r
8177       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8178       return 0;\r
8179     case IDM_Copy:\r
8180       SendMessage(hwnd, WM_COPY, 0, 0);\r
8181       return 0;\r
8182     }\r
8183     break;\r
8184   }\r
8185   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8186 }\r
8187 \r
8188 #define CO_MAX  100000\r
8189 #define CO_TRIM   1000\r
8190 \r
8191 LRESULT CALLBACK\r
8192 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8193 {\r
8194   static SnapData sd;\r
8195   static HWND hText, hInput /*, hFocus*/;\r
8196 //  InputSource *is = consoleInputSource;\r
8197   RECT rect;\r
8198   static int sizeX, sizeY;\r
8199   int newSizeX, newSizeY;\r
8200   MINMAXINFO *mmi;\r
8201 \r
8202   switch (message) {\r
8203   case WM_INITDIALOG: /* message: initialize dialog box */\r
8204     hwndConsole = hDlg;\r
8205     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8206     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8207     SetFocus(hInput);\r
8208     consoleTextWindowProc = (WNDPROC)\r
8209       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8210     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8211     consoleInputWindowProc = (WNDPROC)\r
8212       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8213     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8214     Colorize(ColorNormal, TRUE);\r
8215     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8216     ChangedConsoleFont();\r
8217     GetClientRect(hDlg, &rect);\r
8218     sizeX = rect.right;\r
8219     sizeY = rect.bottom;\r
8220     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8221         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8222       WINDOWPLACEMENT wp;\r
8223       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8224       wp.length = sizeof(WINDOWPLACEMENT);\r
8225       wp.flags = 0;\r
8226       wp.showCmd = SW_SHOW;\r
8227       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8228       wp.rcNormalPosition.left = wpConsole.x;\r
8229       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8230       wp.rcNormalPosition.top = wpConsole.y;\r
8231       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8232       SetWindowPlacement(hDlg, &wp);\r
8233     }\r
8234 #if 1\r
8235    // [HGM] Chessknight's change 2004-07-13\r
8236    else { /* Determine Defaults */\r
8237        WINDOWPLACEMENT wp;\r
8238        wpConsole.x = winWidth + 1;\r
8239        wpConsole.y = boardY;\r
8240        wpConsole.width = screenWidth -  winWidth;\r
8241        wpConsole.height = winHeight;\r
8242        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8243        wp.length = sizeof(WINDOWPLACEMENT);\r
8244        wp.flags = 0;\r
8245        wp.showCmd = SW_SHOW;\r
8246        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8247        wp.rcNormalPosition.left = wpConsole.x;\r
8248        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8249        wp.rcNormalPosition.top = wpConsole.y;\r
8250        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8251        SetWindowPlacement(hDlg, &wp);\r
8252     }\r
8253 #endif\r
8254     return FALSE;\r
8255 \r
8256   case WM_SETFOCUS:\r
8257     SetFocus(hInput);\r
8258     return 0;\r
8259 \r
8260   case WM_CLOSE:\r
8261     ExitEvent(0);\r
8262     /* not reached */\r
8263     break;\r
8264 \r
8265   case WM_SIZE:\r
8266     if (IsIconic(hDlg)) break;\r
8267     newSizeX = LOWORD(lParam);\r
8268     newSizeY = HIWORD(lParam);\r
8269     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8270       RECT rectText, rectInput;\r
8271       POINT pt;\r
8272       int newTextHeight, newTextWidth;\r
8273       GetWindowRect(hText, &rectText);\r
8274       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8275       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8276       if (newTextHeight < 0) {\r
8277         newSizeY += -newTextHeight;\r
8278         newTextHeight = 0;\r
8279       }\r
8280       SetWindowPos(hText, NULL, 0, 0,\r
8281         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8282       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8283       pt.x = rectInput.left;\r
8284       pt.y = rectInput.top + newSizeY - sizeY;\r
8285       ScreenToClient(hDlg, &pt);\r
8286       SetWindowPos(hInput, NULL, \r
8287         pt.x, pt.y, /* needs client coords */   \r
8288         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8289         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8290     }\r
8291     sizeX = newSizeX;\r
8292     sizeY = newSizeY;\r
8293     break;\r
8294 \r
8295   case WM_GETMINMAXINFO:\r
8296     /* Prevent resizing window too small */\r
8297     mmi = (MINMAXINFO *) lParam;\r
8298     mmi->ptMinTrackSize.x = 100;\r
8299     mmi->ptMinTrackSize.y = 100;\r
8300     break;\r
8301 \r
8302   /* [AS] Snapping */\r
8303   case WM_ENTERSIZEMOVE:\r
8304     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8305 \r
8306   case WM_SIZING:\r
8307     return OnSizing( &sd, hDlg, wParam, lParam );\r
8308 \r
8309   case WM_MOVING:\r
8310     return OnMoving( &sd, hDlg, wParam, lParam );\r
8311 \r
8312   case WM_EXITSIZEMOVE:\r
8313     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8314   }\r
8315 \r
8316   return DefWindowProc(hDlg, message, wParam, lParam);\r
8317 }\r
8318 \r
8319 \r
8320 VOID\r
8321 ConsoleCreate()\r
8322 {\r
8323   HWND hCons;\r
8324   if (hwndConsole) return;\r
8325   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8326   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8327 }\r
8328 \r
8329 \r
8330 VOID\r
8331 ConsoleOutput(char* data, int length, int forceVisible)\r
8332 {\r
8333   HWND hText;\r
8334   int trim, exlen;\r
8335   char *p, *q;\r
8336   char buf[CO_MAX+1];\r
8337   POINT pEnd;\r
8338   RECT rect;\r
8339   static int delayLF = 0;\r
8340   CHARRANGE savesel, sel;\r
8341 \r
8342   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8343   p = data;\r
8344   q = buf;\r
8345   if (delayLF) {\r
8346     *q++ = '\r';\r
8347     *q++ = '\n';\r
8348     delayLF = 0;\r
8349   }\r
8350   while (length--) {\r
8351     if (*p == '\n') {\r
8352       if (*++p) {\r
8353         *q++ = '\r';\r
8354         *q++ = '\n';\r
8355       } else {\r
8356         delayLF = 1;\r
8357       }\r
8358     } else if (*p == '\007') {\r
8359        MyPlaySound(&sounds[(int)SoundBell]);\r
8360        p++;\r
8361     } else {\r
8362       *q++ = *p++;\r
8363     }\r
8364   }\r
8365   *q = NULLCHAR;\r
8366   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8367   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8368   /* Save current selection */\r
8369   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8370   exlen = GetWindowTextLength(hText);\r
8371   /* Find out whether current end of text is visible */\r
8372   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8373   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8374   /* Trim existing text if it's too long */\r
8375   if (exlen + (q - buf) > CO_MAX) {\r
8376     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8377     sel.cpMin = 0;\r
8378     sel.cpMax = trim;\r
8379     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8380     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8381     exlen -= trim;\r
8382     savesel.cpMin -= trim;\r
8383     savesel.cpMax -= trim;\r
8384     if (exlen < 0) exlen = 0;\r
8385     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8386     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8387   }\r
8388   /* Append the new text */\r
8389   sel.cpMin = exlen;\r
8390   sel.cpMax = exlen;\r
8391   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8392   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8393   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8394   if (forceVisible || exlen == 0 ||\r
8395       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8396        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8397     /* Scroll to make new end of text visible if old end of text\r
8398        was visible or new text is an echo of user typein */\r
8399     sel.cpMin = 9999999;\r
8400     sel.cpMax = 9999999;\r
8401     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8402     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8403     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8404     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8405   }\r
8406   if (savesel.cpMax == exlen || forceVisible) {\r
8407     /* Move insert point to new end of text if it was at the old\r
8408        end of text or if the new text is an echo of user typein */\r
8409     sel.cpMin = 9999999;\r
8410     sel.cpMax = 9999999;\r
8411     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8412   } else {\r
8413     /* Restore previous selection */\r
8414     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8415   }\r
8416   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8417 }\r
8418 \r
8419 /*---------*/\r
8420 \r
8421 \r
8422 void\r
8423 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8424 {\r
8425   char buf[100];\r
8426   char *str;\r
8427   COLORREF oldFg, oldBg;\r
8428   HFONT oldFont;\r
8429   RECT rect;\r
8430 \r
8431   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8432 \r
8433   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8434   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8435   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8436 \r
8437   rect.left = x;\r
8438   rect.right = x + squareSize;\r
8439   rect.top  = y;\r
8440   rect.bottom = y + squareSize;\r
8441   str = buf;\r
8442 \r
8443   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8444                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8445              y, ETO_CLIPPED|ETO_OPAQUE,\r
8446              &rect, str, strlen(str), NULL);\r
8447 \r
8448   (void) SetTextColor(hdc, oldFg);\r
8449   (void) SetBkColor(hdc, oldBg);\r
8450   (void) SelectObject(hdc, oldFont);\r
8451 }\r
8452 \r
8453 void\r
8454 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8455               RECT *rect, char *color, char *flagFell)\r
8456 {\r
8457   char buf[100];\r
8458   char *str;\r
8459   COLORREF oldFg, oldBg;\r
8460   HFONT oldFont;\r
8461 \r
8462   if (appData.clockMode) {\r
8463     if (tinyLayout)\r
8464       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8465     else\r
8466       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8467     str = buf;\r
8468   } else {\r
8469     str = color;\r
8470   }\r
8471 \r
8472   if (highlight) {\r
8473     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8474     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8475   } else {\r
8476     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8477     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8478   }\r
8479   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8480 \r
8481   JAWS_SILENCE\r
8482 \r
8483   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8484              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8485              rect, str, strlen(str), NULL);\r
8486   if(logoHeight > 0 && appData.clockMode) {\r
8487       RECT r;\r
8488       sprintf(buf, "%s %s", buf+7, flagFell);\r
8489       r.top = rect->top + logoHeight/2;\r
8490       r.left = rect->left;\r
8491       r.right = rect->right;\r
8492       r.bottom = rect->bottom;\r
8493       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8494                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8495                  &r, str, strlen(str), NULL);\r
8496   }\r
8497   (void) SetTextColor(hdc, oldFg);\r
8498   (void) SetBkColor(hdc, oldBg);\r
8499   (void) SelectObject(hdc, oldFont);\r
8500 }\r
8501 \r
8502 \r
8503 int\r
8504 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8505            OVERLAPPED *ovl)\r
8506 {\r
8507   int ok, err;\r
8508 \r
8509   /* [AS]  */\r
8510   if( count <= 0 ) {\r
8511     if (appData.debugMode) {\r
8512       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8513     }\r
8514 \r
8515     return ERROR_INVALID_USER_BUFFER;\r
8516   }\r
8517 \r
8518   ResetEvent(ovl->hEvent);\r
8519   ovl->Offset = ovl->OffsetHigh = 0;\r
8520   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8521   if (ok) {\r
8522     err = NO_ERROR;\r
8523   } else {\r
8524     err = GetLastError();\r
8525     if (err == ERROR_IO_PENDING) {\r
8526       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8527       if (ok)\r
8528         err = NO_ERROR;\r
8529       else\r
8530         err = GetLastError();\r
8531     }\r
8532   }\r
8533   return err;\r
8534 }\r
8535 \r
8536 int\r
8537 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8538             OVERLAPPED *ovl)\r
8539 {\r
8540   int ok, err;\r
8541 \r
8542   ResetEvent(ovl->hEvent);\r
8543   ovl->Offset = ovl->OffsetHigh = 0;\r
8544   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8545   if (ok) {\r
8546     err = NO_ERROR;\r
8547   } else {\r
8548     err = GetLastError();\r
8549     if (err == ERROR_IO_PENDING) {\r
8550       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8551       if (ok)\r
8552         err = NO_ERROR;\r
8553       else\r
8554         err = GetLastError();\r
8555     }\r
8556   }\r
8557   return err;\r
8558 }\r
8559 \r
8560 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8561 void CheckForInputBufferFull( InputSource * is )\r
8562 {\r
8563     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8564         /* Look for end of line */\r
8565         char * p = is->buf;\r
8566         \r
8567         while( p < is->next && *p != '\n' ) {\r
8568             p++;\r
8569         }\r
8570 \r
8571         if( p >= is->next ) {\r
8572             if (appData.debugMode) {\r
8573                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8574             }\r
8575 \r
8576             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8577             is->count = (DWORD) -1;\r
8578             is->next = is->buf;\r
8579         }\r
8580     }\r
8581 }\r
8582 \r
8583 DWORD\r
8584 InputThread(LPVOID arg)\r
8585 {\r
8586   InputSource *is;\r
8587   OVERLAPPED ovl;\r
8588 \r
8589   is = (InputSource *) arg;\r
8590   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8591   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8592   while (is->hThread != NULL) {\r
8593     is->error = DoReadFile(is->hFile, is->next,\r
8594                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8595                            &is->count, &ovl);\r
8596     if (is->error == NO_ERROR) {\r
8597       is->next += is->count;\r
8598     } else {\r
8599       if (is->error == ERROR_BROKEN_PIPE) {\r
8600         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8601         is->count = 0;\r
8602       } else {\r
8603         is->count = (DWORD) -1;\r
8604         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8605         break; \r
8606       }\r
8607     }\r
8608 \r
8609     CheckForInputBufferFull( is );\r
8610 \r
8611     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8612 \r
8613     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8614 \r
8615     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8616   }\r
8617 \r
8618   CloseHandle(ovl.hEvent);\r
8619   CloseHandle(is->hFile);\r
8620 \r
8621   if (appData.debugMode) {\r
8622     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8623   }\r
8624 \r
8625   return 0;\r
8626 }\r
8627 \r
8628 \r
8629 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8630 DWORD\r
8631 NonOvlInputThread(LPVOID arg)\r
8632 {\r
8633   InputSource *is;\r
8634   char *p, *q;\r
8635   int i;\r
8636   char prev;\r
8637 \r
8638   is = (InputSource *) arg;\r
8639   while (is->hThread != NULL) {\r
8640     is->error = ReadFile(is->hFile, is->next,\r
8641                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8642                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8643     if (is->error == NO_ERROR) {\r
8644       /* Change CRLF to LF */\r
8645       if (is->next > is->buf) {\r
8646         p = is->next - 1;\r
8647         i = is->count + 1;\r
8648       } else {\r
8649         p = is->next;\r
8650         i = is->count;\r
8651       }\r
8652       q = p;\r
8653       prev = NULLCHAR;\r
8654       while (i > 0) {\r
8655         if (prev == '\r' && *p == '\n') {\r
8656           *(q-1) = '\n';\r
8657           is->count--;\r
8658         } else { \r
8659           *q++ = *p;\r
8660         }\r
8661         prev = *p++;\r
8662         i--;\r
8663       }\r
8664       *q = NULLCHAR;\r
8665       is->next = q;\r
8666     } else {\r
8667       if (is->error == ERROR_BROKEN_PIPE) {\r
8668         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8669         is->count = 0; \r
8670       } else {\r
8671         is->count = (DWORD) -1;\r
8672       }\r
8673     }\r
8674 \r
8675     CheckForInputBufferFull( is );\r
8676 \r
8677     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8678 \r
8679     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8680 \r
8681     if (is->count < 0) break;  /* Quit on error */\r
8682   }\r
8683   CloseHandle(is->hFile);\r
8684   return 0;\r
8685 }\r
8686 \r
8687 DWORD\r
8688 SocketInputThread(LPVOID arg)\r
8689 {\r
8690   InputSource *is;\r
8691 \r
8692   is = (InputSource *) arg;\r
8693   while (is->hThread != NULL) {\r
8694     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8695     if ((int)is->count == SOCKET_ERROR) {\r
8696       is->count = (DWORD) -1;\r
8697       is->error = WSAGetLastError();\r
8698     } else {\r
8699       is->error = NO_ERROR;\r
8700       is->next += is->count;\r
8701       if (is->count == 0 && is->second == is) {\r
8702         /* End of file on stderr; quit with no message */\r
8703         break;\r
8704       }\r
8705     }\r
8706     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8707 \r
8708     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8709 \r
8710     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8711   }\r
8712   return 0;\r
8713 }\r
8714 \r
8715 VOID\r
8716 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8717 {\r
8718   InputSource *is;\r
8719 \r
8720   is = (InputSource *) lParam;\r
8721   if (is->lineByLine) {\r
8722     /* Feed in lines one by one */\r
8723     char *p = is->buf;\r
8724     char *q = p;\r
8725     while (q < is->next) {\r
8726       if (*q++ == '\n') {\r
8727         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8728         p = q;\r
8729       }\r
8730     }\r
8731     \r
8732     /* Move any partial line to the start of the buffer */\r
8733     q = is->buf;\r
8734     while (p < is->next) {\r
8735       *q++ = *p++;\r
8736     }\r
8737     is->next = q;\r
8738 \r
8739     if (is->error != NO_ERROR || is->count == 0) {\r
8740       /* Notify backend of the error.  Note: If there was a partial\r
8741          line at the end, it is not flushed through. */\r
8742       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8743     }\r
8744   } else {\r
8745     /* Feed in the whole chunk of input at once */\r
8746     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8747     is->next = is->buf;\r
8748   }\r
8749 }\r
8750 \r
8751 /*---------------------------------------------------------------------------*\\r
8752  *\r
8753  *  Menu enables. Used when setting various modes.\r
8754  *\r
8755 \*---------------------------------------------------------------------------*/\r
8756 \r
8757 typedef struct {\r
8758   int item;\r
8759   int flags;\r
8760 } Enables;\r
8761 \r
8762 VOID\r
8763 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8764 {\r
8765   while (enab->item > 0) {\r
8766     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8767     enab++;\r
8768   }\r
8769 }\r
8770 \r
8771 Enables gnuEnables[] = {\r
8772   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8773   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8774   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8775   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8776   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8777   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8778   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8779   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8780   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8781   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8782   { -1, -1 }\r
8783 };\r
8784 \r
8785 Enables icsEnables[] = {\r
8786   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8787   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8788   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8789   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8790   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8791   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8792   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8793   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8794   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8795   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8796   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8797   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8798   { -1, -1 }\r
8799 };\r
8800 \r
8801 #ifdef ZIPPY\r
8802 Enables zippyEnables[] = {\r
8803   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8804   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8805   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8806   { -1, -1 }\r
8807 };\r
8808 #endif\r
8809 \r
8810 Enables ncpEnables[] = {\r
8811   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8812   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8813   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8814   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8815   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8816   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8817   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8818   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8819   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8820   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8821   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8822   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8823   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8824   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8825   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8826   { -1, -1 }\r
8827 };\r
8828 \r
8829 Enables trainingOnEnables[] = {\r
8830   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8831   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8832   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8833   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8834   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8835   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8836   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8837   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8838   { -1, -1 }\r
8839 };\r
8840 \r
8841 Enables trainingOffEnables[] = {\r
8842   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8843   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8844   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8845   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8846   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8847   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8848   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8849   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8850   { -1, -1 }\r
8851 };\r
8852 \r
8853 /* These modify either ncpEnables or gnuEnables */\r
8854 Enables cmailEnables[] = {\r
8855   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8856   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8857   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8858   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8859   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8860   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8861   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8862   { -1, -1 }\r
8863 };\r
8864 \r
8865 Enables machineThinkingEnables[] = {\r
8866   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8867   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8868   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8869   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8870   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8871   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8872   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8873   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8874   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8875   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8876   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8877   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8878   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8879   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8880   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8881   { -1, -1 }\r
8882 };\r
8883 \r
8884 Enables userThinkingEnables[] = {\r
8885   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8886   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8887   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8888   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8889   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8890   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8891   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8892   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8893   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8894   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8895   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8896   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8897   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8898   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8899   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8900   { -1, -1 }\r
8901 };\r
8902 \r
8903 /*---------------------------------------------------------------------------*\\r
8904  *\r
8905  *  Front-end interface functions exported by XBoard.\r
8906  *  Functions appear in same order as prototypes in frontend.h.\r
8907  * \r
8908 \*---------------------------------------------------------------------------*/\r
8909 VOID\r
8910 ModeHighlight()\r
8911 {\r
8912   static UINT prevChecked = 0;\r
8913   static int prevPausing = 0;\r
8914   UINT nowChecked;\r
8915 \r
8916   if (pausing != prevPausing) {\r
8917     prevPausing = pausing;\r
8918     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8919                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8920     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8921   }\r
8922 \r
8923   switch (gameMode) {\r
8924   case BeginningOfGame:\r
8925     if (appData.icsActive)\r
8926       nowChecked = IDM_IcsClient;\r
8927     else if (appData.noChessProgram)\r
8928       nowChecked = IDM_EditGame;\r
8929     else\r
8930       nowChecked = IDM_MachineBlack;\r
8931     break;\r
8932   case MachinePlaysBlack:\r
8933     nowChecked = IDM_MachineBlack;\r
8934     break;\r
8935   case MachinePlaysWhite:\r
8936     nowChecked = IDM_MachineWhite;\r
8937     break;\r
8938   case TwoMachinesPlay:\r
8939     nowChecked = IDM_TwoMachines;\r
8940     break;\r
8941   case AnalyzeMode:\r
8942     nowChecked = IDM_AnalysisMode;\r
8943     break;\r
8944   case AnalyzeFile:\r
8945     nowChecked = IDM_AnalyzeFile;\r
8946     break;\r
8947   case EditGame:\r
8948     nowChecked = IDM_EditGame;\r
8949     break;\r
8950   case PlayFromGameFile:\r
8951     nowChecked = IDM_LoadGame;\r
8952     break;\r
8953   case EditPosition:\r
8954     nowChecked = IDM_EditPosition;\r
8955     break;\r
8956   case Training:\r
8957     nowChecked = IDM_Training;\r
8958     break;\r
8959   case IcsPlayingWhite:\r
8960   case IcsPlayingBlack:\r
8961   case IcsObserving:\r
8962   case IcsIdle:\r
8963     nowChecked = IDM_IcsClient;\r
8964     break;\r
8965   default:\r
8966   case EndOfGame:\r
8967     nowChecked = 0;\r
8968     break;\r
8969   }\r
8970   if (prevChecked != 0)\r
8971     (void) CheckMenuItem(GetMenu(hwndMain),\r
8972                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8973   if (nowChecked != 0)\r
8974     (void) CheckMenuItem(GetMenu(hwndMain),\r
8975                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8976 \r
8977   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8978     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8979                           MF_BYCOMMAND|MF_ENABLED);\r
8980   } else {\r
8981     (void) EnableMenuItem(GetMenu(hwndMain), \r
8982                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8983   }\r
8984 \r
8985   prevChecked = nowChecked;\r
8986 \r
8987   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8988   if (appData.icsActive) {\r
8989        if (appData.icsEngineAnalyze) {\r
8990                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8991                        MF_BYCOMMAND|MF_CHECKED);\r
8992        } else {\r
8993                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8994                        MF_BYCOMMAND|MF_UNCHECKED);\r
8995        }\r
8996   }\r
8997 }\r
8998 \r
8999 VOID\r
9000 SetICSMode()\r
9001 {\r
9002   HMENU hmenu = GetMenu(hwndMain);\r
9003   SetMenuEnables(hmenu, icsEnables);\r
9004   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
9005     MF_BYPOSITION|MF_ENABLED);\r
9006 #ifdef ZIPPY\r
9007   if (appData.zippyPlay) {\r
9008     SetMenuEnables(hmenu, zippyEnables);\r
9009     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
9010          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9011           MF_BYCOMMAND|MF_ENABLED);\r
9012   }\r
9013 #endif\r
9014 }\r
9015 \r
9016 VOID\r
9017 SetGNUMode()\r
9018 {\r
9019   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
9020 }\r
9021 \r
9022 VOID\r
9023 SetNCPMode()\r
9024 {\r
9025   HMENU hmenu = GetMenu(hwndMain);\r
9026   SetMenuEnables(hmenu, ncpEnables);\r
9027   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
9028     MF_BYPOSITION|MF_GRAYED);\r
9029     DrawMenuBar(hwndMain);\r
9030 }\r
9031 \r
9032 VOID\r
9033 SetCmailMode()\r
9034 {\r
9035   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9036 }\r
9037 \r
9038 VOID \r
9039 SetTrainingModeOn()\r
9040 {\r
9041   int i;\r
9042   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9043   for (i = 0; i < N_BUTTONS; i++) {\r
9044     if (buttonDesc[i].hwnd != NULL)\r
9045       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9046   }\r
9047   CommentPopDown();\r
9048 }\r
9049 \r
9050 VOID SetTrainingModeOff()\r
9051 {\r
9052   int i;\r
9053   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9054   for (i = 0; i < N_BUTTONS; i++) {\r
9055     if (buttonDesc[i].hwnd != NULL)\r
9056       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9057   }\r
9058 }\r
9059 \r
9060 \r
9061 VOID\r
9062 SetUserThinkingEnables()\r
9063 {\r
9064   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9065 }\r
9066 \r
9067 VOID\r
9068 SetMachineThinkingEnables()\r
9069 {\r
9070   HMENU hMenu = GetMenu(hwndMain);\r
9071   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9072 \r
9073   SetMenuEnables(hMenu, machineThinkingEnables);\r
9074 \r
9075   if (gameMode == MachinePlaysBlack) {\r
9076     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9077   } else if (gameMode == MachinePlaysWhite) {\r
9078     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9079   } else if (gameMode == TwoMachinesPlay) {\r
9080     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9081   }\r
9082 }\r
9083 \r
9084 \r
9085 VOID\r
9086 DisplayTitle(char *str)\r
9087 {\r
9088   char title[MSG_SIZ], *host;\r
9089   if (str[0] != NULLCHAR) {\r
9090     strcpy(title, str);\r
9091   } else if (appData.icsActive) {\r
9092     if (appData.icsCommPort[0] != NULLCHAR)\r
9093       host = "ICS";\r
9094     else \r
9095       host = appData.icsHost;\r
9096     sprintf(title, "%s: %s", szTitle, host);\r
9097   } else if (appData.noChessProgram) {\r
9098     strcpy(title, szTitle);\r
9099   } else {\r
9100     strcpy(title, szTitle);\r
9101     strcat(title, ": ");\r
9102     strcat(title, first.tidy);\r
9103   }\r
9104   SetWindowText(hwndMain, title);\r
9105 }\r
9106 \r
9107 \r
9108 VOID\r
9109 DisplayMessage(char *str1, char *str2)\r
9110 {\r
9111   HDC hdc;\r
9112   HFONT oldFont;\r
9113   int remain = MESSAGE_TEXT_MAX - 1;\r
9114   int len;\r
9115 \r
9116   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9117   messageText[0] = NULLCHAR;\r
9118   if (*str1) {\r
9119     len = strlen(str1);\r
9120     if (len > remain) len = remain;\r
9121     strncpy(messageText, str1, len);\r
9122     messageText[len] = NULLCHAR;\r
9123     remain -= len;\r
9124   }\r
9125   if (*str2 && remain >= 2) {\r
9126     if (*str1) {\r
9127       strcat(messageText, "  ");\r
9128       remain -= 2;\r
9129     }\r
9130     len = strlen(str2);\r
9131     if (len > remain) len = remain;\r
9132     strncat(messageText, str2, len);\r
9133   }\r
9134   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9135 \r
9136   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9137 \r
9138   SAYMACHINEMOVE();\r
9139 \r
9140   hdc = GetDC(hwndMain);\r
9141   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9142   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9143              &messageRect, messageText, strlen(messageText), NULL);\r
9144   (void) SelectObject(hdc, oldFont);\r
9145   (void) ReleaseDC(hwndMain, hdc);\r
9146 }\r
9147 \r
9148 VOID\r
9149 DisplayError(char *str, int error)\r
9150 {\r
9151   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9152   int len;\r
9153 \r
9154   if (error == 0) {\r
9155     strcpy(buf, str);\r
9156   } else {\r
9157     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9158                         NULL, error, LANG_NEUTRAL,\r
9159                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9160     if (len > 0) {\r
9161       sprintf(buf, "%s:\n%s", str, buf2);\r
9162     } else {\r
9163       ErrorMap *em = errmap;\r
9164       while (em->err != 0 && em->err != error) em++;\r
9165       if (em->err != 0) {\r
9166         sprintf(buf, "%s:\n%s", str, em->msg);\r
9167       } else {\r
9168         sprintf(buf, "%s:\nError code %d", str, error);\r
9169       }\r
9170     }\r
9171   }\r
9172   \r
9173   ErrorPopUp("Error", buf);\r
9174 }\r
9175 \r
9176 \r
9177 VOID\r
9178 DisplayMoveError(char *str)\r
9179 {\r
9180   fromX = fromY = -1;\r
9181   ClearHighlights();\r
9182   DrawPosition(FALSE, NULL);\r
9183   if (appData.popupMoveErrors) {\r
9184     ErrorPopUp("Error", str);\r
9185   } else {\r
9186     DisplayMessage(str, "");\r
9187     moveErrorMessageUp = TRUE;\r
9188   }\r
9189 }\r
9190 \r
9191 VOID\r
9192 DisplayFatalError(char *str, int error, int exitStatus)\r
9193 {\r
9194   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9195   int len;\r
9196   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9197 \r
9198   if (error != 0) {\r
9199     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9200                         NULL, error, LANG_NEUTRAL,\r
9201                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9202     if (len > 0) {\r
9203       sprintf(buf, "%s:\n%s", str, buf2);\r
9204     } else {\r
9205       ErrorMap *em = errmap;\r
9206       while (em->err != 0 && em->err != error) em++;\r
9207       if (em->err != 0) {\r
9208         sprintf(buf, "%s:\n%s", str, em->msg);\r
9209       } else {\r
9210         sprintf(buf, "%s:\nError code %d", str, error);\r
9211       }\r
9212     }\r
9213     str = buf;\r
9214   }\r
9215   if (appData.debugMode) {\r
9216     fprintf(debugFP, "%s: %s\n", label, str);\r
9217   }\r
9218   if (appData.popupExitMessage) {\r
9219     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9220                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9221   }\r
9222   ExitEvent(exitStatus);\r
9223 }\r
9224 \r
9225 \r
9226 VOID\r
9227 DisplayInformation(char *str)\r
9228 {\r
9229   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9230 }\r
9231 \r
9232 \r
9233 VOID\r
9234 DisplayNote(char *str)\r
9235 {\r
9236   ErrorPopUp("Note", str);\r
9237 }\r
9238 \r
9239 \r
9240 typedef struct {\r
9241   char *title, *question, *replyPrefix;\r
9242   ProcRef pr;\r
9243 } QuestionParams;\r
9244 \r
9245 LRESULT CALLBACK\r
9246 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9247 {\r
9248   static QuestionParams *qp;\r
9249   char reply[MSG_SIZ];\r
9250   int len, err;\r
9251 \r
9252   switch (message) {\r
9253   case WM_INITDIALOG:\r
9254     qp = (QuestionParams *) lParam;\r
9255     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9256     SetWindowText(hDlg, qp->title);\r
9257     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9258     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9259     return FALSE;\r
9260 \r
9261   case WM_COMMAND:\r
9262     switch (LOWORD(wParam)) {\r
9263     case IDOK:\r
9264       strcpy(reply, qp->replyPrefix);\r
9265       if (*reply) strcat(reply, " ");\r
9266       len = strlen(reply);\r
9267       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9268       strcat(reply, "\n");\r
9269       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9270       EndDialog(hDlg, TRUE);\r
9271       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9272       return TRUE;\r
9273     case IDCANCEL:\r
9274       EndDialog(hDlg, FALSE);\r
9275       return TRUE;\r
9276     default:\r
9277       break;\r
9278     }\r
9279     break;\r
9280   }\r
9281   return FALSE;\r
9282 }\r
9283 \r
9284 VOID\r
9285 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9286 {\r
9287     QuestionParams qp;\r
9288     FARPROC lpProc;\r
9289     \r
9290     qp.title = title;\r
9291     qp.question = question;\r
9292     qp.replyPrefix = replyPrefix;\r
9293     qp.pr = pr;\r
9294     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9295     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9296       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9297     FreeProcInstance(lpProc);\r
9298 }\r
9299 \r
9300 /* [AS] Pick FRC position */\r
9301 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9302 {\r
9303     static int * lpIndexFRC;\r
9304     BOOL index_is_ok;\r
9305     char buf[16];\r
9306 \r
9307     switch( message )\r
9308     {\r
9309     case WM_INITDIALOG:\r
9310         lpIndexFRC = (int *) lParam;\r
9311 \r
9312         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9313 \r
9314         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9315         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9316         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9317         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9318 \r
9319         break;\r
9320 \r
9321     case WM_COMMAND:\r
9322         switch( LOWORD(wParam) ) {\r
9323         case IDOK:\r
9324             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9325             EndDialog( hDlg, 0 );\r
9326             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9327             return TRUE;\r
9328         case IDCANCEL:\r
9329             EndDialog( hDlg, 1 );   \r
9330             return TRUE;\r
9331         case IDC_NFG_Edit:\r
9332             if( HIWORD(wParam) == EN_CHANGE ) {\r
9333                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9334 \r
9335                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9336             }\r
9337             return TRUE;\r
9338         case IDC_NFG_Random:\r
9339             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9340             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9341             return TRUE;\r
9342         }\r
9343 \r
9344         break;\r
9345     }\r
9346 \r
9347     return FALSE;\r
9348 }\r
9349 \r
9350 int NewGameFRC()\r
9351 {\r
9352     int result;\r
9353     int index = appData.defaultFrcPosition;\r
9354     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9355 \r
9356     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9357 \r
9358     if( result == 0 ) {\r
9359         appData.defaultFrcPosition = index;\r
9360     }\r
9361 \r
9362     return result;\r
9363 }\r
9364 \r
9365 /* [AS] Game list options */\r
9366 typedef struct {\r
9367     char id;\r
9368     char * name;\r
9369 } GLT_Item;\r
9370 \r
9371 static GLT_Item GLT_ItemInfo[] = {\r
9372     { GLT_EVENT,      "Event" },\r
9373     { GLT_SITE,       "Site" },\r
9374     { GLT_DATE,       "Date" },\r
9375     { GLT_ROUND,      "Round" },\r
9376     { GLT_PLAYERS,    "Players" },\r
9377     { GLT_RESULT,     "Result" },\r
9378     { GLT_WHITE_ELO,  "White Rating" },\r
9379     { GLT_BLACK_ELO,  "Black Rating" },\r
9380     { GLT_TIME_CONTROL,"Time Control" },\r
9381     { GLT_VARIANT,    "Variant" },\r
9382     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9383     { 0, 0 }\r
9384 };\r
9385 \r
9386 const char * GLT_FindItem( char id )\r
9387 {\r
9388     const char * result = 0;\r
9389 \r
9390     GLT_Item * list = GLT_ItemInfo;\r
9391 \r
9392     while( list->id != 0 ) {\r
9393         if( list->id == id ) {\r
9394             result = list->name;\r
9395             break;\r
9396         }\r
9397 \r
9398         list++;\r
9399     }\r
9400 \r
9401     return result;\r
9402 }\r
9403 \r
9404 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9405 {\r
9406     const char * name = GLT_FindItem( id );\r
9407 \r
9408     if( name != 0 ) {\r
9409         if( index >= 0 ) {\r
9410             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9411         }\r
9412         else {\r
9413             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9414         }\r
9415     }\r
9416 }\r
9417 \r
9418 void GLT_TagsToList( HWND hDlg, char * tags )\r
9419 {\r
9420     char * pc = tags;\r
9421 \r
9422     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9423 \r
9424     while( *pc ) {\r
9425         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9426         pc++;\r
9427     }\r
9428 \r
9429     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9430 \r
9431     pc = GLT_ALL_TAGS;\r
9432 \r
9433     while( *pc ) {\r
9434         if( strchr( tags, *pc ) == 0 ) {\r
9435             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9436         }\r
9437         pc++;\r
9438     }\r
9439 \r
9440     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9441 }\r
9442 \r
9443 char GLT_ListItemToTag( HWND hDlg, int index )\r
9444 {\r
9445     char result = '\0';\r
9446     char name[128];\r
9447 \r
9448     GLT_Item * list = GLT_ItemInfo;\r
9449 \r
9450     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9451         while( list->id != 0 ) {\r
9452             if( strcmp( list->name, name ) == 0 ) {\r
9453                 result = list->id;\r
9454                 break;\r
9455             }\r
9456 \r
9457             list++;\r
9458         }\r
9459     }\r
9460 \r
9461     return result;\r
9462 }\r
9463 \r
9464 void GLT_MoveSelection( HWND hDlg, int delta )\r
9465 {\r
9466     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9467     int idx2 = idx1 + delta;\r
9468     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9469 \r
9470     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9471         char buf[128];\r
9472 \r
9473         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9474         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9475         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9476         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9477     }\r
9478 }\r
9479 \r
9480 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9481 {\r
9482     static char glt[64];\r
9483     static char * lpUserGLT;\r
9484 \r
9485     switch( message )\r
9486     {\r
9487     case WM_INITDIALOG:\r
9488         lpUserGLT = (char *) lParam;\r
9489         \r
9490         strcpy( glt, lpUserGLT );\r
9491 \r
9492         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9493 \r
9494         /* Initialize list */\r
9495         GLT_TagsToList( hDlg, glt );\r
9496 \r
9497         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9498 \r
9499         break;\r
9500 \r
9501     case WM_COMMAND:\r
9502         switch( LOWORD(wParam) ) {\r
9503         case IDOK:\r
9504             {\r
9505                 char * pc = lpUserGLT;\r
9506                 int idx = 0;\r
9507 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9508                 char id;\r
9509 \r
9510                 do {\r
9511                     id = GLT_ListItemToTag( hDlg, idx );\r
9512 \r
9513                     *pc++ = id;\r
9514                     idx++;\r
9515                 } while( id != '\0' );\r
9516             }\r
9517             EndDialog( hDlg, 0 );\r
9518             return TRUE;\r
9519         case IDCANCEL:\r
9520             EndDialog( hDlg, 1 );\r
9521             return TRUE;\r
9522 \r
9523         case IDC_GLT_Default:\r
9524             strcpy( glt, GLT_DEFAULT_TAGS );\r
9525             GLT_TagsToList( hDlg, glt );\r
9526             return TRUE;\r
9527 \r
9528         case IDC_GLT_Restore:\r
9529             strcpy( glt, lpUserGLT );\r
9530             GLT_TagsToList( hDlg, glt );\r
9531             return TRUE;\r
9532 \r
9533         case IDC_GLT_Up:\r
9534             GLT_MoveSelection( hDlg, -1 );\r
9535             return TRUE;\r
9536 \r
9537         case IDC_GLT_Down:\r
9538             GLT_MoveSelection( hDlg, +1 );\r
9539             return TRUE;\r
9540         }\r
9541 \r
9542         break;\r
9543     }\r
9544 \r
9545     return FALSE;\r
9546 }\r
9547 \r
9548 int GameListOptions()\r
9549 {\r
9550     char glt[64];\r
9551     int result;\r
9552     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9553 \r
9554     strcpy( glt, appData.gameListTags );\r
9555 \r
9556     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9557 \r
9558     if( result == 0 ) {\r
9559         /* [AS] Memory leak here! */\r
9560         appData.gameListTags = strdup( glt ); \r
9561     }\r
9562 \r
9563     return result;\r
9564 }\r
9565 \r
9566 \r
9567 VOID\r
9568 DisplayIcsInteractionTitle(char *str)\r
9569 {\r
9570   char consoleTitle[MSG_SIZ];\r
9571 \r
9572   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9573   SetWindowText(hwndConsole, consoleTitle);\r
9574 }\r
9575 \r
9576 void\r
9577 DrawPosition(int fullRedraw, Board board)\r
9578 {\r
9579   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9580 }\r
9581 \r
9582 \r
9583 VOID\r
9584 ResetFrontEnd()\r
9585 {\r
9586   fromX = fromY = -1;\r
9587   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9588     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9589     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9590     dragInfo.lastpos = dragInfo.pos;\r
9591     dragInfo.start.x = dragInfo.start.y = -1;\r
9592     dragInfo.from = dragInfo.start;\r
9593     ReleaseCapture();\r
9594     DrawPosition(TRUE, NULL);\r
9595   }\r
9596 }\r
9597 \r
9598 \r
9599 VOID\r
9600 CommentPopUp(char *title, char *str)\r
9601 {\r
9602   HWND hwnd = GetActiveWindow();\r
9603   EitherCommentPopUp(0, title, str, FALSE);\r
9604   SetActiveWindow(hwnd);\r
9605 }\r
9606 \r
9607 VOID\r
9608 CommentPopDown(void)\r
9609 {\r
9610   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9611   if (commentDialog) {\r
9612     ShowWindow(commentDialog, SW_HIDE);\r
9613   }\r
9614   commentDialogUp = FALSE;\r
9615 }\r
9616 \r
9617 VOID\r
9618 EditCommentPopUp(int index, char *title, char *str)\r
9619 {\r
9620   EitherCommentPopUp(index, title, str, TRUE);\r
9621 }\r
9622 \r
9623 \r
9624 VOID\r
9625 RingBell()\r
9626 {\r
9627   MyPlaySound(&sounds[(int)SoundMove]);\r
9628 }\r
9629 \r
9630 VOID PlayIcsWinSound()\r
9631 {\r
9632   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9633 }\r
9634 \r
9635 VOID PlayIcsLossSound()\r
9636 {\r
9637   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9638 }\r
9639 \r
9640 VOID PlayIcsDrawSound()\r
9641 {\r
9642   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9643 }\r
9644 \r
9645 VOID PlayIcsUnfinishedSound()\r
9646 {\r
9647   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9648 }\r
9649 \r
9650 VOID\r
9651 PlayAlarmSound()\r
9652 {\r
9653   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9654 }\r
9655 \r
9656 \r
9657 VOID\r
9658 EchoOn()\r
9659 {\r
9660   HWND hInput;\r
9661   consoleEcho = TRUE;\r
9662   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9663   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9664   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9665 }\r
9666 \r
9667 \r
9668 VOID\r
9669 EchoOff()\r
9670 {\r
9671   CHARFORMAT cf;\r
9672   HWND hInput;\r
9673   consoleEcho = FALSE;\r
9674   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9675   /* This works OK: set text and background both to the same color */\r
9676   cf = consoleCF;\r
9677   cf.crTextColor = COLOR_ECHOOFF;\r
9678   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9679   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9680 }\r
9681 \r
9682 /* No Raw()...? */\r
9683 \r
9684 void Colorize(ColorClass cc, int continuation)\r
9685 {\r
9686   currentColorClass = cc;\r
9687   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9688   consoleCF.crTextColor = textAttribs[cc].color;\r
9689   consoleCF.dwEffects = textAttribs[cc].effects;\r
9690   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9691 }\r
9692 \r
9693 char *\r
9694 UserName()\r
9695 {\r
9696   static char buf[MSG_SIZ];\r
9697   DWORD bufsiz = MSG_SIZ;\r
9698 \r
9699   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9700         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9701   }\r
9702   if (!GetUserName(buf, &bufsiz)) {\r
9703     /*DisplayError("Error getting user name", GetLastError());*/\r
9704     strcpy(buf, "User");\r
9705   }\r
9706   return buf;\r
9707 }\r
9708 \r
9709 char *\r
9710 HostName()\r
9711 {\r
9712   static char buf[MSG_SIZ];\r
9713   DWORD bufsiz = MSG_SIZ;\r
9714 \r
9715   if (!GetComputerName(buf, &bufsiz)) {\r
9716     /*DisplayError("Error getting host name", GetLastError());*/\r
9717     strcpy(buf, "Unknown");\r
9718   }\r
9719   return buf;\r
9720 }\r
9721 \r
9722 \r
9723 int\r
9724 ClockTimerRunning()\r
9725 {\r
9726   return clockTimerEvent != 0;\r
9727 }\r
9728 \r
9729 int\r
9730 StopClockTimer()\r
9731 {\r
9732   if (clockTimerEvent == 0) return FALSE;\r
9733   KillTimer(hwndMain, clockTimerEvent);\r
9734   clockTimerEvent = 0;\r
9735   return TRUE;\r
9736 }\r
9737 \r
9738 void\r
9739 StartClockTimer(long millisec)\r
9740 {\r
9741   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9742                              (UINT) millisec, NULL);\r
9743 }\r
9744 \r
9745 void\r
9746 DisplayWhiteClock(long timeRemaining, int highlight)\r
9747 {\r
9748   HDC hdc;\r
9749   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9750 \r
9751   if(appData.noGUI) return;\r
9752   hdc = GetDC(hwndMain);\r
9753   if (!IsIconic(hwndMain)) {\r
9754     DisplayAClock(hdc, timeRemaining, highlight, \r
9755                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9756   }\r
9757   if (highlight && iconCurrent == iconBlack) {\r
9758     iconCurrent = iconWhite;\r
9759     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9760     if (IsIconic(hwndMain)) {\r
9761       DrawIcon(hdc, 2, 2, iconCurrent);\r
9762     }\r
9763   }\r
9764   (void) ReleaseDC(hwndMain, hdc);\r
9765   if (hwndConsole)\r
9766     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9767 }\r
9768 \r
9769 void\r
9770 DisplayBlackClock(long timeRemaining, int highlight)\r
9771 {\r
9772   HDC hdc;\r
9773   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9774 \r
9775   if(appData.noGUI) return;\r
9776   hdc = GetDC(hwndMain);\r
9777   if (!IsIconic(hwndMain)) {\r
9778     DisplayAClock(hdc, timeRemaining, highlight, \r
9779                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9780   }\r
9781   if (highlight && iconCurrent == iconWhite) {\r
9782     iconCurrent = iconBlack;\r
9783     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9784     if (IsIconic(hwndMain)) {\r
9785       DrawIcon(hdc, 2, 2, iconCurrent);\r
9786     }\r
9787   }\r
9788   (void) ReleaseDC(hwndMain, hdc);\r
9789   if (hwndConsole)\r
9790     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9791 }\r
9792 \r
9793 \r
9794 int\r
9795 LoadGameTimerRunning()\r
9796 {\r
9797   return loadGameTimerEvent != 0;\r
9798 }\r
9799 \r
9800 int\r
9801 StopLoadGameTimer()\r
9802 {\r
9803   if (loadGameTimerEvent == 0) return FALSE;\r
9804   KillTimer(hwndMain, loadGameTimerEvent);\r
9805   loadGameTimerEvent = 0;\r
9806   return TRUE;\r
9807 }\r
9808 \r
9809 void\r
9810 StartLoadGameTimer(long millisec)\r
9811 {\r
9812   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9813                                 (UINT) millisec, NULL);\r
9814 }\r
9815 \r
9816 void\r
9817 AutoSaveGame()\r
9818 {\r
9819   char *defName;\r
9820   FILE *f;\r
9821   char fileTitle[MSG_SIZ];\r
9822 \r
9823   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9824   f = OpenFileDialog(hwndMain, "a", defName,\r
9825                      appData.oldSaveStyle ? "gam" : "pgn",\r
9826                      GAME_FILT, \r
9827                      "Save Game to File", NULL, fileTitle, NULL);\r
9828   if (f != NULL) {\r
9829     SaveGame(f, 0, "");\r
9830     fclose(f);\r
9831   }\r
9832 }\r
9833 \r
9834 \r
9835 void\r
9836 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9837 {\r
9838   if (delayedTimerEvent != 0) {\r
9839     if (appData.debugMode) {\r
9840       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9841     }\r
9842     KillTimer(hwndMain, delayedTimerEvent);\r
9843     delayedTimerEvent = 0;\r
9844     delayedTimerCallback();\r
9845   }\r
9846   delayedTimerCallback = cb;\r
9847   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9848                                 (UINT) millisec, NULL);\r
9849 }\r
9850 \r
9851 DelayedEventCallback\r
9852 GetDelayedEvent()\r
9853 {\r
9854   if (delayedTimerEvent) {\r
9855     return delayedTimerCallback;\r
9856   } else {\r
9857     return NULL;\r
9858   }\r
9859 }\r
9860 \r
9861 void\r
9862 CancelDelayedEvent()\r
9863 {\r
9864   if (delayedTimerEvent) {\r
9865     KillTimer(hwndMain, delayedTimerEvent);\r
9866     delayedTimerEvent = 0;\r
9867   }\r
9868 }\r
9869 \r
9870 DWORD GetWin32Priority(int nice)\r
9871 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9872 /*\r
9873 REALTIME_PRIORITY_CLASS     0x00000100\r
9874 HIGH_PRIORITY_CLASS         0x00000080\r
9875 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9876 NORMAL_PRIORITY_CLASS       0x00000020\r
9877 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9878 IDLE_PRIORITY_CLASS         0x00000040\r
9879 */\r
9880         if (nice < -15) return 0x00000080;\r
9881         if (nice < 0)   return 0x00008000;\r
9882         if (nice == 0)  return 0x00000020;\r
9883         if (nice < 15)  return 0x00004000;\r
9884         return 0x00000040;\r
9885 }\r
9886 \r
9887 /* Start a child process running the given program.\r
9888    The process's standard output can be read from "from", and its\r
9889    standard input can be written to "to".\r
9890    Exit with fatal error if anything goes wrong.\r
9891    Returns an opaque pointer that can be used to destroy the process\r
9892    later.\r
9893 */\r
9894 int\r
9895 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9896 {\r
9897 #define BUFSIZE 4096\r
9898 \r
9899   HANDLE hChildStdinRd, hChildStdinWr,\r
9900     hChildStdoutRd, hChildStdoutWr;\r
9901   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9902   SECURITY_ATTRIBUTES saAttr;\r
9903   BOOL fSuccess;\r
9904   PROCESS_INFORMATION piProcInfo;\r
9905   STARTUPINFO siStartInfo;\r
9906   ChildProc *cp;\r
9907   char buf[MSG_SIZ];\r
9908   DWORD err;\r
9909 \r
9910   if (appData.debugMode) {\r
9911     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9912   }\r
9913 \r
9914   *pr = NoProc;\r
9915 \r
9916   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9917   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9918   saAttr.bInheritHandle = TRUE;\r
9919   saAttr.lpSecurityDescriptor = NULL;\r
9920 \r
9921   /*\r
9922    * The steps for redirecting child's STDOUT:\r
9923    *     1. Create anonymous pipe to be STDOUT for child.\r
9924    *     2. Create a noninheritable duplicate of read handle,\r
9925    *         and close the inheritable read handle.\r
9926    */\r
9927 \r
9928   /* Create a pipe for the child's STDOUT. */\r
9929   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9930     return GetLastError();\r
9931   }\r
9932 \r
9933   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9934   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9935                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9936                              FALSE,     /* not inherited */\r
9937                              DUPLICATE_SAME_ACCESS);\r
9938   if (! fSuccess) {\r
9939     return GetLastError();\r
9940   }\r
9941   CloseHandle(hChildStdoutRd);\r
9942 \r
9943   /*\r
9944    * The steps for redirecting child's STDIN:\r
9945    *     1. Create anonymous pipe to be STDIN for child.\r
9946    *     2. Create a noninheritable duplicate of write handle,\r
9947    *         and close the inheritable write handle.\r
9948    */\r
9949 \r
9950   /* Create a pipe for the child's STDIN. */\r
9951   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9952     return GetLastError();\r
9953   }\r
9954 \r
9955   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9956   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9957                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9958                              FALSE,     /* not inherited */\r
9959                              DUPLICATE_SAME_ACCESS);\r
9960   if (! fSuccess) {\r
9961     return GetLastError();\r
9962   }\r
9963   CloseHandle(hChildStdinWr);\r
9964 \r
9965   /* Arrange to (1) look in dir for the child .exe file, and\r
9966    * (2) have dir be the child's working directory.  Interpret\r
9967    * dir relative to the directory WinBoard loaded from. */\r
9968   GetCurrentDirectory(MSG_SIZ, buf);\r
9969   SetCurrentDirectory(installDir);\r
9970   SetCurrentDirectory(dir);\r
9971 \r
9972   /* Now create the child process. */\r
9973 \r
9974   siStartInfo.cb = sizeof(STARTUPINFO);\r
9975   siStartInfo.lpReserved = NULL;\r
9976   siStartInfo.lpDesktop = NULL;\r
9977   siStartInfo.lpTitle = NULL;\r
9978   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9979   siStartInfo.cbReserved2 = 0;\r
9980   siStartInfo.lpReserved2 = NULL;\r
9981   siStartInfo.hStdInput = hChildStdinRd;\r
9982   siStartInfo.hStdOutput = hChildStdoutWr;\r
9983   siStartInfo.hStdError = hChildStdoutWr;\r
9984 \r
9985   fSuccess = CreateProcess(NULL,\r
9986                            cmdLine,        /* command line */\r
9987                            NULL,           /* process security attributes */\r
9988                            NULL,           /* primary thread security attrs */\r
9989                            TRUE,           /* handles are inherited */\r
9990                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9991                            NULL,           /* use parent's environment */\r
9992                            NULL,\r
9993                            &siStartInfo, /* STARTUPINFO pointer */\r
9994                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9995 \r
9996   err = GetLastError();\r
9997   SetCurrentDirectory(buf); /* return to prev directory */\r
9998   if (! fSuccess) {\r
9999     return err;\r
10000   }\r
10001 \r
10002   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
10003     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
10004     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
10005   }\r
10006 \r
10007   /* Close the handles we don't need in the parent */\r
10008   CloseHandle(piProcInfo.hThread);\r
10009   CloseHandle(hChildStdinRd);\r
10010   CloseHandle(hChildStdoutWr);\r
10011 \r
10012   /* Prepare return value */\r
10013   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10014   cp->kind = CPReal;\r
10015   cp->hProcess = piProcInfo.hProcess;\r
10016   cp->pid = piProcInfo.dwProcessId;\r
10017   cp->hFrom = hChildStdoutRdDup;\r
10018   cp->hTo = hChildStdinWrDup;\r
10019 \r
10020   *pr = (void *) cp;\r
10021 \r
10022   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10023      2000 where engines sometimes don't see the initial command(s)\r
10024      from WinBoard and hang.  I don't understand how that can happen,\r
10025      but the Sleep is harmless, so I've put it in.  Others have also\r
10026      reported what may be the same problem, so hopefully this will fix\r
10027      it for them too.  */\r
10028   Sleep(500);\r
10029 \r
10030   return NO_ERROR;\r
10031 }\r
10032 \r
10033 \r
10034 void\r
10035 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10036 {\r
10037   ChildProc *cp; int result;\r
10038 \r
10039   cp = (ChildProc *) pr;\r
10040   if (cp == NULL) return;\r
10041 \r
10042   switch (cp->kind) {\r
10043   case CPReal:\r
10044     /* TerminateProcess is considered harmful, so... */\r
10045     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10046     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10047     /* The following doesn't work because the chess program\r
10048        doesn't "have the same console" as WinBoard.  Maybe\r
10049        we could arrange for this even though neither WinBoard\r
10050        nor the chess program uses a console for stdio? */\r
10051     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10052 \r
10053     /* [AS] Special termination modes for misbehaving programs... */\r
10054     if( signal == 9 ) { \r
10055         result = TerminateProcess( cp->hProcess, 0 );\r
10056 \r
10057         if ( appData.debugMode) {\r
10058             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10059         }\r
10060     }\r
10061     else if( signal == 10 ) {\r
10062         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10063 \r
10064         if( dw != WAIT_OBJECT_0 ) {\r
10065             result = TerminateProcess( cp->hProcess, 0 );\r
10066 \r
10067             if ( appData.debugMode) {\r
10068                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10069             }\r
10070 \r
10071         }\r
10072     }\r
10073 \r
10074     CloseHandle(cp->hProcess);\r
10075     break;\r
10076 \r
10077   case CPComm:\r
10078     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10079     break;\r
10080 \r
10081   case CPSock:\r
10082     closesocket(cp->sock);\r
10083     WSACleanup();\r
10084     break;\r
10085 \r
10086   case CPRcmd:\r
10087     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10088     closesocket(cp->sock);\r
10089     closesocket(cp->sock2);\r
10090     WSACleanup();\r
10091     break;\r
10092   }\r
10093   free(cp);\r
10094 }\r
10095 \r
10096 void\r
10097 InterruptChildProcess(ProcRef pr)\r
10098 {\r
10099   ChildProc *cp;\r
10100 \r
10101   cp = (ChildProc *) pr;\r
10102   if (cp == NULL) return;\r
10103   switch (cp->kind) {\r
10104   case CPReal:\r
10105     /* The following doesn't work because the chess program\r
10106        doesn't "have the same console" as WinBoard.  Maybe\r
10107        we could arrange for this even though neither WinBoard\r
10108        nor the chess program uses a console for stdio */\r
10109     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10110     break;\r
10111 \r
10112   case CPComm:\r
10113   case CPSock:\r
10114     /* Can't interrupt */\r
10115     break;\r
10116 \r
10117   case CPRcmd:\r
10118     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10119     break;\r
10120   }\r
10121 }\r
10122 \r
10123 \r
10124 int\r
10125 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10126 {\r
10127   char cmdLine[MSG_SIZ];\r
10128 \r
10129   if (port[0] == NULLCHAR) {\r
10130     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10131   } else {\r
10132     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10133   }\r
10134   return StartChildProcess(cmdLine, "", pr);\r
10135 }\r
10136 \r
10137 \r
10138 /* Code to open TCP sockets */\r
10139 \r
10140 int\r
10141 OpenTCP(char *host, char *port, ProcRef *pr)\r
10142 {\r
10143   ChildProc *cp;\r
10144   int err;\r
10145   SOCKET s;\r
10146   struct sockaddr_in sa, mysa;\r
10147   struct hostent FAR *hp;\r
10148   unsigned short uport;\r
10149   WORD wVersionRequested;\r
10150   WSADATA wsaData;\r
10151 \r
10152   /* Initialize socket DLL */\r
10153   wVersionRequested = MAKEWORD(1, 1);\r
10154   err = WSAStartup(wVersionRequested, &wsaData);\r
10155   if (err != 0) return err;\r
10156 \r
10157   /* Make socket */\r
10158   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10159     err = WSAGetLastError();\r
10160     WSACleanup();\r
10161     return err;\r
10162   }\r
10163 \r
10164   /* Bind local address using (mostly) don't-care values.\r
10165    */\r
10166   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10167   mysa.sin_family = AF_INET;\r
10168   mysa.sin_addr.s_addr = INADDR_ANY;\r
10169   uport = (unsigned short) 0;\r
10170   mysa.sin_port = htons(uport);\r
10171   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10172       == SOCKET_ERROR) {\r
10173     err = WSAGetLastError();\r
10174     WSACleanup();\r
10175     return err;\r
10176   }\r
10177 \r
10178   /* Resolve remote host name */\r
10179   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10180   if (!(hp = gethostbyname(host))) {\r
10181     unsigned int b0, b1, b2, b3;\r
10182 \r
10183     err = WSAGetLastError();\r
10184 \r
10185     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10186       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10187       hp->h_addrtype = AF_INET;\r
10188       hp->h_length = 4;\r
10189       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10190       hp->h_addr_list[0] = (char *) malloc(4);\r
10191       hp->h_addr_list[0][0] = (char) b0;\r
10192       hp->h_addr_list[0][1] = (char) b1;\r
10193       hp->h_addr_list[0][2] = (char) b2;\r
10194       hp->h_addr_list[0][3] = (char) b3;\r
10195     } else {\r
10196       WSACleanup();\r
10197       return err;\r
10198     }\r
10199   }\r
10200   sa.sin_family = hp->h_addrtype;\r
10201   uport = (unsigned short) atoi(port);\r
10202   sa.sin_port = htons(uport);\r
10203   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10204 \r
10205   /* Make connection */\r
10206   if (connect(s, (struct sockaddr *) &sa,\r
10207               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10208     err = WSAGetLastError();\r
10209     WSACleanup();\r
10210     return err;\r
10211   }\r
10212 \r
10213   /* Prepare return value */\r
10214   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10215   cp->kind = CPSock;\r
10216   cp->sock = s;\r
10217   *pr = (ProcRef *) cp;\r
10218 \r
10219   return NO_ERROR;\r
10220 }\r
10221 \r
10222 int\r
10223 OpenCommPort(char *name, ProcRef *pr)\r
10224 {\r
10225   HANDLE h;\r
10226   COMMTIMEOUTS ct;\r
10227   ChildProc *cp;\r
10228   char fullname[MSG_SIZ];\r
10229 \r
10230   if (*name != '\\')\r
10231     sprintf(fullname, "\\\\.\\%s", name);\r
10232   else\r
10233     strcpy(fullname, name);\r
10234 \r
10235   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10236                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10237   if (h == (HANDLE) -1) {\r
10238     return GetLastError();\r
10239   }\r
10240   hCommPort = h;\r
10241 \r
10242   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10243 \r
10244   /* Accumulate characters until a 100ms pause, then parse */\r
10245   ct.ReadIntervalTimeout = 100;\r
10246   ct.ReadTotalTimeoutMultiplier = 0;\r
10247   ct.ReadTotalTimeoutConstant = 0;\r
10248   ct.WriteTotalTimeoutMultiplier = 0;\r
10249   ct.WriteTotalTimeoutConstant = 0;\r
10250   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10251 \r
10252   /* Prepare return value */\r
10253   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10254   cp->kind = CPComm;\r
10255   cp->hFrom = h;\r
10256   cp->hTo = h;\r
10257   *pr = (ProcRef *) cp;\r
10258 \r
10259   return NO_ERROR;\r
10260 }\r
10261 \r
10262 int\r
10263 OpenLoopback(ProcRef *pr)\r
10264 {\r
10265   DisplayFatalError("Not implemented", 0, 1);\r
10266   return NO_ERROR;\r
10267 }\r
10268 \r
10269 \r
10270 int\r
10271 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10272 {\r
10273   ChildProc *cp;\r
10274   int err;\r
10275   SOCKET s, s2, s3;\r
10276   struct sockaddr_in sa, mysa;\r
10277   struct hostent FAR *hp;\r
10278   unsigned short uport;\r
10279   WORD wVersionRequested;\r
10280   WSADATA wsaData;\r
10281   int fromPort;\r
10282   char stderrPortStr[MSG_SIZ];\r
10283 \r
10284   /* Initialize socket DLL */\r
10285   wVersionRequested = MAKEWORD(1, 1);\r
10286   err = WSAStartup(wVersionRequested, &wsaData);\r
10287   if (err != 0) return err;\r
10288 \r
10289   /* Resolve remote host name */\r
10290   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10291   if (!(hp = gethostbyname(host))) {\r
10292     unsigned int b0, b1, b2, b3;\r
10293 \r
10294     err = WSAGetLastError();\r
10295 \r
10296     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10297       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10298       hp->h_addrtype = AF_INET;\r
10299       hp->h_length = 4;\r
10300       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10301       hp->h_addr_list[0] = (char *) malloc(4);\r
10302       hp->h_addr_list[0][0] = (char) b0;\r
10303       hp->h_addr_list[0][1] = (char) b1;\r
10304       hp->h_addr_list[0][2] = (char) b2;\r
10305       hp->h_addr_list[0][3] = (char) b3;\r
10306     } else {\r
10307       WSACleanup();\r
10308       return err;\r
10309     }\r
10310   }\r
10311   sa.sin_family = hp->h_addrtype;\r
10312   uport = (unsigned short) 514;\r
10313   sa.sin_port = htons(uport);\r
10314   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10315 \r
10316   /* Bind local socket to unused "privileged" port address\r
10317    */\r
10318   s = INVALID_SOCKET;\r
10319   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10320   mysa.sin_family = AF_INET;\r
10321   mysa.sin_addr.s_addr = INADDR_ANY;\r
10322   for (fromPort = 1023;; fromPort--) {\r
10323     if (fromPort < 0) {\r
10324       WSACleanup();\r
10325       return WSAEADDRINUSE;\r
10326     }\r
10327     if (s == INVALID_SOCKET) {\r
10328       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10329         err = WSAGetLastError();\r
10330         WSACleanup();\r
10331         return err;\r
10332       }\r
10333     }\r
10334     uport = (unsigned short) fromPort;\r
10335     mysa.sin_port = htons(uport);\r
10336     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10337         == SOCKET_ERROR) {\r
10338       err = WSAGetLastError();\r
10339       if (err == WSAEADDRINUSE) continue;\r
10340       WSACleanup();\r
10341       return err;\r
10342     }\r
10343     if (connect(s, (struct sockaddr *) &sa,\r
10344       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10345       err = WSAGetLastError();\r
10346       if (err == WSAEADDRINUSE) {\r
10347         closesocket(s);\r
10348         s = -1;\r
10349         continue;\r
10350       }\r
10351       WSACleanup();\r
10352       return err;\r
10353     }\r
10354     break;\r
10355   }\r
10356 \r
10357   /* Bind stderr local socket to unused "privileged" port address\r
10358    */\r
10359   s2 = INVALID_SOCKET;\r
10360   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10361   mysa.sin_family = AF_INET;\r
10362   mysa.sin_addr.s_addr = INADDR_ANY;\r
10363   for (fromPort = 1023;; fromPort--) {\r
10364     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10365     if (fromPort < 0) {\r
10366       (void) closesocket(s);\r
10367       WSACleanup();\r
10368       return WSAEADDRINUSE;\r
10369     }\r
10370     if (s2 == INVALID_SOCKET) {\r
10371       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10372         err = WSAGetLastError();\r
10373         closesocket(s);\r
10374         WSACleanup();\r
10375         return err;\r
10376       }\r
10377     }\r
10378     uport = (unsigned short) fromPort;\r
10379     mysa.sin_port = htons(uport);\r
10380     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10381         == SOCKET_ERROR) {\r
10382       err = WSAGetLastError();\r
10383       if (err == WSAEADDRINUSE) continue;\r
10384       (void) closesocket(s);\r
10385       WSACleanup();\r
10386       return err;\r
10387     }\r
10388     if (listen(s2, 1) == SOCKET_ERROR) {\r
10389       err = WSAGetLastError();\r
10390       if (err == WSAEADDRINUSE) {\r
10391         closesocket(s2);\r
10392         s2 = INVALID_SOCKET;\r
10393         continue;\r
10394       }\r
10395       (void) closesocket(s);\r
10396       (void) closesocket(s2);\r
10397       WSACleanup();\r
10398       return err;\r
10399     }\r
10400     break;\r
10401   }\r
10402   prevStderrPort = fromPort; // remember port used\r
10403   sprintf(stderrPortStr, "%d", fromPort);\r
10404 \r
10405   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10406     err = WSAGetLastError();\r
10407     (void) closesocket(s);\r
10408     (void) closesocket(s2);\r
10409     WSACleanup();\r
10410     return err;\r
10411   }\r
10412 \r
10413   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10414     err = WSAGetLastError();\r
10415     (void) closesocket(s);\r
10416     (void) closesocket(s2);\r
10417     WSACleanup();\r
10418     return err;\r
10419   }\r
10420   if (*user == NULLCHAR) user = UserName();\r
10421   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10422     err = WSAGetLastError();\r
10423     (void) closesocket(s);\r
10424     (void) closesocket(s2);\r
10425     WSACleanup();\r
10426     return err;\r
10427   }\r
10428   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10429     err = WSAGetLastError();\r
10430     (void) closesocket(s);\r
10431     (void) closesocket(s2);\r
10432     WSACleanup();\r
10433     return err;\r
10434   }\r
10435 \r
10436   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10437     err = WSAGetLastError();\r
10438     (void) closesocket(s);\r
10439     (void) closesocket(s2);\r
10440     WSACleanup();\r
10441     return err;\r
10442   }\r
10443   (void) closesocket(s2);  /* Stop listening */\r
10444 \r
10445   /* Prepare return value */\r
10446   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10447   cp->kind = CPRcmd;\r
10448   cp->sock = s;\r
10449   cp->sock2 = s3;\r
10450   *pr = (ProcRef *) cp;\r
10451 \r
10452   return NO_ERROR;\r
10453 }\r
10454 \r
10455 \r
10456 InputSourceRef\r
10457 AddInputSource(ProcRef pr, int lineByLine,\r
10458                InputCallback func, VOIDSTAR closure)\r
10459 {\r
10460   InputSource *is, *is2 = NULL;\r
10461   ChildProc *cp = (ChildProc *) pr;\r
10462 \r
10463   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10464   is->lineByLine = lineByLine;\r
10465   is->func = func;\r
10466   is->closure = closure;\r
10467   is->second = NULL;\r
10468   is->next = is->buf;\r
10469   if (pr == NoProc) {\r
10470     is->kind = CPReal;\r
10471     consoleInputSource = is;\r
10472   } else {\r
10473     is->kind = cp->kind;\r
10474     /* \r
10475         [AS] Try to avoid a race condition if the thread is given control too early:\r
10476         we create all threads suspended so that the is->hThread variable can be\r
10477         safely assigned, then let the threads start with ResumeThread.\r
10478     */\r
10479     switch (cp->kind) {\r
10480     case CPReal:\r
10481       is->hFile = cp->hFrom;\r
10482       cp->hFrom = NULL; /* now owned by InputThread */\r
10483       is->hThread =\r
10484         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10485                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10486       break;\r
10487 \r
10488     case CPComm:\r
10489       is->hFile = cp->hFrom;\r
10490       cp->hFrom = NULL; /* now owned by InputThread */\r
10491       is->hThread =\r
10492         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10493                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10494       break;\r
10495 \r
10496     case CPSock:\r
10497       is->sock = cp->sock;\r
10498       is->hThread =\r
10499         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10500                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10501       break;\r
10502 \r
10503     case CPRcmd:\r
10504       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10505       *is2 = *is;\r
10506       is->sock = cp->sock;\r
10507       is->second = is2;\r
10508       is2->sock = cp->sock2;\r
10509       is2->second = is2;\r
10510       is->hThread =\r
10511         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10512                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10513       is2->hThread =\r
10514         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10515                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10516       break;\r
10517     }\r
10518 \r
10519     if( is->hThread != NULL ) {\r
10520         ResumeThread( is->hThread );\r
10521     }\r
10522 \r
10523     if( is2 != NULL && is2->hThread != NULL ) {\r
10524         ResumeThread( is2->hThread );\r
10525     }\r
10526   }\r
10527 \r
10528   return (InputSourceRef) is;\r
10529 }\r
10530 \r
10531 void\r
10532 RemoveInputSource(InputSourceRef isr)\r
10533 {\r
10534   InputSource *is;\r
10535 \r
10536   is = (InputSource *) isr;\r
10537   is->hThread = NULL;  /* tell thread to stop */\r
10538   CloseHandle(is->hThread);\r
10539   if (is->second != NULL) {\r
10540     is->second->hThread = NULL;\r
10541     CloseHandle(is->second->hThread);\r
10542   }\r
10543 }\r
10544 \r
10545 \r
10546 int\r
10547 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10548 {\r
10549   DWORD dOutCount;\r
10550   int outCount = SOCKET_ERROR;\r
10551   ChildProc *cp = (ChildProc *) pr;\r
10552   static OVERLAPPED ovl;\r
10553 \r
10554   if (pr == NoProc) {\r
10555     ConsoleOutput(message, count, FALSE);\r
10556     return count;\r
10557   } \r
10558 \r
10559   if (ovl.hEvent == NULL) {\r
10560     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10561   }\r
10562   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10563 \r
10564   switch (cp->kind) {\r
10565   case CPSock:\r
10566   case CPRcmd:\r
10567     outCount = send(cp->sock, message, count, 0);\r
10568     if (outCount == SOCKET_ERROR) {\r
10569       *outError = WSAGetLastError();\r
10570     } else {\r
10571       *outError = NO_ERROR;\r
10572     }\r
10573     break;\r
10574 \r
10575   case CPReal:\r
10576     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10577                   &dOutCount, NULL)) {\r
10578       *outError = NO_ERROR;\r
10579       outCount = (int) dOutCount;\r
10580     } else {\r
10581       *outError = GetLastError();\r
10582     }\r
10583     break;\r
10584 \r
10585   case CPComm:\r
10586     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10587                             &dOutCount, &ovl);\r
10588     if (*outError == NO_ERROR) {\r
10589       outCount = (int) dOutCount;\r
10590     }\r
10591     break;\r
10592   }\r
10593   return outCount;\r
10594 }\r
10595 \r
10596 int\r
10597 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10598                        long msdelay)\r
10599 {\r
10600   /* Ignore delay, not implemented for WinBoard */\r
10601   return OutputToProcess(pr, message, count, outError);\r
10602 }\r
10603 \r
10604 \r
10605 void\r
10606 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10607                         char *buf, int count, int error)\r
10608 {\r
10609   DisplayFatalError("Not implemented", 0, 1);\r
10610 }\r
10611 \r
10612 /* see wgamelist.c for Game List functions */\r
10613 /* see wedittags.c for Edit Tags functions */\r
10614 \r
10615 \r
10616 VOID\r
10617 ICSInitScript()\r
10618 {\r
10619   FILE *f;\r
10620   char buf[MSG_SIZ];\r
10621   char *dummy;\r
10622 \r
10623   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10624     f = fopen(buf, "r");\r
10625     if (f != NULL) {\r
10626       ProcessICSInitScript(f);\r
10627       fclose(f);\r
10628     }\r
10629   }\r
10630 }\r
10631 \r
10632 \r
10633 VOID\r
10634 StartAnalysisClock()\r
10635 {\r
10636   if (analysisTimerEvent) return;\r
10637   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10638                                         (UINT) 2000, NULL);\r
10639 }\r
10640 \r
10641 LRESULT CALLBACK\r
10642 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10643 {\r
10644   static HANDLE hwndText;\r
10645   RECT rect;\r
10646   static int sizeX, sizeY;\r
10647   int newSizeX, newSizeY, flags;\r
10648   MINMAXINFO *mmi;\r
10649 \r
10650   switch (message) {\r
10651   case WM_INITDIALOG: /* message: initialize dialog box */\r
10652     /* Initialize the dialog items */\r
10653     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10654     SetWindowText(hDlg, analysisTitle);\r
10655     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10656     /* Size and position the dialog */\r
10657     if (!analysisDialog) {\r
10658       analysisDialog = hDlg;\r
10659       flags = SWP_NOZORDER;\r
10660       GetClientRect(hDlg, &rect);\r
10661       sizeX = rect.right;\r
10662       sizeY = rect.bottom;\r
10663       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10664           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10665         WINDOWPLACEMENT wp;\r
10666         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10667         wp.length = sizeof(WINDOWPLACEMENT);\r
10668         wp.flags = 0;\r
10669         wp.showCmd = SW_SHOW;\r
10670         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10671         wp.rcNormalPosition.left = analysisX;\r
10672         wp.rcNormalPosition.right = analysisX + analysisW;\r
10673         wp.rcNormalPosition.top = analysisY;\r
10674         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10675         SetWindowPlacement(hDlg, &wp);\r
10676 \r
10677         GetClientRect(hDlg, &rect);\r
10678         newSizeX = rect.right;\r
10679         newSizeY = rect.bottom;\r
10680         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10681                               newSizeX, newSizeY);\r
10682         sizeX = newSizeX;\r
10683         sizeY = newSizeY;\r
10684       }\r
10685     }\r
10686     return FALSE;\r
10687 \r
10688   case WM_COMMAND: /* message: received a command */\r
10689     switch (LOWORD(wParam)) {\r
10690     case IDCANCEL:\r
10691       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10692           ExitAnalyzeMode();\r
10693           ModeHighlight();\r
10694           return TRUE;\r
10695       }\r
10696       EditGameEvent();\r
10697       return TRUE;\r
10698     default:\r
10699       break;\r
10700     }\r
10701     break;\r
10702 \r
10703   case WM_SIZE:\r
10704     newSizeX = LOWORD(lParam);\r
10705     newSizeY = HIWORD(lParam);\r
10706     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10707     sizeX = newSizeX;\r
10708     sizeY = newSizeY;\r
10709     break;\r
10710 \r
10711   case WM_GETMINMAXINFO:\r
10712     /* Prevent resizing window too small */\r
10713     mmi = (MINMAXINFO *) lParam;\r
10714     mmi->ptMinTrackSize.x = 100;\r
10715     mmi->ptMinTrackSize.y = 100;\r
10716     break;\r
10717   }\r
10718   return FALSE;\r
10719 }\r
10720 \r
10721 VOID\r
10722 AnalysisPopUp(char* title, char* str)\r
10723 {\r
10724   FARPROC lpProc;\r
10725   char *p, *q;\r
10726 \r
10727   /* [AS] */\r
10728   EngineOutputPopUp();\r
10729   return;\r
10730 \r
10731   if (str == NULL) str = "";\r
10732   p = (char *) malloc(2 * strlen(str) + 2);\r
10733   q = p;\r
10734   while (*str) {\r
10735     if (*str == '\n') *q++ = '\r';\r
10736     *q++ = *str++;\r
10737   }\r
10738   *q = NULLCHAR;\r
10739   if (analysisText != NULL) free(analysisText);\r
10740   analysisText = p;\r
10741 \r
10742   if (analysisDialog) {\r
10743     SetWindowText(analysisDialog, title);\r
10744     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10745     ShowWindow(analysisDialog, SW_SHOW);\r
10746   } else {\r
10747     analysisTitle = title;\r
10748     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10749     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10750                  hwndMain, (DLGPROC)lpProc);\r
10751     FreeProcInstance(lpProc);\r
10752   }\r
10753   analysisDialogUp = TRUE;  \r
10754 }\r
10755 \r
10756 VOID\r
10757 AnalysisPopDown()\r
10758 {\r
10759   if (analysisDialog) {\r
10760     ShowWindow(analysisDialog, SW_HIDE);\r
10761   }\r
10762   analysisDialogUp = FALSE;  \r
10763 }\r
10764 \r
10765 \r
10766 VOID\r
10767 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10768 {\r
10769   highlightInfo.sq[0].x = fromX;\r
10770   highlightInfo.sq[0].y = fromY;\r
10771   highlightInfo.sq[1].x = toX;\r
10772   highlightInfo.sq[1].y = toY;\r
10773 }\r
10774 \r
10775 VOID\r
10776 ClearHighlights()\r
10777 {\r
10778   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10779     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10780 }\r
10781 \r
10782 VOID\r
10783 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10784 {\r
10785   premoveHighlightInfo.sq[0].x = fromX;\r
10786   premoveHighlightInfo.sq[0].y = fromY;\r
10787   premoveHighlightInfo.sq[1].x = toX;\r
10788   premoveHighlightInfo.sq[1].y = toY;\r
10789 }\r
10790 \r
10791 VOID\r
10792 ClearPremoveHighlights()\r
10793 {\r
10794   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10795     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10796 }\r
10797 \r
10798 VOID\r
10799 ShutDownFrontEnd()\r
10800 {\r
10801   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10802   DeleteClipboardTempFiles();\r
10803 }\r
10804 \r
10805 void\r
10806 BoardToTop()\r
10807 {\r
10808     if (IsIconic(hwndMain))\r
10809       ShowWindow(hwndMain, SW_RESTORE);\r
10810 \r
10811     SetActiveWindow(hwndMain);\r
10812 }\r
10813 \r
10814 /*\r
10815  * Prototypes for animation support routines\r
10816  */\r
10817 static void ScreenSquare(int column, int row, POINT * pt);\r
10818 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10819      POINT frames[], int * nFrames);\r
10820 \r
10821 \r
10822 void\r
10823 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10824 {       // [HGM] atomic: animate blast wave\r
10825         int i;\r
10826 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10827         explodeInfo.fromX = fromX;\r
10828         explodeInfo.fromY = fromY;\r
10829         explodeInfo.toX = toX;\r
10830         explodeInfo.toY = toY;\r
10831         for(i=1; i<nFrames; i++) {\r
10832             explodeInfo.radius = (i*180)/(nFrames-1);\r
10833             DrawPosition(FALSE, NULL);\r
10834             Sleep(appData.animSpeed);\r
10835         }\r
10836         explodeInfo.radius = 0;\r
10837         DrawPosition(TRUE, NULL);\r
10838 }\r
10839 \r
10840 #define kFactor 4\r
10841 \r
10842 void\r
10843 AnimateMove(board, fromX, fromY, toX, toY)\r
10844      Board board;\r
10845      int fromX;\r
10846      int fromY;\r
10847      int toX;\r
10848      int toY;\r
10849 {\r
10850   ChessSquare piece;\r
10851   POINT start, finish, mid;\r
10852   POINT frames[kFactor * 2 + 1];\r
10853   int nFrames, n;\r
10854 \r
10855   if (!appData.animate) return;\r
10856   if (doingSizing) return;\r
10857   if (fromY < 0 || fromX < 0) return;\r
10858   piece = board[fromY][fromX];\r
10859   if (piece >= EmptySquare) return;\r
10860 \r
10861   ScreenSquare(fromX, fromY, &start);\r
10862   ScreenSquare(toX, toY, &finish);\r
10863 \r
10864   /* All pieces except knights move in straight line */\r
10865   if (piece != WhiteKnight && piece != BlackKnight) {\r
10866     mid.x = start.x + (finish.x - start.x) / 2;\r
10867     mid.y = start.y + (finish.y - start.y) / 2;\r
10868   } else {\r
10869     /* Knight: make diagonal movement then straight */\r
10870     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10871        mid.x = start.x + (finish.x - start.x) / 2;\r
10872        mid.y = finish.y;\r
10873      } else {\r
10874        mid.x = finish.x;\r
10875        mid.y = start.y + (finish.y - start.y) / 2;\r
10876      }\r
10877   }\r
10878   \r
10879   /* Don't use as many frames for very short moves */\r
10880   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10881     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10882   else\r
10883     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10884 \r
10885   animInfo.from.x = fromX;\r
10886   animInfo.from.y = fromY;\r
10887   animInfo.to.x = toX;\r
10888   animInfo.to.y = toY;\r
10889   animInfo.lastpos = start;\r
10890   animInfo.piece = piece;\r
10891   for (n = 0; n < nFrames; n++) {\r
10892     animInfo.pos = frames[n];\r
10893     DrawPosition(FALSE, NULL);\r
10894     animInfo.lastpos = animInfo.pos;\r
10895     Sleep(appData.animSpeed);\r
10896   }\r
10897   animInfo.pos = finish;\r
10898   DrawPosition(FALSE, NULL);\r
10899   animInfo.piece = EmptySquare;\r
10900   if(gameInfo.variant == VariantAtomic && \r
10901      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10902         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10903 }\r
10904 \r
10905 /*      Convert board position to corner of screen rect and color       */\r
10906 \r
10907 static void\r
10908 ScreenSquare(column, row, pt)\r
10909      int column; int row; POINT * pt;\r
10910 {\r
10911   if (flipView) {\r
10912     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10913     pt->y = lineGap + row * (squareSize + lineGap);\r
10914   } else {\r
10915     pt->x = lineGap + column * (squareSize + lineGap);\r
10916     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10917   }\r
10918 }\r
10919 \r
10920 /*      Generate a series of frame coords from start->mid->finish.\r
10921         The movement rate doubles until the half way point is\r
10922         reached, then halves back down to the final destination,\r
10923         which gives a nice slow in/out effect. The algorithmn\r
10924         may seem to generate too many intermediates for short\r
10925         moves, but remember that the purpose is to attract the\r
10926         viewers attention to the piece about to be moved and\r
10927         then to where it ends up. Too few frames would be less\r
10928         noticeable.                                             */\r
10929 \r
10930 static void\r
10931 Tween(start, mid, finish, factor, frames, nFrames)\r
10932      POINT * start; POINT * mid;\r
10933      POINT * finish; int factor;\r
10934      POINT frames[]; int * nFrames;\r
10935 {\r
10936   int n, fraction = 1, count = 0;\r
10937 \r
10938   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10939   for (n = 0; n < factor; n++)\r
10940     fraction *= 2;\r
10941   for (n = 0; n < factor; n++) {\r
10942     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10943     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10944     count ++;\r
10945     fraction = fraction / 2;\r
10946   }\r
10947   \r
10948   /* Midpoint */\r
10949   frames[count] = *mid;\r
10950   count ++;\r
10951   \r
10952   /* Slow out, stepping 1/2, then 1/4, ... */\r
10953   fraction = 2;\r
10954   for (n = 0; n < factor; n++) {\r
10955     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10956     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10957     count ++;\r
10958     fraction = fraction * 2;\r
10959   }\r
10960   *nFrames = count;\r
10961 }\r
10962 \r
10963 void\r
10964 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10965 {\r
10966 #if 0\r
10967     char buf[256];\r
10968 \r
10969     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10970         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10971 \r
10972     OutputDebugString( buf );\r
10973 #endif\r
10974 \r
10975     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10976 \r
10977     EvalGraphSet( first, last, current, pvInfoList );\r
10978 }\r
10979 \r
10980 void SetProgramStats( FrontEndProgramStats * stats )\r
10981 {\r
10982 #if 0\r
10983     char buf[1024];\r
10984 \r
10985     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10986         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10987 \r
10988     OutputDebugString( buf );\r
10989 #endif\r
10990 \r
10991     EngineOutputUpdate( stats );\r
10992 }\r