more callbacks
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "winboard.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "wgamelist.h"\r
89 #include "wedittags.h"\r
90 #include "woptions.h"\r
91 #include "wsockerr.h"\r
92 #include "defaults.h"\r
93 #include "help.h"\r
94 #include "wsnap.h"\r
95 \r
96 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
97 \r
98   int myrandom(void);\r
99   void mysrandom(unsigned int seed);\r
100 \r
101 extern int whiteFlag, blackFlag;\r
102 Boolean flipClock = FALSE;\r
103 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 typedef struct {\r
112   ChessSquare piece;  \r
113   POINT pos;      /* window coordinates of current pos */\r
114   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
115   POINT from;     /* board coordinates of the piece's orig pos */\r
116   POINT to;       /* board coordinates of the piece's new pos */\r
117 } AnimInfo;\r
118 \r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
120 \r
121 typedef struct {\r
122   POINT start;    /* window coordinates of start pos */\r
123   POINT pos;      /* window coordinates of current pos */\r
124   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
125   POINT from;     /* board coordinates of the piece's orig pos */\r
126 } DragInfo;\r
127 \r
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
129 \r
130 typedef struct {\r
131   POINT sq[2];    /* board coordinates of from, to squares */\r
132 } HighlightInfo;\r
133 \r
134 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
136 \r
137 typedef struct { // [HGM] atomic\r
138   int fromX, fromY, toX, toY, radius;\r
139 } ExplodeInfo;\r
140 \r
141 static ExplodeInfo explodeInfo;\r
142 \r
143 /* Window class names */\r
144 char szAppName[] = "WinBoard";\r
145 char szConsoleName[] = "WBConsole";\r
146 \r
147 /* Title bar text */\r
148 char szTitle[] = "WinBoard";\r
149 char szConsoleTitle[] = "I C S Interaction";\r
150 \r
151 char *programName;\r
152 char *settingsFileName;\r
153 BOOLEAN saveSettingsOnExit;\r
154 char installDir[MSG_SIZ];\r
155 \r
156 BoardSize boardSize;\r
157 BOOLEAN chessProgram;\r
158 static int boardX, boardY;\r
159 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
160 static int squareSize, lineGap, minorSize;\r
161 static int winWidth, winHeight, winW, winH;\r
162 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
163 static int logoHeight = 0;\r
164 static char messageText[MESSAGE_TEXT_MAX];\r
165 static int clockTimerEvent = 0;\r
166 static int loadGameTimerEvent = 0;\r
167 static int analysisTimerEvent = 0;\r
168 static DelayedEventCallback delayedTimerCallback;\r
169 static int delayedTimerEvent = 0;\r
170 static int buttonCount = 2;\r
171 char *icsTextMenuString;\r
172 char *icsNames;\r
173 char *firstChessProgramNames;\r
174 char *secondChessProgramNames;\r
175 \r
176 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
177 \r
178 #define PALETTESIZE 256\r
179 \r
180 HINSTANCE hInst;          /* current instance */\r
181 HWND hwndMain = NULL;        /* root window*/\r
182 HWND hwndConsole = NULL;\r
183 BOOLEAN alwaysOnTop = FALSE;\r
184 RECT boardRect;\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
186   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
187 HPALETTE hPal;\r
188 ColorClass currentColorClass;\r
189 \r
190 HWND hCommPort = NULL;    /* currently open comm port */\r
191 static HWND hwndPause;    /* pause button */\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,\r
194   blackSquareBrush, /* [HGM] for band between board and holdings */\r
195   explodeBrush,     /* [HGM] atomic */\r
196   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
197 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
198 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
199 static HPEN gridPen = NULL;\r
200 static HPEN highlightPen = NULL;\r
201 static HPEN premovePen = NULL;\r
202 static NPLOGPALETTE pLogPal;\r
203 static BOOL paletteChanged = FALSE;\r
204 static HICON iconWhite, iconBlack, iconCurrent;\r
205 static int doingSizing = FALSE;\r
206 static int lastSizing = 0;\r
207 static int prevStderrPort;\r
208 static HBITMAP userLogo;\r
209 \r
210 /* [AS] Support for background textures */\r
211 #define BACK_TEXTURE_MODE_DISABLED      0\r
212 #define BACK_TEXTURE_MODE_PLAIN         1\r
213 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 #define oldDialog (_winmajor < 4)\r
226 #endif\r
227 \r
228 char *defaultTextAttribs[] = \r
229 {\r
230   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
231   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
232   COLOR_NONE\r
233 };\r
234 \r
235 typedef struct {\r
236   char *name;\r
237   int squareSize;\r
238   int lineGap;\r
239   int smallLayout;\r
240   int tinyLayout;\r
241   int cliWidth, cliHeight;\r
242 } SizeInfo;\r
243 \r
244 SizeInfo sizeInfo[] = \r
245 {\r
246   { "tiny",     21, 0, 1, 1, 0, 0 },\r
247   { "teeny",    25, 1, 1, 1, 0, 0 },\r
248   { "dinky",    29, 1, 1, 1, 0, 0 },\r
249   { "petite",   33, 1, 1, 1, 0, 0 },\r
250   { "slim",     37, 2, 1, 0, 0, 0 },\r
251   { "small",    40, 2, 1, 0, 0, 0 },\r
252   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
253   { "middling", 49, 2, 0, 0, 0, 0 },\r
254   { "average",  54, 2, 0, 0, 0, 0 },\r
255   { "moderate", 58, 3, 0, 0, 0, 0 },\r
256   { "medium",   64, 3, 0, 0, 0, 0 },\r
257   { "bulky",    72, 3, 0, 0, 0, 0 },\r
258   { "large",    80, 3, 0, 0, 0, 0 },\r
259   { "big",      87, 3, 0, 0, 0, 0 },\r
260   { "huge",     95, 3, 0, 0, 0, 0 },\r
261   { "giant",    108, 3, 0, 0, 0, 0 },\r
262   { "colossal", 116, 4, 0, 0, 0, 0 },\r
263   { "titanic",  129, 4, 0, 0, 0, 0 },\r
264   { NULL, 0, 0, 0, 0, 0, 0 }\r
265 };\r
266 \r
267 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
268 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
269 {\r
270   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
271   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
272   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
273   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
274   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
275   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
276   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
277   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
278   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
279   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
280   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
281   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
282   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
283   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
284   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
285   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
286   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
287   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
288 };\r
289 \r
290 MyFont *font[NUM_SIZES][NUM_FONTS];\r
291 \r
292 typedef struct {\r
293   char *label;\r
294   int id;\r
295   HWND hwnd;\r
296   WNDPROC wndproc;\r
297 } MyButtonDesc;\r
298 \r
299 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
300 #define N_BUTTONS 5\r
301 \r
302 MyButtonDesc buttonDesc[N_BUTTONS] =\r
303 {\r
304   {"<<", IDM_ToStart, NULL, NULL},\r
305   {"<", IDM_Backward, NULL, NULL},\r
306   {"P", IDM_Pause, NULL, NULL},\r
307   {">", IDM_Forward, NULL, NULL},\r
308   {">>", IDM_ToEnd, NULL, NULL},\r
309 };\r
310 \r
311 int tinyLayout = 0, smallLayout = 0;\r
312 #define MENU_BAR_ITEMS 7\r
313 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
314   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
315   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
316 };\r
317 \r
318 \r
319 MySound sounds[(int)NSoundClasses];\r
320 MyTextAttribs textAttribs[(int)NColorClasses];\r
321 \r
322 MyColorizeAttribs colorizeAttribs[] = {\r
323   { (COLORREF)0, 0, "Shout Text" },\r
324   { (COLORREF)0, 0, "SShout/CShout" },\r
325   { (COLORREF)0, 0, "Channel 1 Text" },\r
326   { (COLORREF)0, 0, "Channel Text" },\r
327   { (COLORREF)0, 0, "Kibitz Text" },\r
328   { (COLORREF)0, 0, "Tell Text" },\r
329   { (COLORREF)0, 0, "Challenge Text" },\r
330   { (COLORREF)0, 0, "Request Text" },\r
331   { (COLORREF)0, 0, "Seek Text" },\r
332   { (COLORREF)0, 0, "Normal Text" },\r
333   { (COLORREF)0, 0, "None" }\r
334 };\r
335 \r
336 \r
337 \r
338 static char *commentTitle;\r
339 static char *commentText;\r
340 static int commentIndex;\r
341 static Boolean editComment = FALSE;\r
342 HWND commentDialog = NULL;\r
343 BOOLEAN commentDialogUp = FALSE;\r
344 static int commentX, commentY, commentH, commentW;\r
345 \r
346 static char *analysisTitle;\r
347 static char *analysisText;\r
348 HWND analysisDialog = NULL;\r
349 BOOLEAN analysisDialogUp = FALSE;\r
350 static int analysisX, analysisY, analysisH, analysisW;\r
351 \r
352 char errorTitle[MSG_SIZ];\r
353 char errorMessage[2*MSG_SIZ];\r
354 HWND errorDialog = NULL;\r
355 BOOLEAN moveErrorMessageUp = FALSE;\r
356 BOOLEAN consoleEcho = TRUE;\r
357 CHARFORMAT consoleCF;\r
358 COLORREF consoleBackgroundColor;\r
359 \r
360 char *programVersion;\r
361 \r
362 #define CPReal 1\r
363 #define CPComm 2\r
364 #define CPSock 3\r
365 #define CPRcmd 4\r
366 typedef int CPKind;\r
367 \r
368 typedef struct {\r
369   CPKind kind;\r
370   HANDLE hProcess;\r
371   DWORD pid;\r
372   HANDLE hTo;\r
373   HANDLE hFrom;\r
374   SOCKET sock;\r
375   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
376 } ChildProc;\r
377 \r
378 #define INPUT_SOURCE_BUF_SIZE 4096\r
379 \r
380 typedef struct _InputSource {\r
381   CPKind kind;\r
382   HANDLE hFile;\r
383   SOCKET sock;\r
384   int lineByLine;\r
385   HANDLE hThread;\r
386   DWORD id;\r
387   char buf[INPUT_SOURCE_BUF_SIZE];\r
388   char *next;\r
389   DWORD count;\r
390   int error;\r
391   InputCallback func;\r
392   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
393   VOIDSTAR closure;\r
394 } InputSource;\r
395 \r
396 InputSource *consoleInputSource;\r
397 \r
398 DCB dcb;\r
399 \r
400 /* forward */\r
401 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
402 VOID ConsoleCreate();\r
403 LRESULT CALLBACK\r
404   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
405 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
406 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
407 VOID ParseCommSettings(char *arg, DCB *dcb);\r
408 LRESULT CALLBACK\r
409   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
410 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
411 void ParseIcsTextMenu(char *icsTextMenuString);\r
412 VOID PopUpMoveDialog(char firstchar);\r
413 VOID PopUpNameDialog(char firstchar);\r
414 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
415 \r
416 /* [AS] */\r
417 int NewGameFRC();\r
418 int GameListOptions();\r
419 \r
420 HWND moveHistoryDialog = NULL;\r
421 BOOLEAN moveHistoryDialogUp = FALSE;\r
422 \r
423 WindowPlacement wpMoveHistory;\r
424 \r
425 HWND evalGraphDialog = NULL;\r
426 BOOLEAN evalGraphDialogUp = FALSE;\r
427 \r
428 WindowPlacement wpEvalGraph;\r
429 \r
430 HWND engineOutputDialog = NULL;\r
431 BOOLEAN engineOutputDialogUp = FALSE;\r
432 \r
433 WindowPlacement wpEngineOutput;\r
434 WindowPlacement wpGameList;\r
435 WindowPlacement wpConsole;\r
436 \r
437 VOID MoveHistoryPopUp();\r
438 VOID MoveHistoryPopDown();\r
439 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
440 BOOL MoveHistoryIsUp();\r
441 \r
442 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
443 VOID EvalGraphPopUp();\r
444 VOID EvalGraphPopDown();\r
445 BOOL EvalGraphIsUp();\r
446 \r
447 VOID EngineOutputPopUp();\r
448 VOID EngineOutputPopDown();\r
449 BOOL EngineOutputIsUp();\r
450 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
451 \r
452 VOID GothicPopUp(char *title, VariantClass variant);\r
453 /*\r
454  * Setting "frozen" should disable all user input other than deleting\r
455  * the window.  We do this while engines are initializing themselves.\r
456  */\r
457 static int frozen = 0;\r
458 static int oldMenuItemState[MENU_BAR_ITEMS];\r
459 void FreezeUI()\r
460 {\r
461   HMENU hmenu;\r
462   int i;\r
463 \r
464   if (frozen) return;\r
465   frozen = 1;\r
466   hmenu = GetMenu(hwndMain);\r
467   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
468     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
469   }\r
470   DrawMenuBar(hwndMain);\r
471 }\r
472 \r
473 /* Undo a FreezeUI */\r
474 void ThawUI()\r
475 {\r
476   HMENU hmenu;\r
477   int i;\r
478 \r
479   if (!frozen) return;\r
480   frozen = 0;\r
481   hmenu = GetMenu(hwndMain);\r
482   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
483     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
484   }\r
485   DrawMenuBar(hwndMain);\r
486 }\r
487 \r
488 static int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
489 \r
490 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
491 #ifdef JAWS\r
492 #include "jaws.c"\r
493 #else\r
494 #define JAWS_INIT\r
495 #define JAWS_ALT_INTERCEPT\r
496 #define JAWS_KB_NAVIGATION\r
497 #define JAWS_MENU_ITEMS\r
498 #define JAWS_SILENCE\r
499 #define JAWS_REPLAY\r
500 #define JAWS_ACCEL\r
501 #define JAWS_COPYRIGHT\r
502 #define JAWS_DELETE(X) X\r
503 #define SAYMACHINEMOVE()\r
504 #define SAY(X)\r
505 #endif\r
506 \r
507 /*---------------------------------------------------------------------------*\\r
508  *\r
509  * WinMain\r
510  *\r
511 \*---------------------------------------------------------------------------*/\r
512 \r
513 int APIENTRY\r
514 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
515         LPSTR lpCmdLine, int nCmdShow)\r
516 {\r
517   MSG msg;\r
518   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
519 //  INITCOMMONCONTROLSEX ex;\r
520 \r
521   debugFP = stderr;\r
522 \r
523   LoadLibrary("RICHED32.DLL");\r
524   consoleCF.cbSize = sizeof(CHARFORMAT);\r
525 \r
526   if (!InitApplication(hInstance)) {\r
527     return (FALSE);\r
528   }\r
529   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
530     return (FALSE);\r
531   }\r
532 \r
533   JAWS_INIT\r
534 \r
535 //  InitCommonControlsEx(&ex);\r
536   InitCommonControls();\r
537 \r
538   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
539   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
540   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
541 \r
542   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
543 \r
544   while (GetMessage(&msg, /* message structure */\r
545                     NULL, /* handle of window receiving the message */\r
546                     0,    /* lowest message to examine */\r
547                     0))   /* highest message to examine */\r
548     {\r
549 \r
550       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
551         // [HGM] navigate: switch between all windows with tab\r
552         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
553         int i, currentElement = 0;\r
554 \r
555         // first determine what element of the chain we come from (if any)\r
556         if(appData.icsActive) {\r
557             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
558             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
559         }\r
560         if(engineOutputDialog && EngineOutputIsUp()) {\r
561             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
562             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
563         }\r
564         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
565             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
566         }\r
567         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
568         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
569         if(msg.hwnd == e1)                 currentElement = 2; else\r
570         if(msg.hwnd == e2)                 currentElement = 3; else\r
571         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
572         if(msg.hwnd == mh)                currentElement = 4; else\r
573         if(msg.hwnd == evalGraphDialog)    currentElement = 7; else\r
574         if(msg.hwnd == hText)  currentElement = 5; else\r
575         if(msg.hwnd == hInput) currentElement = 6; else\r
576         for (i = 0; i < N_BUTTONS; i++) {\r
577             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
578         }\r
579 \r
580         // determine where to go to\r
581         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
582           do {\r
583             currentElement = (currentElement + direction) % 7;\r
584             switch(currentElement) {\r
585                 case 0:\r
586                   h = hwndMain; break; // passing this case always makes the loop exit\r
587                 case 1:\r
588                   h = buttonDesc[0].hwnd; break; // could be NULL\r
589                 case 2:\r
590                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
591                   h = e1; break;\r
592                 case 3:\r
593                   if(!EngineOutputIsUp()) continue;\r
594                   h = e2; break;\r
595                 case 4:\r
596                   if(!MoveHistoryIsUp()) continue;\r
597                   h = mh; break;\r
598 //              case 5: // input to eval graph does not seem to get here!\r
599 //                if(!EvalGraphIsUp()) continue;\r
600 //                h = evalGraphDialog; break;\r
601                 case 5:\r
602                   if(!appData.icsActive) continue;\r
603                   SAY("display");\r
604                   h = hText; break;\r
605                 case 6:\r
606                   if(!appData.icsActive) continue;\r
607                   SAY("input");\r
608                   h = hInput; break;\r
609             }\r
610           } while(h == 0);\r
611 \r
612           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
613           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
614           SetFocus(h);\r
615 \r
616           continue; // this message now has been processed\r
617         }\r
618       }\r
619 \r
620       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
621           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
622           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
623           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
624           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
625           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
626           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
627           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
628           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
629           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
630         TranslateMessage(&msg); /* Translates virtual key codes */\r
631         DispatchMessage(&msg);  /* Dispatches message to window */\r
632       }\r
633     }\r
634 \r
635 \r
636   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
637 }\r
638 \r
639 /*---------------------------------------------------------------------------*\\r
640  *\r
641  * Initialization functions\r
642  *\r
643 \*---------------------------------------------------------------------------*/\r
644 \r
645 void\r
646 SetUserLogo()\r
647 {   // update user logo if necessary\r
648     static char oldUserName[MSG_SIZ], *curName;\r
649 \r
650     if(appData.autoLogo) {\r
651           curName = UserName();\r
652           if(strcmp(curName, oldUserName)) {\r
653                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
654                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
655                 strcpy(oldUserName, curName);\r
656           }\r
657     }\r
658 }\r
659 \r
660 BOOL\r
661 InitApplication(HINSTANCE hInstance)\r
662 {\r
663   WNDCLASS wc;\r
664 \r
665   /* Fill in window class structure with parameters that describe the */\r
666   /* main window. */\r
667 \r
668   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
669   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
670   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
671   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
672   wc.hInstance     = hInstance;         /* Owner of this class */\r
673   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
674   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
675   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
676   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
677   wc.lpszClassName = szAppName;                 /* Name to register as */\r
678 \r
679   /* Register the window class and return success/failure code. */\r
680   if (!RegisterClass(&wc)) return FALSE;\r
681 \r
682   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
683   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
684   wc.cbClsExtra    = 0;\r
685   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
686   wc.hInstance     = hInstance;\r
687   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
688   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
689   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
690   wc.lpszMenuName  = NULL;\r
691   wc.lpszClassName = szConsoleName;\r
692 \r
693   if (!RegisterClass(&wc)) return FALSE;\r
694   return TRUE;\r
695 }\r
696 \r
697 \r
698 /* Set by InitInstance, used by EnsureOnScreen */\r
699 int screenHeight, screenWidth;\r
700 \r
701 void\r
702 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
703 {\r
704 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
705   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
706   if (*x > screenWidth - 32) *x = 0;\r
707   if (*y > screenHeight - 32) *y = 0;\r
708   if (*x < minX) *x = minX;\r
709   if (*y < minY) *y = minY;\r
710 }\r
711 \r
712 BOOL\r
713 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
714 {\r
715   HWND hwnd; /* Main window handle. */\r
716   int ibs;\r
717   WINDOWPLACEMENT wp;\r
718   char *filepart;\r
719 \r
720   hInst = hInstance;    /* Store instance handle in our global variable */\r
721 \r
722   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
723     *filepart = NULLCHAR;\r
724   } else {\r
725     GetCurrentDirectory(MSG_SIZ, installDir);\r
726   }\r
727   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
728   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
729   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
730   if (appData.debugMode) {\r
731     debugFP = fopen(appData.nameOfDebugFile, "w");\r
732     setbuf(debugFP, NULL);\r
733   }\r
734 \r
735   InitBackEnd1();\r
736 \r
737 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
738 //  InitEngineUCI( installDir, &second );\r
739 \r
740   /* Create a main window for this application instance. */\r
741   hwnd = CreateWindow(szAppName, szTitle,\r
742                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
743                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
744                       NULL, NULL, hInstance, NULL);\r
745   hwndMain = hwnd;\r
746 \r
747   /* If window could not be created, return "failure" */\r
748   if (!hwnd) {\r
749     return (FALSE);\r
750   }\r
751 \r
752   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
753   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
754       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
755 \r
756       if (first.programLogo == NULL && appData.debugMode) {\r
757           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
758       }\r
759   } else if(appData.autoLogo) {\r
760       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
761         char buf[MSG_SIZ];\r
762         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
763         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
764       }\r
765   }\r
766 \r
767   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
768       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
769 \r
770       if (second.programLogo == NULL && appData.debugMode) {\r
771           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
772       }\r
773   } else if(appData.autoLogo) {\r
774       char buf[MSG_SIZ];\r
775       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
776         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
777         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
778       } else\r
779       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
780         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
781         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
782       }\r
783   }\r
784 \r
785   SetUserLogo();\r
786 \r
787   iconWhite = LoadIcon(hInstance, "icon_white");\r
788   iconBlack = LoadIcon(hInstance, "icon_black");\r
789   iconCurrent = iconWhite;\r
790   InitDrawingColors();\r
791   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
792   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
793   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
794     /* Compute window size for each board size, and use the largest\r
795        size that fits on this screen as the default. */\r
796     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
797     if (boardSize == (BoardSize)-1 &&\r
798         winH <= screenHeight\r
799            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
800         && winW <= screenWidth) {\r
801       boardSize = (BoardSize)ibs;\r
802     }\r
803   }\r
804 \r
805   InitDrawingSizes(boardSize, 0);\r
806   InitMenuChecks();\r
807   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
808 \r
809   /* [AS] Load textures if specified */\r
810   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
811   \r
812   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
813       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
814       liteBackTextureMode = appData.liteBackTextureMode;\r
815 \r
816       if (liteBackTexture == NULL && appData.debugMode) {\r
817           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
818       }\r
819   }\r
820   \r
821   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
822       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
823       darkBackTextureMode = appData.darkBackTextureMode;\r
824 \r
825       if (darkBackTexture == NULL && appData.debugMode) {\r
826           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
827       }\r
828   }\r
829 \r
830   mysrandom( (unsigned) time(NULL) );\r
831 \r
832   /* [AS] Restore layout */\r
833   if( wpMoveHistory.visible ) {\r
834       MoveHistoryPopUp();\r
835   }\r
836 \r
837   if( wpEvalGraph.visible ) {\r
838       EvalGraphPopUp();\r
839   }\r
840 \r
841   if( wpEngineOutput.visible ) {\r
842       EngineOutputPopUp();\r
843   }\r
844 \r
845   InitBackEnd2();\r
846 \r
847   /* Make the window visible; update its client area; and return "success" */\r
848   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
849   wp.length = sizeof(WINDOWPLACEMENT);\r
850   wp.flags = 0;\r
851   wp.showCmd = nCmdShow;\r
852   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
853   wp.rcNormalPosition.left = boardX;\r
854   wp.rcNormalPosition.right = boardX + winWidth;\r
855   wp.rcNormalPosition.top = boardY;\r
856   wp.rcNormalPosition.bottom = boardY + winHeight;\r
857   SetWindowPlacement(hwndMain, &wp);\r
858 \r
859   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
860                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
861 \r
862   if (hwndConsole) {\r
863 #if AOT_CONSOLE\r
864     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
865                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
866 #endif\r
867     ShowWindow(hwndConsole, nCmdShow);\r
868   }\r
869   UpdateWindow(hwnd);\r
870 \r
871   return TRUE;\r
872 \r
873 }\r
874 \r
875 \r
876 typedef enum {\r
877   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
878   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
879   ArgSettingsFilename,\r
880   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
881 } ArgType;\r
882 \r
883 typedef struct {\r
884   char *argName;\r
885   ArgType argType;\r
886   /***\r
887   union {\r
888     String *pString;       // ArgString\r
889     int *pInt;             // ArgInt\r
890     float *pFloat;         // ArgFloat\r
891     Boolean *pBoolean;     // ArgBoolean\r
892     COLORREF *pColor;      // ArgColor\r
893     ColorClass cc;         // ArgAttribs\r
894     String *pFilename;     // ArgFilename\r
895     BoardSize *pBoardSize; // ArgBoardSize\r
896     int whichFont;         // ArgFont\r
897     DCB *pDCB;             // ArgCommSettings\r
898     String *pFilename;     // ArgSettingsFilename\r
899   } argLoc;\r
900   ***/\r
901   LPVOID argLoc;\r
902   BOOL save;\r
903 } ArgDescriptor;\r
904 \r
905 int junk;\r
906 ArgDescriptor argDescriptors[] = {\r
907   /* positional arguments */\r
908   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
909   { "", ArgNone, NULL },\r
910   /* keyword arguments */\r
911   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
912   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
913   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
914   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
915   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
916   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
917   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
918   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
919   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
920   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
921   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
922   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
923   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
924   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
925   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
926   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
927   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
928   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
929     FALSE },\r
930   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
931     FALSE },\r
932   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
933     FALSE },\r
934   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
935   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
936     FALSE },\r
937   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
938   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
939   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
940   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
941   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
942   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
943   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
944   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
945   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
946   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
947   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
948   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
949   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
950   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
951   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
952   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
953   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
954   /*!!bitmapDirectory?*/\r
955   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
956   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
957   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
958   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
959   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
960   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
961   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
962   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
963   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
964   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
965   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
966   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
967   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
968   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
969   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
970   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
971   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
972   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
973   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
974   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
975   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
976   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
977   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
978   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
979   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
980   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
981   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
982   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
983   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
984   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
985   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
986   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
987   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
988   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
989   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
990   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
991   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
992   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
993   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
994   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
995   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
996   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
997   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
998   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
999   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1000   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1001   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1002   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
1003   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1004   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1005   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1006   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1007   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1008   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1009   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1010   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1011   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1012   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1013   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1014   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1015   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1016   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1017   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1018   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1019   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1020   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1021   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1022   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1023   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1024   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1025   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1026   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1027   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1028   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1029   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1030   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1031   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1032   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1033   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1034   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1035   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1036   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1037   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1038   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1039   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1040   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1041   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1042   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1043   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1044   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1045   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1046   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1047   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1048   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1049     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1050   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1051   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1052   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1053   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1054   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1055   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1056   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1057   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1058     TRUE }, /* must come after all fonts */\r
1059   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1060   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1061     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1062   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1063   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1064   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1065   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1066   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1067   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1068   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1069   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1070   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1071   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1072   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1073   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1074   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1075   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1076   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1077   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1078   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1079   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1080   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1081   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1082   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1083   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1084   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1085   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1086   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1087   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1088   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1089   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1090 #if 0\r
1091   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1092   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1093 #endif\r
1094   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1095   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1096   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1097   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1098   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1099   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1100   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1101   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1102   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1103   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1104   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1105   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1106   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1107   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1108   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1109   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1110   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1111   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1112   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1113   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1114   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1115   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1116   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1117   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1118   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1119   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1120   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1121   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1122   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1123   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1124   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1125   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1126   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1127   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1128   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1129   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1130   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1131   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1132   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1133   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1134   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1135   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1136   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1137   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1138   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1139   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1140   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1141   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1142   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1143   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1144   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1145   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1146   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1147   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1148   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1149   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1150   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1151   { "highlightLastMove", ArgBoolean,\r
1152     (LPVOID) &appData.highlightLastMove, TRUE },\r
1153   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1154   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1155   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1156   { "highlightDragging", ArgBoolean,\r
1157     (LPVOID) &appData.highlightDragging, TRUE },\r
1158   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1159   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1160   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1161   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1162   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1163   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1164   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1165   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1166   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1167   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1168   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1169   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1170   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1171   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1172   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1173   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1174   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1175   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1176   { "soundShout", ArgFilename,\r
1177     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1178   { "soundSShout", ArgFilename,\r
1179     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1180   { "soundChannel1", ArgFilename,\r
1181     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1182   { "soundChannel", ArgFilename,\r
1183     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1184   { "soundKibitz", ArgFilename,\r
1185     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1186   { "soundTell", ArgFilename,\r
1187     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1188   { "soundChallenge", ArgFilename,\r
1189     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1190   { "soundRequest", ArgFilename,\r
1191     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1192   { "soundSeek", ArgFilename,\r
1193     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1194   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1195   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1196   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1197   { "soundIcsLoss", ArgFilename, \r
1198     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1199   { "soundIcsDraw", ArgFilename, \r
1200     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1201   { "soundIcsUnfinished", ArgFilename, \r
1202     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1203   { "soundIcsAlarm", ArgFilename, \r
1204     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1205   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1206   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1207   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1208   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1209   { "reuseChessPrograms", ArgBoolean,\r
1210     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1211   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1212   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1213   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1214   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1215   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1216   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1217   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1218   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1219   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1220   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1221   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1222   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1223   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1224   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1225   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1226     TRUE },\r
1227   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1228     TRUE },\r
1229   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1230   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1231   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1232   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1233   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1234   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1235   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1236   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1237   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1238   /* [AS] New features */\r
1239   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1240   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1241   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1242   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1243   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1244   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1245   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1246   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1247   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1248   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1249   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1250   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1251   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1252   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1253   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1254   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1255   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1256   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1257   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1258   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1259   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1260   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1261   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1262   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1263   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1264   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1265   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1266   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1267   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1268   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1269   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1270   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1271   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1272   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1273   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1274   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1275   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1276   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1277   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1278   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1279   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1280   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1281   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1282   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1283   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1284   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1285   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1286   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1287   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1288   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1289 \r
1290   /* [HGM] board-size, adjudication and misc. options */\r
1291   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1292   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1293   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1294   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1295   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1296   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1297   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1298   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1299   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1300   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1301   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1302   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1303   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1304   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1305   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1306   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1307   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1308   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1309   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1310   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1311   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1312   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1313   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1314   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1315   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1316   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1317   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1318   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1319   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1320   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1321   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1322 \r
1323 #ifdef ZIPPY\r
1324   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1325   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1326   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1327   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1328   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1329   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1330   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1331   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1332   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1333   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1334   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1335   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1336   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1337     FALSE },\r
1338   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1339   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1340   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1341   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1342   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1343   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1344   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1345     FALSE },\r
1346   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1347   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1348   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1349   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1350   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1351   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1352   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1353   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1354   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1355   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1356   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1357   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1358   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1359   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1360   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1361   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1362   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1363   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1364   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1365 #endif\r
1366   /* [HGM] options for broadcasting and time odds */\r
1367   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1368   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1369   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1370   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1371   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1372   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1373   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1374   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1375   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1376   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1377   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1378 \r
1379   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1380   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1381   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1382   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1383   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1384   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1385   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1386   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1387   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1388   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1389   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1390   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1391   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1392   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1393   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1394   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1395   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1396   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1397   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1398   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1399   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1400   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1401   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1402   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1403   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1404   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1405   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1406   /* [AS] Layout stuff */\r
1407   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1408   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1409   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1410   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1411   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1412 \r
1413   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1414   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1415   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1416   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1417   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1418 \r
1419   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1420   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1421   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1422   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1423   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1424 \r
1425   { NULL, ArgNone, NULL, FALSE }\r
1426 };\r
1427 \r
1428 \r
1429 /* Kludge for indirection files on command line */\r
1430 char* lastIndirectionFilename;\r
1431 ArgDescriptor argDescriptorIndirection =\r
1432 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1433 \r
1434 \r
1435 VOID\r
1436 ExitArgError(char *msg, char *badArg)\r
1437 {\r
1438   char buf[MSG_SIZ];\r
1439 \r
1440   sprintf(buf, "%s %s", msg, badArg);\r
1441   DisplayFatalError(buf, 0, 2);\r
1442   exit(2);\r
1443 }\r
1444 \r
1445 /* Command line font name parser.  NULL name means do nothing.\r
1446    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1447    For backward compatibility, syntax without the colon is also\r
1448    accepted, but font names with digits in them won't work in that case.\r
1449 */\r
1450 VOID\r
1451 ParseFontName(char *name, MyFontParams *mfp)\r
1452 {\r
1453   char *p, *q;\r
1454   if (name == NULL) return;\r
1455   p = name;\r
1456   q = strchr(p, ':');\r
1457   if (q) {\r
1458     if (q - p >= sizeof(mfp->faceName))\r
1459       ExitArgError("Font name too long:", name);\r
1460     memcpy(mfp->faceName, p, q - p);\r
1461     mfp->faceName[q - p] = NULLCHAR;\r
1462     p = q + 1;\r
1463   } else {\r
1464     q = mfp->faceName;\r
1465     while (*p && !isdigit(*p)) {\r
1466       *q++ = *p++;\r
1467       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1468         ExitArgError("Font name too long:", name);\r
1469     }\r
1470     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1471     *q = NULLCHAR;\r
1472   }\r
1473   if (!*p) ExitArgError("Font point size missing:", name);\r
1474   mfp->pointSize = (float) atof(p);\r
1475   mfp->bold = (strchr(p, 'b') != NULL);\r
1476   mfp->italic = (strchr(p, 'i') != NULL);\r
1477   mfp->underline = (strchr(p, 'u') != NULL);\r
1478   mfp->strikeout = (strchr(p, 's') != NULL);\r
1479 }\r
1480 \r
1481 /* Color name parser.\r
1482    X version accepts X color names, but this one\r
1483    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1484 COLORREF\r
1485 ParseColorName(char *name)\r
1486 {\r
1487   int red, green, blue, count;\r
1488   char buf[MSG_SIZ];\r
1489 \r
1490   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1491   if (count != 3) {\r
1492     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1493       &red, &green, &blue);\r
1494   }\r
1495   if (count != 3) {\r
1496     sprintf(buf, "Can't parse color name %s", name);\r
1497     DisplayError(buf, 0);\r
1498     return RGB(0, 0, 0);\r
1499   }\r
1500   return PALETTERGB(red, green, blue);\r
1501 }\r
1502 \r
1503 \r
1504 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1505 {\r
1506   char *e = argValue;\r
1507   int eff = 0;\r
1508 \r
1509   while (*e) {\r
1510     if (*e == 'b')      eff |= CFE_BOLD;\r
1511     else if (*e == 'i') eff |= CFE_ITALIC;\r
1512     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1513     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1514     else if (*e == '#' || isdigit(*e)) break;\r
1515     e++;\r
1516   }\r
1517   *effects = eff;\r
1518   *color   = ParseColorName(e);\r
1519 }\r
1520 \r
1521 \r
1522 BoardSize\r
1523 ParseBoardSize(char *name)\r
1524 {\r
1525   BoardSize bs = SizeTiny;\r
1526   while (sizeInfo[bs].name != NULL) {\r
1527     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1528     bs++;\r
1529   }\r
1530   ExitArgError("Unrecognized board size value", name);\r
1531   return bs; /* not reached */\r
1532 }\r
1533 \r
1534 \r
1535 char\r
1536 StringGet(void *getClosure)\r
1537 {\r
1538   char **p = (char **) getClosure;\r
1539   return *((*p)++);\r
1540 }\r
1541 \r
1542 char\r
1543 FileGet(void *getClosure)\r
1544 {\r
1545   int c;\r
1546   FILE* f = (FILE*) getClosure;\r
1547 \r
1548   c = getc(f);\r
1549   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1550   if (c == EOF)\r
1551     return NULLCHAR;\r
1552   else\r
1553     return (char) c;\r
1554 }\r
1555 \r
1556 /* Parse settings file named "name". If file found, return the\r
1557    full name in fullname and return TRUE; else return FALSE */\r
1558 BOOLEAN\r
1559 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1560 {\r
1561   char *dummy;\r
1562   FILE *f;\r
1563   int ok; char buf[MSG_SIZ];\r
1564 \r
1565   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1566   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1567     sprintf(buf, "%s.ini", name);\r
1568     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1569   }\r
1570   if (ok) {\r
1571     f = fopen(fullname, "r");\r
1572     if (f != NULL) {\r
1573       ParseArgs(FileGet, f);\r
1574       fclose(f);\r
1575       return TRUE;\r
1576     }\r
1577   }\r
1578   return FALSE;\r
1579 }\r
1580 \r
1581 VOID\r
1582 ParseArgs(GetFunc get, void *cl)\r
1583 {\r
1584   char argName[ARG_MAX];\r
1585   char argValue[ARG_MAX];\r
1586   ArgDescriptor *ad;\r
1587   char start;\r
1588   char *q;\r
1589   int i, octval;\r
1590   char ch;\r
1591   int posarg = 0;\r
1592 \r
1593   ch = get(cl);\r
1594   for (;;) {\r
1595     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1596     if (ch == NULLCHAR) break;\r
1597     if (ch == ';') {\r
1598       /* Comment to end of line */\r
1599       ch = get(cl);\r
1600       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1601       continue;\r
1602     } else if (ch == '/' || ch == '-') {\r
1603       /* Switch */\r
1604       q = argName;\r
1605       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1606              ch != '\n' && ch != '\t') {\r
1607         *q++ = ch;\r
1608         ch = get(cl);\r
1609       }\r
1610       *q = NULLCHAR;\r
1611 \r
1612       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1613         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1614 \r
1615       if (ad->argName == NULL)\r
1616         ExitArgError("Unrecognized argument", argName);\r
1617 \r
1618     } else if (ch == '@') {\r
1619       /* Indirection file */\r
1620       ad = &argDescriptorIndirection;\r
1621       ch = get(cl);\r
1622     } else {\r
1623       /* Positional argument */\r
1624       ad = &argDescriptors[posarg++];\r
1625       strcpy(argName, ad->argName);\r
1626     }\r
1627 \r
1628     if (ad->argType == ArgTrue) {\r
1629       *(Boolean *) ad->argLoc = TRUE;\r
1630       continue;\r
1631     }\r
1632     if (ad->argType == ArgFalse) {\r
1633       *(Boolean *) ad->argLoc = FALSE;\r
1634       continue;\r
1635     }\r
1636 \r
1637     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1638     if (ch == NULLCHAR || ch == '\n') {\r
1639       ExitArgError("No value provided for argument", argName);\r
1640     }\r
1641     q = argValue;\r
1642     if (ch == '{') {\r
1643       // Quoting with { }.  No characters have to (or can) be escaped.\r
1644       // Thus the string cannot contain a '}' character.\r
1645       start = ch;\r
1646       ch = get(cl);\r
1647       while (start) {\r
1648         switch (ch) {\r
1649         case NULLCHAR:\r
1650           start = NULLCHAR;\r
1651           break;\r
1652           \r
1653         case '}':\r
1654           ch = get(cl);\r
1655           start = NULLCHAR;\r
1656           break;\r
1657 \r
1658         default:\r
1659           *q++ = ch;\r
1660           ch = get(cl);\r
1661           break;\r
1662         }\r
1663       }   \r
1664     } else if (ch == '\'' || ch == '"') {\r
1665       // Quoting with ' ' or " ", with \ as escape character.\r
1666       // Inconvenient for long strings that may contain Windows filenames.\r
1667       start = ch;\r
1668       ch = get(cl);\r
1669       while (start) {\r
1670         switch (ch) {\r
1671         case NULLCHAR:\r
1672           start = NULLCHAR;\r
1673           break;\r
1674 \r
1675         default:\r
1676         not_special:\r
1677           *q++ = ch;\r
1678           ch = get(cl);\r
1679           break;\r
1680 \r
1681         case '\'':\r
1682         case '\"':\r
1683           if (ch == start) {\r
1684             ch = get(cl);\r
1685             start = NULLCHAR;\r
1686             break;\r
1687           } else {\r
1688             goto not_special;\r
1689           }\r
1690 \r
1691         case '\\':\r
1692           if (ad->argType == ArgFilename\r
1693               || ad->argType == ArgSettingsFilename) {\r
1694               goto not_special;\r
1695           }\r
1696           ch = get(cl);\r
1697           switch (ch) {\r
1698           case NULLCHAR:\r
1699             ExitArgError("Incomplete \\ escape in value for", argName);\r
1700             break;\r
1701           case 'n':\r
1702             *q++ = '\n';\r
1703             ch = get(cl);\r
1704             break;\r
1705           case 'r':\r
1706             *q++ = '\r';\r
1707             ch = get(cl);\r
1708             break;\r
1709           case 't':\r
1710             *q++ = '\t';\r
1711             ch = get(cl);\r
1712             break;\r
1713           case 'b':\r
1714             *q++ = '\b';\r
1715             ch = get(cl);\r
1716             break;\r
1717           case 'f':\r
1718             *q++ = '\f';\r
1719             ch = get(cl);\r
1720             break;\r
1721           default:\r
1722             octval = 0;\r
1723             for (i = 0; i < 3; i++) {\r
1724               if (ch >= '0' && ch <= '7') {\r
1725                 octval = octval*8 + (ch - '0');\r
1726                 ch = get(cl);\r
1727               } else {\r
1728                 break;\r
1729               }\r
1730             }\r
1731             if (i > 0) {\r
1732               *q++ = (char) octval;\r
1733             } else {\r
1734               *q++ = ch;\r
1735               ch = get(cl);\r
1736             }\r
1737             break;\r
1738           }\r
1739           break;\r
1740         }\r
1741       }\r
1742     } else {\r
1743       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1744         *q++ = ch;\r
1745         ch = get(cl);\r
1746       }\r
1747     }\r
1748     *q = NULLCHAR;\r
1749 \r
1750     switch (ad->argType) {\r
1751     case ArgInt:\r
1752       *(int *) ad->argLoc = atoi(argValue);\r
1753       break;\r
1754 \r
1755     case ArgX:\r
1756       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1757       break;\r
1758 \r
1759     case ArgY:\r
1760       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1761       break;\r
1762 \r
1763     case ArgZ:\r
1764       *(int *) ad->argLoc = atoi(argValue);\r
1765       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1766       break;\r
1767 \r
1768     case ArgFloat:\r
1769       *(float *) ad->argLoc = (float) atof(argValue);\r
1770       break;\r
1771 \r
1772     case ArgString:\r
1773     case ArgFilename:\r
1774       *(char **) ad->argLoc = strdup(argValue);\r
1775       break;\r
1776 \r
1777     case ArgSettingsFilename:\r
1778       {\r
1779         char fullname[MSG_SIZ];\r
1780         if (ParseSettingsFile(argValue, fullname)) {\r
1781           if (ad->argLoc != NULL) {\r
1782             *(char **) ad->argLoc = strdup(fullname);\r
1783           }\r
1784         } else {\r
1785           if (ad->argLoc != NULL) {\r
1786           } else {\r
1787             ExitArgError("Failed to open indirection file", argValue);\r
1788           }\r
1789         }\r
1790       }\r
1791       break;\r
1792 \r
1793     case ArgBoolean:\r
1794       switch (argValue[0]) {\r
1795       case 't':\r
1796       case 'T':\r
1797         *(Boolean *) ad->argLoc = TRUE;\r
1798         break;\r
1799       case 'f':\r
1800       case 'F':\r
1801         *(Boolean *) ad->argLoc = FALSE;\r
1802         break;\r
1803       default:\r
1804         ExitArgError("Unrecognized boolean argument value", argValue);\r
1805         break;\r
1806       }\r
1807       break;\r
1808 \r
1809     case ArgColor:\r
1810       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1811       break;\r
1812 \r
1813     case ArgAttribs: {\r
1814       ColorClass cc = (ColorClass)ad->argLoc;\r
1815       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1816       }\r
1817       break;\r
1818       \r
1819     case ArgBoardSize:\r
1820       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1821       break;\r
1822 \r
1823     case ArgFont:\r
1824       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1825       break;\r
1826 \r
1827     case ArgCommSettings:\r
1828       ParseCommSettings(argValue, &dcb);\r
1829       break;\r
1830 \r
1831     case ArgNone:\r
1832       ExitArgError("Unrecognized argument", argValue);\r
1833       break;\r
1834     case ArgTrue:\r
1835     case ArgFalse: ;\r
1836     }\r
1837   }\r
1838 }\r
1839 \r
1840 VOID\r
1841 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1842 {\r
1843   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1844   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1845   DeleteDC(hdc);\r
1846   lf->lfWidth = 0;\r
1847   lf->lfEscapement = 0;\r
1848   lf->lfOrientation = 0;\r
1849   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1850   lf->lfItalic = mfp->italic;\r
1851   lf->lfUnderline = mfp->underline;\r
1852   lf->lfStrikeOut = mfp->strikeout;\r
1853   lf->lfCharSet = DEFAULT_CHARSET;\r
1854   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1855   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1856   lf->lfQuality = DEFAULT_QUALITY;\r
1857   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1858   strcpy(lf->lfFaceName, mfp->faceName);\r
1859 }\r
1860 \r
1861 VOID\r
1862 CreateFontInMF(MyFont *mf)\r
1863 {\r
1864   LFfromMFP(&mf->lf, &mf->mfp);\r
1865   if (mf->hf) DeleteObject(mf->hf);\r
1866   mf->hf = CreateFontIndirect(&mf->lf);\r
1867 }\r
1868 \r
1869 VOID\r
1870 SetDefaultTextAttribs()\r
1871 {\r
1872   ColorClass cc;\r
1873   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1874     ParseAttribs(&textAttribs[cc].color, \r
1875                  &textAttribs[cc].effects, \r
1876                  defaultTextAttribs[cc]);\r
1877   }\r
1878 }\r
1879 \r
1880 VOID\r
1881 SetDefaultSounds()\r
1882 {\r
1883   ColorClass cc;\r
1884   SoundClass sc;\r
1885   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1886     textAttribs[cc].sound.name = strdup("");\r
1887     textAttribs[cc].sound.data = NULL;\r
1888   }\r
1889   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1890     sounds[sc].name = strdup("");\r
1891     sounds[sc].data = NULL;\r
1892   }\r
1893   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1894 }\r
1895 \r
1896 VOID\r
1897 LoadAllSounds()\r
1898 {\r
1899   ColorClass cc;\r
1900   SoundClass sc;\r
1901   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1902     MyLoadSound(&textAttribs[cc].sound);\r
1903   }\r
1904   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1905     MyLoadSound(&sounds[sc]);\r
1906   }\r
1907 }\r
1908 \r
1909 VOID\r
1910 InitAppData(LPSTR lpCmdLine)\r
1911 {\r
1912   int i, j;\r
1913   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1914   char *dummy, *p;\r
1915 \r
1916   programName = szAppName;\r
1917 \r
1918   /* Initialize to defaults */\r
1919   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1920   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1921   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1922   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1923   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1924   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1925   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1926   SetDefaultTextAttribs();\r
1927   SetDefaultSounds();\r
1928   appData.movesPerSession = MOVES_PER_SESSION;\r
1929   appData.initString = INIT_STRING;\r
1930   appData.secondInitString = INIT_STRING;\r
1931   appData.firstComputerString = COMPUTER_STRING;\r
1932   appData.secondComputerString = COMPUTER_STRING;\r
1933   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1934   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1935   appData.firstPlaysBlack = FALSE;\r
1936   appData.noChessProgram = FALSE;\r
1937   chessProgram = FALSE;\r
1938   appData.firstHost = FIRST_HOST;\r
1939   appData.secondHost = SECOND_HOST;\r
1940   appData.firstDirectory = FIRST_DIRECTORY;\r
1941   appData.secondDirectory = SECOND_DIRECTORY;\r
1942   appData.bitmapDirectory = "";\r
1943   appData.remoteShell = REMOTE_SHELL;\r
1944   appData.remoteUser = "";\r
1945   appData.timeDelay = TIME_DELAY;\r
1946   appData.timeControl = TIME_CONTROL;\r
1947   appData.timeIncrement = TIME_INCREMENT;\r
1948   appData.icsActive = FALSE;\r
1949   appData.icsHost = "";\r
1950   appData.icsPort = ICS_PORT;\r
1951   appData.icsCommPort = ICS_COMM_PORT;\r
1952   appData.icsLogon = ICS_LOGON;\r
1953   appData.icsHelper = "";\r
1954   appData.useTelnet = FALSE;\r
1955   appData.telnetProgram = TELNET_PROGRAM;\r
1956   appData.gateway = "";\r
1957   appData.loadGameFile = "";\r
1958   appData.loadGameIndex = 0;\r
1959   appData.saveGameFile = "";\r
1960   appData.autoSaveGames = FALSE;\r
1961   appData.loadPositionFile = "";\r
1962   appData.loadPositionIndex = 1;\r
1963   appData.savePositionFile = "";\r
1964   appData.matchMode = FALSE;\r
1965   appData.matchGames = 0;\r
1966   appData.monoMode = FALSE;\r
1967   appData.debugMode = FALSE;\r
1968   appData.clockMode = TRUE;\r
1969   boardSize = (BoardSize) -1; /* determine by screen size */\r
1970   appData.Iconic = FALSE; /*unused*/\r
1971   appData.searchTime = "";\r
1972   appData.searchDepth = 0;\r
1973   appData.showCoords = FALSE;\r
1974   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1975   appData.autoCallFlag = FALSE;\r
1976   appData.flipView = FALSE;\r
1977   appData.autoFlipView = TRUE;\r
1978   appData.cmailGameName = "";\r
1979   appData.alwaysPromoteToQueen = FALSE;\r
1980   appData.oldSaveStyle = FALSE;\r
1981   appData.quietPlay = FALSE;\r
1982   appData.showThinking = FALSE;\r
1983   appData.ponderNextMove = TRUE;\r
1984   appData.periodicUpdates = TRUE;\r
1985   appData.popupExitMessage = TRUE;\r
1986   appData.popupMoveErrors = FALSE;\r
1987   appData.autoObserve = FALSE;\r
1988   appData.autoComment = FALSE;\r
1989   appData.animate = TRUE;\r
1990   appData.animSpeed = 10;\r
1991   appData.animateDragging = TRUE;\r
1992   appData.highlightLastMove = TRUE;\r
1993   appData.getMoveList = TRUE;\r
1994   appData.testLegality = TRUE;\r
1995   appData.premove = TRUE;\r
1996   appData.premoveWhite = FALSE;\r
1997   appData.premoveWhiteText = "";\r
1998   appData.premoveBlack = FALSE;\r
1999   appData.premoveBlackText = "";\r
2000   appData.icsAlarm = TRUE;\r
2001   appData.icsAlarmTime = 5000;\r
2002   appData.autoRaiseBoard = TRUE;\r
2003   appData.localLineEditing = TRUE;\r
2004   appData.colorize = TRUE;\r
2005   appData.reuseFirst = TRUE;\r
2006   appData.reuseSecond = TRUE;\r
2007   appData.blindfold = FALSE;\r
2008   appData.icsEngineAnalyze = FALSE;\r
2009   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2010   dcb.DCBlength = sizeof(DCB);\r
2011   dcb.BaudRate = 9600;\r
2012   dcb.fBinary = TRUE;\r
2013   dcb.fParity = FALSE;\r
2014   dcb.fOutxCtsFlow = FALSE;\r
2015   dcb.fOutxDsrFlow = FALSE;\r
2016   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2017   dcb.fDsrSensitivity = FALSE;\r
2018   dcb.fTXContinueOnXoff = TRUE;\r
2019   dcb.fOutX = FALSE;\r
2020   dcb.fInX = FALSE;\r
2021   dcb.fNull = FALSE;\r
2022   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2023   dcb.fAbortOnError = FALSE;\r
2024   dcb.ByteSize = 7;\r
2025   dcb.Parity = SPACEPARITY;\r
2026   dcb.StopBits = ONESTOPBIT;\r
2027   settingsFileName = SETTINGS_FILE;\r
2028   saveSettingsOnExit = TRUE;\r
2029   boardX = CW_USEDEFAULT;\r
2030   boardY = CW_USEDEFAULT;\r
2031   analysisX = CW_USEDEFAULT; \r
2032   analysisY = CW_USEDEFAULT; \r
2033   analysisW = CW_USEDEFAULT;\r
2034   analysisH = CW_USEDEFAULT;\r
2035   commentX = CW_USEDEFAULT; \r
2036   commentY = CW_USEDEFAULT; \r
2037   commentW = CW_USEDEFAULT;\r
2038   commentH = CW_USEDEFAULT;\r
2039   editTagsX = CW_USEDEFAULT; \r
2040   editTagsY = CW_USEDEFAULT; \r
2041   editTagsW = CW_USEDEFAULT;\r
2042   editTagsH = CW_USEDEFAULT;\r
2043   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2044   icsNames = ICS_NAMES;\r
2045   firstChessProgramNames = FCP_NAMES;\r
2046   secondChessProgramNames = SCP_NAMES;\r
2047   appData.initialMode = "";\r
2048   appData.variant = "normal";\r
2049   appData.firstProtocolVersion = PROTOVER;\r
2050   appData.secondProtocolVersion = PROTOVER;\r
2051   appData.showButtonBar = TRUE;\r
2052 \r
2053    /* [AS] New properties (see comments in header file) */\r
2054   appData.firstScoreIsAbsolute = FALSE;\r
2055   appData.secondScoreIsAbsolute = FALSE;\r
2056   appData.saveExtendedInfoInPGN = FALSE;\r
2057   appData.hideThinkingFromHuman = FALSE;\r
2058   appData.liteBackTextureFile = "";\r
2059   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2060   appData.darkBackTextureFile = "";\r
2061   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2062   appData.renderPiecesWithFont = "";\r
2063   appData.fontToPieceTable = "";\r
2064   appData.fontBackColorWhite = 0;\r
2065   appData.fontForeColorWhite = 0;\r
2066   appData.fontBackColorBlack = 0;\r
2067   appData.fontForeColorBlack = 0;\r
2068   appData.fontPieceSize = 80;\r
2069   appData.overrideLineGap = 1;\r
2070   appData.adjudicateLossThreshold = 0;\r
2071   appData.delayBeforeQuit = 0;\r
2072   appData.delayAfterQuit = 0;\r
2073   appData.nameOfDebugFile = "winboard.debug";\r
2074   appData.pgnEventHeader = "Computer Chess Game";\r
2075   appData.defaultFrcPosition = -1;\r
2076   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2077   appData.saveOutOfBookInfo = TRUE;\r
2078   appData.showEvalInMoveHistory = TRUE;\r
2079   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2080   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2081   appData.highlightMoveWithArrow = FALSE;\r
2082   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2083   appData.useStickyWindows = TRUE;\r
2084   appData.adjudicateDrawMoves = 0;\r
2085   appData.autoDisplayComment = TRUE;\r
2086   appData.autoDisplayTags = TRUE;\r
2087   appData.firstIsUCI = FALSE;\r
2088   appData.secondIsUCI = FALSE;\r
2089   appData.firstHasOwnBookUCI = TRUE;\r
2090   appData.secondHasOwnBookUCI = TRUE;\r
2091   appData.polyglotDir = "";\r
2092   appData.usePolyglotBook = FALSE;\r
2093   appData.polyglotBook = "";\r
2094   appData.defaultHashSize = 64;\r
2095   appData.defaultCacheSizeEGTB = 4;\r
2096   appData.defaultPathEGTB = "c:\\egtb";\r
2097   appData.firstOptions = "";\r
2098   appData.secondOptions = "";\r
2099 \r
2100   InitWindowPlacement( &wpGameList );\r
2101   InitWindowPlacement( &wpMoveHistory );\r
2102   InitWindowPlacement( &wpEvalGraph );\r
2103   InitWindowPlacement( &wpEngineOutput );\r
2104   InitWindowPlacement( &wpConsole );\r
2105 \r
2106   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2107   appData.NrFiles      = -1;\r
2108   appData.NrRanks      = -1;\r
2109   appData.holdingsSize = -1;\r
2110   appData.testClaims   = FALSE;\r
2111   appData.checkMates   = FALSE;\r
2112   appData.materialDraws= FALSE;\r
2113   appData.trivialDraws = FALSE;\r
2114   appData.ruleMoves    = 51;\r
2115   appData.drawRepeats  = 6;\r
2116   appData.matchPause   = 10000;\r
2117   appData.alphaRank    = FALSE;\r
2118   appData.allWhite     = FALSE;\r
2119   appData.upsideDown   = FALSE;\r
2120   appData.serverPause  = 15;\r
2121   appData.serverMovesName   = NULL;\r
2122   appData.suppressLoadMoves = FALSE;\r
2123   appData.firstTimeOdds  = 1;\r
2124   appData.secondTimeOdds = 1;\r
2125   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2126   appData.secondAccumulateTC = 1;\r
2127   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2128   appData.secondNPS = -1;\r
2129   appData.engineComments = 1;\r
2130   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2131   appData.egtFormats = "";\r
2132 \r
2133 #ifdef ZIPPY\r
2134   appData.zippyTalk = ZIPPY_TALK;\r
2135   appData.zippyPlay = ZIPPY_PLAY;\r
2136   appData.zippyLines = ZIPPY_LINES;\r
2137   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2138   appData.zippyPassword = ZIPPY_PASSWORD;\r
2139   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2140   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2141   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2142   appData.zippyUseI = ZIPPY_USE_I;\r
2143   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2144   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2145   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2146   appData.zippyGameStart = ZIPPY_GAME_START;\r
2147   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2148   appData.zippyAbort = ZIPPY_ABORT;\r
2149   appData.zippyVariants = ZIPPY_VARIANTS;\r
2150   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2151   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2152 #endif\r
2153 \r
2154   /* Point font array elements to structures and\r
2155      parse default font names */\r
2156   for (i=0; i<NUM_FONTS; i++) {\r
2157     for (j=0; j<NUM_SIZES; j++) {\r
2158       font[j][i] = &fontRec[j][i];\r
2159       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2160     }\r
2161   }\r
2162   \r
2163   /* Parse default settings file if any */\r
2164   if (ParseSettingsFile(settingsFileName, buf)) {\r
2165     settingsFileName = strdup(buf);\r
2166   }\r
2167 \r
2168   /* Parse command line */\r
2169   ParseArgs(StringGet, &lpCmdLine);\r
2170 \r
2171   /* [HGM] make sure board size is acceptable */\r
2172   if(appData.NrFiles > BOARD_SIZE ||\r
2173      appData.NrRanks > BOARD_SIZE   )\r
2174       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2175 \r
2176   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2177    * with options from the command line, we now make an even higher priority\r
2178    * overrule by WB options attached to the engine command line. This so that\r
2179    * tournament managers can use WB options (such as /timeOdds) that follow\r
2180    * the engines.\r
2181    */\r
2182   if(appData.firstChessProgram != NULL) {\r
2183       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2184       static char *f = "first";\r
2185       char buf[MSG_SIZ], *q = buf;\r
2186       if(p != NULL) { // engine command line contains WinBoard options\r
2187           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2188           ParseArgs(StringGet, &q);\r
2189           p[-1] = 0; // cut them offengine command line\r
2190       }\r
2191   }\r
2192   // now do same for second chess program\r
2193   if(appData.secondChessProgram != NULL) {\r
2194       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2195       static char *s = "second";\r
2196       char buf[MSG_SIZ], *q = buf;\r
2197       if(p != NULL) { // engine command line contains WinBoard options\r
2198           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2199           ParseArgs(StringGet, &q);\r
2200           p[-1] = 0; // cut them offengine command line\r
2201       }\r
2202   }\r
2203 \r
2204 \r
2205   /* Propagate options that affect others */\r
2206   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2207   if (appData.icsActive || appData.noChessProgram) {\r
2208      chessProgram = FALSE;  /* not local chess program mode */\r
2209   }\r
2210 \r
2211   /* Open startup dialog if needed */\r
2212   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2213       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2214       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2215                         *appData.secondChessProgram == NULLCHAR))) {\r
2216     FARPROC lpProc;\r
2217     \r
2218     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2219     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2220     FreeProcInstance(lpProc);\r
2221   }\r
2222 \r
2223   /* Make sure save files land in the right (?) directory */\r
2224   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2225     appData.saveGameFile = strdup(buf);\r
2226   }\r
2227   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2228     appData.savePositionFile = strdup(buf);\r
2229   }\r
2230 \r
2231   /* Finish initialization for fonts and sounds */\r
2232   for (i=0; i<NUM_FONTS; i++) {\r
2233     for (j=0; j<NUM_SIZES; j++) {\r
2234       CreateFontInMF(font[j][i]);\r
2235     }\r
2236   }\r
2237   /* xboard, and older WinBoards, controlled the move sound with the\r
2238      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2239      always turn the option on (so that the backend will call us),\r
2240      then let the user turn the sound off by setting it to silence if\r
2241      desired.  To accommodate old winboard.ini files saved by old\r
2242      versions of WinBoard, we also turn off the sound if the option\r
2243      was initially set to false. */\r
2244   if (!appData.ringBellAfterMoves) {\r
2245     sounds[(int)SoundMove].name = strdup("");\r
2246     appData.ringBellAfterMoves = TRUE;\r
2247   }\r
2248   GetCurrentDirectory(MSG_SIZ, currDir);\r
2249   SetCurrentDirectory(installDir);\r
2250   LoadAllSounds();\r
2251   SetCurrentDirectory(currDir);\r
2252 \r
2253   p = icsTextMenuString;\r
2254   if (p[0] == '@') {\r
2255     FILE* f = fopen(p + 1, "r");\r
2256     if (f == NULL) {\r
2257       DisplayFatalError(p + 1, errno, 2);\r
2258       return;\r
2259     }\r
2260     i = fread(buf, 1, sizeof(buf)-1, f);\r
2261     fclose(f);\r
2262     buf[i] = NULLCHAR;\r
2263     p = buf;\r
2264   }\r
2265   ParseIcsTextMenu(strdup(p));\r
2266 }\r
2267 \r
2268 \r
2269 VOID\r
2270 InitMenuChecks()\r
2271 {\r
2272   HMENU hmenu = GetMenu(hwndMain);\r
2273 \r
2274   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2275                         MF_BYCOMMAND|((appData.icsActive &&\r
2276                                        *appData.icsCommPort != NULLCHAR) ?\r
2277                                       MF_ENABLED : MF_GRAYED));\r
2278   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2279                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2280                                      MF_CHECKED : MF_UNCHECKED));\r
2281 }\r
2282 \r
2283 \r
2284 VOID\r
2285 SaveSettings(char* name)\r
2286 {\r
2287   FILE *f;\r
2288   ArgDescriptor *ad;\r
2289   WINDOWPLACEMENT wp;\r
2290   char dir[MSG_SIZ];\r
2291 \r
2292   if (!hwndMain) return;\r
2293 \r
2294   GetCurrentDirectory(MSG_SIZ, dir);\r
2295   SetCurrentDirectory(installDir);\r
2296   f = fopen(name, "w");\r
2297   SetCurrentDirectory(dir);\r
2298   if (f == NULL) {\r
2299     DisplayError(name, errno);\r
2300     return;\r
2301   }\r
2302   fprintf(f, ";\n");\r
2303   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2304   fprintf(f, ";\n");\r
2305   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2306   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2307   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2308   fprintf(f, ";\n");\r
2309 \r
2310   wp.length = sizeof(WINDOWPLACEMENT);\r
2311   GetWindowPlacement(hwndMain, &wp);\r
2312   boardX = wp.rcNormalPosition.left;\r
2313   boardY = wp.rcNormalPosition.top;\r
2314 \r
2315   if (hwndConsole) {\r
2316     GetWindowPlacement(hwndConsole, &wp);\r
2317     wpConsole.x = wp.rcNormalPosition.left;\r
2318     wpConsole.y = wp.rcNormalPosition.top;\r
2319     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2320     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2321   }\r
2322 \r
2323   if (analysisDialog) {\r
2324     GetWindowPlacement(analysisDialog, &wp);\r
2325     analysisX = wp.rcNormalPosition.left;\r
2326     analysisY = wp.rcNormalPosition.top;\r
2327     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2328     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2329   }\r
2330 \r
2331   if (commentDialog) {\r
2332     GetWindowPlacement(commentDialog, &wp);\r
2333     commentX = wp.rcNormalPosition.left;\r
2334     commentY = wp.rcNormalPosition.top;\r
2335     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2336     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2337   }\r
2338 \r
2339   if (editTagsDialog) {\r
2340     GetWindowPlacement(editTagsDialog, &wp);\r
2341     editTagsX = wp.rcNormalPosition.left;\r
2342     editTagsY = wp.rcNormalPosition.top;\r
2343     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2344     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2345   }\r
2346 \r
2347   if (gameListDialog) {\r
2348     GetWindowPlacement(gameListDialog, &wp);\r
2349     wpGameList.x = wp.rcNormalPosition.left;\r
2350     wpGameList.y = wp.rcNormalPosition.top;\r
2351     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2352     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2353   }\r
2354 \r
2355   /* [AS] Move history */\r
2356   wpMoveHistory.visible = MoveHistoryIsUp();\r
2357   \r
2358   if( moveHistoryDialog ) {\r
2359     GetWindowPlacement(moveHistoryDialog, &wp);\r
2360     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2361     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2362     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2363     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2364   }\r
2365 \r
2366   /* [AS] Eval graph */\r
2367   wpEvalGraph.visible = EvalGraphIsUp();\r
2368 \r
2369   if( evalGraphDialog ) {\r
2370     GetWindowPlacement(evalGraphDialog, &wp);\r
2371     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2372     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2373     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2374     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2375   }\r
2376 \r
2377   /* [AS] Engine output */\r
2378   wpEngineOutput.visible = EngineOutputIsUp();\r
2379 \r
2380   if( engineOutputDialog ) {\r
2381     GetWindowPlacement(engineOutputDialog, &wp);\r
2382     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2383     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2384     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2385     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2386   }\r
2387 \r
2388   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2389     if (!ad->save) continue;\r
2390     switch (ad->argType) {\r
2391     case ArgString:\r
2392       {\r
2393         char *p = *(char **)ad->argLoc;\r
2394         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2395           /* Quote multiline values or \-containing values\r
2396              with { } if possible */\r
2397           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2398         } else {\r
2399           /* Else quote with " " */\r
2400           fprintf(f, "/%s=\"", ad->argName);\r
2401           while (*p) {\r
2402             if (*p == '\n') fprintf(f, "\n");\r
2403             else if (*p == '\r') fprintf(f, "\\r");\r
2404             else if (*p == '\t') fprintf(f, "\\t");\r
2405             else if (*p == '\b') fprintf(f, "\\b");\r
2406             else if (*p == '\f') fprintf(f, "\\f");\r
2407             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2408             else if (*p == '\"') fprintf(f, "\\\"");\r
2409             else if (*p == '\\') fprintf(f, "\\\\");\r
2410             else putc(*p, f);\r
2411             p++;\r
2412           }\r
2413           fprintf(f, "\"\n");\r
2414         }\r
2415       }\r
2416       break;\r
2417     case ArgInt:\r
2418     case ArgZ:\r
2419       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2420       break;\r
2421     case ArgX:\r
2422       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2423       break;\r
2424     case ArgY:\r
2425       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2426       break;\r
2427     case ArgFloat:\r
2428       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2429       break;\r
2430     case ArgBoolean:\r
2431       fprintf(f, "/%s=%s\n", ad->argName, \r
2432         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2433       break;\r
2434     case ArgTrue:\r
2435       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2436       break;\r
2437     case ArgFalse:\r
2438       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2439       break;\r
2440     case ArgColor:\r
2441       {\r
2442         COLORREF color = *(COLORREF *)ad->argLoc;\r
2443         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2444           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2445       }\r
2446       break;\r
2447     case ArgAttribs:\r
2448       {\r
2449         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2450         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2451           (ta->effects & CFE_BOLD) ? "b" : "",\r
2452           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2453           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2454           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2455           (ta->effects) ? " " : "",\r
2456           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2457       }\r
2458       break;\r
2459     case ArgFilename:\r
2460       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2461         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2462       } else {\r
2463         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2464       }\r
2465       break;\r
2466     case ArgBoardSize:\r
2467       fprintf(f, "/%s=%s\n", ad->argName,\r
2468               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2469       break;\r
2470     case ArgFont:\r
2471       {\r
2472         int bs;\r
2473         for (bs=0; bs<NUM_SIZES; bs++) {\r
2474           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2475           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2476           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2477             ad->argName, mfp->faceName, mfp->pointSize,\r
2478             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2479             mfp->bold ? "b" : "",\r
2480             mfp->italic ? "i" : "",\r
2481             mfp->underline ? "u" : "",\r
2482             mfp->strikeout ? "s" : "");\r
2483         }\r
2484       }\r
2485       break;\r
2486     case ArgCommSettings:\r
2487       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2488     case ArgNone:\r
2489     case ArgSettingsFilename: ;\r
2490     }\r
2491   }\r
2492   fclose(f);\r
2493 }\r
2494 \r
2495 \r
2496 \r
2497 /*---------------------------------------------------------------------------*\\r
2498  *\r
2499  * GDI board drawing routines\r
2500  *\r
2501 \*---------------------------------------------------------------------------*/\r
2502 \r
2503 /* [AS] Draw square using background texture */\r
2504 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2505 {\r
2506     XFORM   x;\r
2507 \r
2508     if( mode == 0 ) {\r
2509         return; /* Should never happen! */\r
2510     }\r
2511 \r
2512     SetGraphicsMode( dst, GM_ADVANCED );\r
2513 \r
2514     switch( mode ) {\r
2515     case 1:\r
2516         /* Identity */\r
2517         break;\r
2518     case 2:\r
2519         /* X reflection */\r
2520         x.eM11 = -1.0;\r
2521         x.eM12 = 0;\r
2522         x.eM21 = 0;\r
2523         x.eM22 = 1.0;\r
2524         x.eDx = (FLOAT) dw + dx - 1;\r
2525         x.eDy = 0;\r
2526         dx = 0;\r
2527         SetWorldTransform( dst, &x );\r
2528         break;\r
2529     case 3:\r
2530         /* Y reflection */\r
2531         x.eM11 = 1.0;\r
2532         x.eM12 = 0;\r
2533         x.eM21 = 0;\r
2534         x.eM22 = -1.0;\r
2535         x.eDx = 0;\r
2536         x.eDy = (FLOAT) dh + dy - 1;\r
2537         dy = 0;\r
2538         SetWorldTransform( dst, &x );\r
2539         break;\r
2540     case 4:\r
2541         /* X/Y flip */\r
2542         x.eM11 = 0;\r
2543         x.eM12 = 1.0;\r
2544         x.eM21 = 1.0;\r
2545         x.eM22 = 0;\r
2546         x.eDx = (FLOAT) dx;\r
2547         x.eDy = (FLOAT) dy;\r
2548         dx = 0;\r
2549         dy = 0;\r
2550         SetWorldTransform( dst, &x );\r
2551         break;\r
2552     }\r
2553 \r
2554     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2555 \r
2556     x.eM11 = 1.0;\r
2557     x.eM12 = 0;\r
2558     x.eM21 = 0;\r
2559     x.eM22 = 1.0;\r
2560     x.eDx = 0;\r
2561     x.eDy = 0;\r
2562     SetWorldTransform( dst, &x );\r
2563 \r
2564     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2565 }\r
2566 \r
2567 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2568 enum {\r
2569     PM_WP = (int) WhitePawn, \r
2570     PM_WN = (int) WhiteKnight, \r
2571     PM_WB = (int) WhiteBishop, \r
2572     PM_WR = (int) WhiteRook, \r
2573     PM_WQ = (int) WhiteQueen, \r
2574     PM_WF = (int) WhiteFerz, \r
2575     PM_WW = (int) WhiteWazir, \r
2576     PM_WE = (int) WhiteAlfil, \r
2577     PM_WM = (int) WhiteMan, \r
2578     PM_WO = (int) WhiteCannon, \r
2579     PM_WU = (int) WhiteUnicorn, \r
2580     PM_WH = (int) WhiteNightrider, \r
2581     PM_WA = (int) WhiteAngel, \r
2582     PM_WC = (int) WhiteMarshall, \r
2583     PM_WAB = (int) WhiteCardinal, \r
2584     PM_WD = (int) WhiteDragon, \r
2585     PM_WL = (int) WhiteLance, \r
2586     PM_WS = (int) WhiteCobra, \r
2587     PM_WV = (int) WhiteFalcon, \r
2588     PM_WSG = (int) WhiteSilver, \r
2589     PM_WG = (int) WhiteGrasshopper, \r
2590     PM_WK = (int) WhiteKing,\r
2591     PM_BP = (int) BlackPawn, \r
2592     PM_BN = (int) BlackKnight, \r
2593     PM_BB = (int) BlackBishop, \r
2594     PM_BR = (int) BlackRook, \r
2595     PM_BQ = (int) BlackQueen, \r
2596     PM_BF = (int) BlackFerz, \r
2597     PM_BW = (int) BlackWazir, \r
2598     PM_BE = (int) BlackAlfil, \r
2599     PM_BM = (int) BlackMan,\r
2600     PM_BO = (int) BlackCannon, \r
2601     PM_BU = (int) BlackUnicorn, \r
2602     PM_BH = (int) BlackNightrider, \r
2603     PM_BA = (int) BlackAngel, \r
2604     PM_BC = (int) BlackMarshall, \r
2605     PM_BG = (int) BlackGrasshopper, \r
2606     PM_BAB = (int) BlackCardinal,\r
2607     PM_BD = (int) BlackDragon,\r
2608     PM_BL = (int) BlackLance,\r
2609     PM_BS = (int) BlackCobra,\r
2610     PM_BV = (int) BlackFalcon,\r
2611     PM_BSG = (int) BlackSilver,\r
2612     PM_BK = (int) BlackKing\r
2613 };\r
2614 \r
2615 static HFONT hPieceFont = NULL;\r
2616 static HBITMAP hPieceMask[(int) EmptySquare];\r
2617 static HBITMAP hPieceFace[(int) EmptySquare];\r
2618 static int fontBitmapSquareSize = 0;\r
2619 static char pieceToFontChar[(int) EmptySquare] =\r
2620                               { 'p', 'n', 'b', 'r', 'q', \r
2621                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2622                       'k', 'o', 'm', 'v', 't', 'w', \r
2623                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2624                                                               'l' };\r
2625 \r
2626 extern BOOL SetCharTable( char *table, const char * map );\r
2627 /* [HGM] moved to backend.c */\r
2628 \r
2629 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2630 {\r
2631     HBRUSH hbrush;\r
2632     BYTE r1 = GetRValue( color );\r
2633     BYTE g1 = GetGValue( color );\r
2634     BYTE b1 = GetBValue( color );\r
2635     BYTE r2 = r1 / 2;\r
2636     BYTE g2 = g1 / 2;\r
2637     BYTE b2 = b1 / 2;\r
2638     RECT rc;\r
2639 \r
2640     /* Create a uniform background first */\r
2641     hbrush = CreateSolidBrush( color );\r
2642     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2643     FillRect( hdc, &rc, hbrush );\r
2644     DeleteObject( hbrush );\r
2645     \r
2646     if( mode == 1 ) {\r
2647         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2648         int steps = squareSize / 2;\r
2649         int i;\r
2650 \r
2651         for( i=0; i<steps; i++ ) {\r
2652             BYTE r = r1 - (r1-r2) * i / steps;\r
2653             BYTE g = g1 - (g1-g2) * i / steps;\r
2654             BYTE b = b1 - (b1-b2) * i / steps;\r
2655 \r
2656             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2657             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2658             FillRect( hdc, &rc, hbrush );\r
2659             DeleteObject(hbrush);\r
2660         }\r
2661     }\r
2662     else if( mode == 2 ) {\r
2663         /* Diagonal gradient, good more or less for every piece */\r
2664         POINT triangle[3];\r
2665         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2666         HBRUSH hbrush_old;\r
2667         int steps = squareSize;\r
2668         int i;\r
2669 \r
2670         triangle[0].x = squareSize - steps;\r
2671         triangle[0].y = squareSize;\r
2672         triangle[1].x = squareSize;\r
2673         triangle[1].y = squareSize;\r
2674         triangle[2].x = squareSize;\r
2675         triangle[2].y = squareSize - steps;\r
2676 \r
2677         for( i=0; i<steps; i++ ) {\r
2678             BYTE r = r1 - (r1-r2) * i / steps;\r
2679             BYTE g = g1 - (g1-g2) * i / steps;\r
2680             BYTE b = b1 - (b1-b2) * i / steps;\r
2681 \r
2682             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2683             hbrush_old = SelectObject( hdc, hbrush );\r
2684             Polygon( hdc, triangle, 3 );\r
2685             SelectObject( hdc, hbrush_old );\r
2686             DeleteObject(hbrush);\r
2687             triangle[0].x++;\r
2688             triangle[2].y++;\r
2689         }\r
2690 \r
2691         SelectObject( hdc, hpen );\r
2692     }\r
2693 }\r
2694 \r
2695 /*\r
2696     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2697     seems to work ok. The main problem here is to find the "inside" of a chess\r
2698     piece: follow the steps as explained below.\r
2699 */\r
2700 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2701 {\r
2702     HBITMAP hbm;\r
2703     HBITMAP hbm_old;\r
2704     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2705     RECT rc;\r
2706     SIZE sz;\r
2707     POINT pt;\r
2708     int backColor = whitePieceColor; \r
2709     int foreColor = blackPieceColor;\r
2710     \r
2711     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2712         backColor = appData.fontBackColorWhite;\r
2713         foreColor = appData.fontForeColorWhite;\r
2714     }\r
2715     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2716         backColor = appData.fontBackColorBlack;\r
2717         foreColor = appData.fontForeColorBlack;\r
2718     }\r
2719 \r
2720     /* Mask */\r
2721     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2722 \r
2723     hbm_old = SelectObject( hdc, hbm );\r
2724 \r
2725     rc.left = 0;\r
2726     rc.top = 0;\r
2727     rc.right = squareSize;\r
2728     rc.bottom = squareSize;\r
2729 \r
2730     /* Step 1: background is now black */\r
2731     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2732 \r
2733     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2734 \r
2735     pt.x = (squareSize - sz.cx) / 2;\r
2736     pt.y = (squareSize - sz.cy) / 2;\r
2737 \r
2738     SetBkMode( hdc, TRANSPARENT );\r
2739     SetTextColor( hdc, chroma );\r
2740     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2741     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2742 \r
2743     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2744     /* Step 3: the area outside the piece is filled with white */\r
2745 //    FloodFill( hdc, 0, 0, chroma );\r
2746     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2747     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2748     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2749     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2750     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2751     /* \r
2752         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2753         but if the start point is not inside the piece we're lost!\r
2754         There should be a better way to do this... if we could create a region or path\r
2755         from the fill operation we would be fine for example.\r
2756     */\r
2757 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2758     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2759 \r
2760     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2761         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2762         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2763 \r
2764         SelectObject( dc2, bm2 );\r
2765         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2766         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2767         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2768         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2769         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2770 \r
2771         DeleteDC( dc2 );\r
2772         DeleteObject( bm2 );\r
2773     }\r
2774 \r
2775     SetTextColor( hdc, 0 );\r
2776     /* \r
2777         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2778         draw the piece again in black for safety.\r
2779     */\r
2780     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2781 \r
2782     SelectObject( hdc, hbm_old );\r
2783 \r
2784     if( hPieceMask[index] != NULL ) {\r
2785         DeleteObject( hPieceMask[index] );\r
2786     }\r
2787 \r
2788     hPieceMask[index] = hbm;\r
2789 \r
2790     /* Face */\r
2791     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2792 \r
2793     SelectObject( hdc, hbm );\r
2794 \r
2795     {\r
2796         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2797         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2798         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2799 \r
2800         SelectObject( dc1, hPieceMask[index] );\r
2801         SelectObject( dc2, bm2 );\r
2802         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2803         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2804         \r
2805         /* \r
2806             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2807             the piece background and deletes (makes transparent) the rest.\r
2808             Thanks to that mask, we are free to paint the background with the greates\r
2809             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2810             We use this, to make gradients and give the pieces a "roundish" look.\r
2811         */\r
2812         SetPieceBackground( hdc, backColor, 2 );\r
2813         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2814 \r
2815         DeleteDC( dc2 );\r
2816         DeleteDC( dc1 );\r
2817         DeleteObject( bm2 );\r
2818     }\r
2819 \r
2820     SetTextColor( hdc, foreColor );\r
2821     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2822 \r
2823     SelectObject( hdc, hbm_old );\r
2824 \r
2825     if( hPieceFace[index] != NULL ) {\r
2826         DeleteObject( hPieceFace[index] );\r
2827     }\r
2828 \r
2829     hPieceFace[index] = hbm;\r
2830 }\r
2831 \r
2832 static int TranslatePieceToFontPiece( int piece )\r
2833 {\r
2834     switch( piece ) {\r
2835     case BlackPawn:\r
2836         return PM_BP;\r
2837     case BlackKnight:\r
2838         return PM_BN;\r
2839     case BlackBishop:\r
2840         return PM_BB;\r
2841     case BlackRook:\r
2842         return PM_BR;\r
2843     case BlackQueen:\r
2844         return PM_BQ;\r
2845     case BlackKing:\r
2846         return PM_BK;\r
2847     case WhitePawn:\r
2848         return PM_WP;\r
2849     case WhiteKnight:\r
2850         return PM_WN;\r
2851     case WhiteBishop:\r
2852         return PM_WB;\r
2853     case WhiteRook:\r
2854         return PM_WR;\r
2855     case WhiteQueen:\r
2856         return PM_WQ;\r
2857     case WhiteKing:\r
2858         return PM_WK;\r
2859 \r
2860     case BlackAngel:\r
2861         return PM_BA;\r
2862     case BlackMarshall:\r
2863         return PM_BC;\r
2864     case BlackFerz:\r
2865         return PM_BF;\r
2866     case BlackNightrider:\r
2867         return PM_BH;\r
2868     case BlackAlfil:\r
2869         return PM_BE;\r
2870     case BlackWazir:\r
2871         return PM_BW;\r
2872     case BlackUnicorn:\r
2873         return PM_BU;\r
2874     case BlackCannon:\r
2875         return PM_BO;\r
2876     case BlackGrasshopper:\r
2877         return PM_BG;\r
2878     case BlackMan:\r
2879         return PM_BM;\r
2880     case BlackSilver:\r
2881         return PM_BSG;\r
2882     case BlackLance:\r
2883         return PM_BL;\r
2884     case BlackFalcon:\r
2885         return PM_BV;\r
2886     case BlackCobra:\r
2887         return PM_BS;\r
2888     case BlackCardinal:\r
2889         return PM_BAB;\r
2890     case BlackDragon:\r
2891         return PM_BD;\r
2892 \r
2893     case WhiteAngel:\r
2894         return PM_WA;\r
2895     case WhiteMarshall:\r
2896         return PM_WC;\r
2897     case WhiteFerz:\r
2898         return PM_WF;\r
2899     case WhiteNightrider:\r
2900         return PM_WH;\r
2901     case WhiteAlfil:\r
2902         return PM_WE;\r
2903     case WhiteWazir:\r
2904         return PM_WW;\r
2905     case WhiteUnicorn:\r
2906         return PM_WU;\r
2907     case WhiteCannon:\r
2908         return PM_WO;\r
2909     case WhiteGrasshopper:\r
2910         return PM_WG;\r
2911     case WhiteMan:\r
2912         return PM_WM;\r
2913     case WhiteSilver:\r
2914         return PM_WSG;\r
2915     case WhiteLance:\r
2916         return PM_WL;\r
2917     case WhiteFalcon:\r
2918         return PM_WV;\r
2919     case WhiteCobra:\r
2920         return PM_WS;\r
2921     case WhiteCardinal:\r
2922         return PM_WAB;\r
2923     case WhiteDragon:\r
2924         return PM_WD;\r
2925     }\r
2926 \r
2927     return 0;\r
2928 }\r
2929 \r
2930 void CreatePiecesFromFont()\r
2931 {\r
2932     LOGFONT lf;\r
2933     HDC hdc_window = NULL;\r
2934     HDC hdc = NULL;\r
2935     HFONT hfont_old;\r
2936     int fontHeight;\r
2937     int i;\r
2938 \r
2939     if( fontBitmapSquareSize < 0 ) {\r
2940         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2941         return;\r
2942     }\r
2943 \r
2944     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2945         fontBitmapSquareSize = -1;\r
2946         return;\r
2947     }\r
2948 \r
2949     if( fontBitmapSquareSize != squareSize ) {\r
2950         hdc_window = GetDC( hwndMain );\r
2951         hdc = CreateCompatibleDC( hdc_window );\r
2952 \r
2953         if( hPieceFont != NULL ) {\r
2954             DeleteObject( hPieceFont );\r
2955         }\r
2956         else {\r
2957             for( i=0; i<=(int)BlackKing; i++ ) {\r
2958                 hPieceMask[i] = NULL;\r
2959                 hPieceFace[i] = NULL;\r
2960             }\r
2961         }\r
2962 \r
2963         fontHeight = 75;\r
2964 \r
2965         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2966             fontHeight = appData.fontPieceSize;\r
2967         }\r
2968 \r
2969         fontHeight = (fontHeight * squareSize) / 100;\r
2970 \r
2971         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2972         lf.lfWidth = 0;\r
2973         lf.lfEscapement = 0;\r
2974         lf.lfOrientation = 0;\r
2975         lf.lfWeight = FW_NORMAL;\r
2976         lf.lfItalic = 0;\r
2977         lf.lfUnderline = 0;\r
2978         lf.lfStrikeOut = 0;\r
2979         lf.lfCharSet = DEFAULT_CHARSET;\r
2980         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2981         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2982         lf.lfQuality = PROOF_QUALITY;\r
2983         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2984         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2985         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2986 \r
2987         hPieceFont = CreateFontIndirect( &lf );\r
2988 \r
2989         if( hPieceFont == NULL ) {\r
2990             fontBitmapSquareSize = -2;\r
2991         }\r
2992         else {\r
2993             /* Setup font-to-piece character table */\r
2994             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2995                 /* No (or wrong) global settings, try to detect the font */\r
2996                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2997                     /* Alpha */\r
2998                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2999                 }\r
3000                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3001                     /* DiagramTT* family */\r
3002                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3003                 }\r
3004                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3005                     /* Fairy symbols */\r
3006                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3007                 }\r
3008                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3009                     /* Good Companion (Some characters get warped as literal :-( */\r
3010                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3011                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3012                     SetCharTable(pieceToFontChar, s);\r
3013                 }\r
3014                 else {\r
3015                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3016                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3017                 }\r
3018             }\r
3019 \r
3020             /* Create bitmaps */\r
3021             hfont_old = SelectObject( hdc, hPieceFont );\r
3022 #if 0\r
3023             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
3024             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
3025             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
3026             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
3027             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
3028             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
3029             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
3030             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
3031             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
3032             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
3033             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
3034             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
3035 \r
3036             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
3037             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
3038             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
3039             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
3040             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
3041             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
3042             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
3043             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
3044             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
3045             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
3046             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
3047             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
3048             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
3049             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
3050             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
3051             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
3052             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
3053             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
3054             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
3055             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
3056             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
3057             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
3058             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
3059             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
3060             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
3061             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
3062             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
3063             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
3064             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
3065             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
3066             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
3067             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
3068 #else\r
3069             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3070                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3071                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3072 #endif\r
3073             SelectObject( hdc, hfont_old );\r
3074 \r
3075             fontBitmapSquareSize = squareSize;\r
3076         }\r
3077     }\r
3078 \r
3079     if( hdc != NULL ) {\r
3080         DeleteDC( hdc );\r
3081     }\r
3082 \r
3083     if( hdc_window != NULL ) {\r
3084         ReleaseDC( hwndMain, hdc_window );\r
3085     }\r
3086 }\r
3087 \r
3088 HBITMAP\r
3089 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3090 {\r
3091   char name[128];\r
3092 \r
3093   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3094   if (gameInfo.event &&\r
3095       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3096       strcmp(name, "k80s") == 0) {\r
3097     strcpy(name, "tim");\r
3098   }\r
3099   return LoadBitmap(hinst, name);\r
3100 }\r
3101 \r
3102 \r
3103 /* Insert a color into the program's logical palette\r
3104    structure.  This code assumes the given color is\r
3105    the result of the RGB or PALETTERGB macro, and it\r
3106    knows how those macros work (which is documented).\r
3107 */\r
3108 VOID\r
3109 InsertInPalette(COLORREF color)\r
3110 {\r
3111   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3112 \r
3113   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3114     DisplayFatalError("Too many colors", 0, 1);\r
3115     pLogPal->palNumEntries--;\r
3116     return;\r
3117   }\r
3118 \r
3119   pe->peFlags = (char) 0;\r
3120   pe->peRed = (char) (0xFF & color);\r
3121   pe->peGreen = (char) (0xFF & (color >> 8));\r
3122   pe->peBlue = (char) (0xFF & (color >> 16));\r
3123   return;\r
3124 }\r
3125 \r
3126 \r
3127 VOID\r
3128 InitDrawingColors()\r
3129 {\r
3130   if (pLogPal == NULL) {\r
3131     /* Allocate enough memory for a logical palette with\r
3132      * PALETTESIZE entries and set the size and version fields\r
3133      * of the logical palette structure.\r
3134      */\r
3135     pLogPal = (NPLOGPALETTE)\r
3136       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3137                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3138     pLogPal->palVersion    = 0x300;\r
3139   }\r
3140   pLogPal->palNumEntries = 0;\r
3141 \r
3142   InsertInPalette(lightSquareColor);\r
3143   InsertInPalette(darkSquareColor);\r
3144   InsertInPalette(whitePieceColor);\r
3145   InsertInPalette(blackPieceColor);\r
3146   InsertInPalette(highlightSquareColor);\r
3147   InsertInPalette(premoveHighlightColor);\r
3148 \r
3149   /*  create a logical color palette according the information\r
3150    *  in the LOGPALETTE structure.\r
3151    */\r
3152   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3153 \r
3154   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3155   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3156   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3157   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3158   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3159   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3160   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3161   /* [AS] Force rendering of the font-based pieces */\r
3162   if( fontBitmapSquareSize > 0 ) {\r
3163     fontBitmapSquareSize = 0;\r
3164   }\r
3165 }\r
3166 \r
3167 \r
3168 int\r
3169 BoardWidth(int boardSize, int n)\r
3170 { /* [HGM] argument n added to allow different width and height */\r
3171   int lineGap = sizeInfo[boardSize].lineGap;\r
3172 \r
3173   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3174       lineGap = appData.overrideLineGap;\r
3175   }\r
3176 \r
3177   return (n + 1) * lineGap +\r
3178           n * sizeInfo[boardSize].squareSize;\r
3179 }\r
3180 \r
3181 /* Respond to board resize by dragging edge */\r
3182 VOID\r
3183 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3184 {\r
3185   BoardSize newSize = NUM_SIZES - 1;\r
3186   static int recurse = 0;\r
3187   if (IsIconic(hwndMain)) return;\r
3188   if (recurse > 0) return;\r
3189   recurse++;\r
3190   while (newSize > 0) {\r
3191         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3192         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3193            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3194     newSize--;\r
3195   } \r
3196   boardSize = newSize;\r
3197   InitDrawingSizes(boardSize, flags);\r
3198   recurse--;\r
3199 }\r
3200 \r
3201 \r
3202 \r
3203 VOID\r
3204 InitDrawingSizes(BoardSize boardSize, int flags)\r
3205 {\r
3206   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3207   ChessSquare piece;\r
3208   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3209   HDC hdc;\r
3210   SIZE clockSize, messageSize;\r
3211   HFONT oldFont;\r
3212   char buf[MSG_SIZ];\r
3213   char *str;\r
3214   HMENU hmenu = GetMenu(hwndMain);\r
3215   RECT crect, wrect, oldRect;\r
3216   int offby;\r
3217   LOGBRUSH logbrush;\r
3218 \r
3219   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3220   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3221 \r
3222   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3223   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3224 \r
3225   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3226   oldRect.top = boardY;\r
3227   oldRect.right = boardX + winWidth;\r
3228   oldRect.bottom = boardY + winHeight;\r
3229 \r
3230   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3231   smallLayout = sizeInfo[boardSize].smallLayout;\r
3232   squareSize = sizeInfo[boardSize].squareSize;\r
3233   lineGap = sizeInfo[boardSize].lineGap;\r
3234   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3235 \r
3236   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3237       lineGap = appData.overrideLineGap;\r
3238   }\r
3239 \r
3240   if (tinyLayout != oldTinyLayout) {\r
3241     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3242     if (tinyLayout) {\r
3243       style &= ~WS_SYSMENU;\r
3244       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3245                  "&Minimize\tCtrl+F4");\r
3246     } else {\r
3247       style |= WS_SYSMENU;\r
3248       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3249     }\r
3250     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3251 \r
3252     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3253       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3254         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3255     }\r
3256     DrawMenuBar(hwndMain);\r
3257   }\r
3258 \r
3259   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3260   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3261 \r
3262   /* Get text area sizes */\r
3263   hdc = GetDC(hwndMain);\r
3264   if (appData.clockMode) {\r
3265     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3266   } else {\r
3267     sprintf(buf, "White");\r
3268   }\r
3269   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3270   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3271   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3272   str = "We only care about the height here";\r
3273   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3274   SelectObject(hdc, oldFont);\r
3275   ReleaseDC(hwndMain, hdc);\r
3276 \r
3277   /* Compute where everything goes */\r
3278   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3279         /* [HGM] logo: if either logo is on, reserve space for it */\r
3280         logoHeight =  2*clockSize.cy;\r
3281         leftLogoRect.left   = OUTER_MARGIN;\r
3282         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3283         leftLogoRect.top    = OUTER_MARGIN;\r
3284         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3285 \r
3286         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3287         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3288         rightLogoRect.top    = OUTER_MARGIN;\r
3289         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3290 \r
3291 \r
3292     whiteRect.left = leftLogoRect.right;\r
3293     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3294     whiteRect.top = OUTER_MARGIN;\r
3295     whiteRect.bottom = whiteRect.top + logoHeight;\r
3296 \r
3297     blackRect.right = rightLogoRect.left;\r
3298     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3299     blackRect.top = whiteRect.top;\r
3300     blackRect.bottom = whiteRect.bottom;\r
3301   } else {\r
3302     whiteRect.left = OUTER_MARGIN;\r
3303     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3304     whiteRect.top = OUTER_MARGIN;\r
3305     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3306 \r
3307     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3308     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3309     blackRect.top = whiteRect.top;\r
3310     blackRect.bottom = whiteRect.bottom;\r
3311   }\r
3312 \r
3313   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3314   if (appData.showButtonBar) {\r
3315     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3316       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3317   } else {\r
3318     messageRect.right = OUTER_MARGIN + boardWidth;\r
3319   }\r
3320   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3321   messageRect.bottom = messageRect.top + messageSize.cy;\r
3322 \r
3323   boardRect.left = OUTER_MARGIN;\r
3324   boardRect.right = boardRect.left + boardWidth;\r
3325   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3326   boardRect.bottom = boardRect.top + boardHeight;\r
3327 \r
3328   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3329   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3330   oldBoardSize = boardSize;\r
3331   oldTinyLayout = tinyLayout;\r
3332   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3333   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3334     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3335   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3336   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3337   winHeight = winH; //       without disturbing window attachments\r
3338   GetWindowRect(hwndMain, &wrect);\r
3339   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3340                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3341 \r
3342   // [HGM] placement: let attached windows follow size change.\r
3343   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3344   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3345   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3346   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3347   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3348 \r
3349   /* compensate if menu bar wrapped */\r
3350   GetClientRect(hwndMain, &crect);\r
3351   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3352   winHeight += offby;\r
3353   switch (flags) {\r
3354   case WMSZ_TOPLEFT:\r
3355     SetWindowPos(hwndMain, NULL, \r
3356                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3357                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3358     break;\r
3359 \r
3360   case WMSZ_TOPRIGHT:\r
3361   case WMSZ_TOP:\r
3362     SetWindowPos(hwndMain, NULL, \r
3363                  wrect.left, wrect.bottom - winHeight, \r
3364                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3365     break;\r
3366 \r
3367   case WMSZ_BOTTOMLEFT:\r
3368   case WMSZ_LEFT:\r
3369     SetWindowPos(hwndMain, NULL, \r
3370                  wrect.right - winWidth, wrect.top, \r
3371                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3372     break;\r
3373 \r
3374   case WMSZ_BOTTOMRIGHT:\r
3375   case WMSZ_BOTTOM:\r
3376   case WMSZ_RIGHT:\r
3377   default:\r
3378     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3379                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3380     break;\r
3381   }\r
3382 \r
3383   hwndPause = NULL;\r
3384   for (i = 0; i < N_BUTTONS; i++) {\r
3385     if (buttonDesc[i].hwnd != NULL) {\r
3386       DestroyWindow(buttonDesc[i].hwnd);\r
3387       buttonDesc[i].hwnd = NULL;\r
3388     }\r
3389     if (appData.showButtonBar) {\r
3390       buttonDesc[i].hwnd =\r
3391         CreateWindow("BUTTON", buttonDesc[i].label,\r
3392                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3393                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3394                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3395                      (HMENU) buttonDesc[i].id,\r
3396                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3397       if (tinyLayout) {\r
3398         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3399                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3400                     MAKELPARAM(FALSE, 0));\r
3401       }\r
3402       if (buttonDesc[i].id == IDM_Pause)\r
3403         hwndPause = buttonDesc[i].hwnd;\r
3404       buttonDesc[i].wndproc = (WNDPROC)\r
3405         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3406     }\r
3407   }\r
3408   if (gridPen != NULL) DeleteObject(gridPen);\r
3409   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3410   if (premovePen != NULL) DeleteObject(premovePen);\r
3411   if (lineGap != 0) {\r
3412     logbrush.lbStyle = BS_SOLID;\r
3413     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3414     gridPen =\r
3415       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3416                    lineGap, &logbrush, 0, NULL);\r
3417     logbrush.lbColor = highlightSquareColor;\r
3418     highlightPen =\r
3419       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3420                    lineGap, &logbrush, 0, NULL);\r
3421 \r
3422     logbrush.lbColor = premoveHighlightColor; \r
3423     premovePen =\r
3424       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3425                    lineGap, &logbrush, 0, NULL);\r
3426 \r
3427     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3428     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3429       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3430       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3431         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3432       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3433         BOARD_WIDTH * (squareSize + lineGap);\r
3434       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3435     }\r
3436     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3437       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3438       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3439         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3440         lineGap / 2 + (i * (squareSize + lineGap));\r
3441       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3442         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3443       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3444     }\r
3445   }\r
3446 \r
3447   /* [HGM] Licensing requirement */\r
3448 #ifdef GOTHIC\r
3449   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3450 #endif\r
3451 #ifdef FALCON\r
3452   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3453 #endif\r
3454   GothicPopUp( "", VariantNormal);\r
3455 \r
3456 \r
3457 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3458 \r
3459   /* Load piece bitmaps for this board size */\r
3460   for (i=0; i<=2; i++) {\r
3461     for (piece = WhitePawn;\r
3462          (int) piece < (int) BlackPawn;\r
3463          piece = (ChessSquare) ((int) piece + 1)) {\r
3464       if (pieceBitmap[i][piece] != NULL)\r
3465         DeleteObject(pieceBitmap[i][piece]);\r
3466     }\r
3467   }\r
3468 \r
3469   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3470   // Orthodox Chess pieces\r
3471   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3472   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3473   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3474   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3475   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3476   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3477   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3478   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3479   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3480   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3481   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3482   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3483   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3484   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3485   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3486   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3487     // in Shogi, Hijack the unused Queen for Lance\r
3488     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3489     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3490     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3491   } else {\r
3492     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3493     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3494     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3495   }\r
3496 \r
3497   if(squareSize <= 72 && squareSize >= 33) { \r
3498     /* A & C are available in most sizes now */\r
3499     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3500       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3501       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3502       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3503       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3504       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3505       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3506       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3507       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3508       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3509       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3510       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3511       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3512     } else { // Smirf-like\r
3513       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3514       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3515       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3516     }\r
3517     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3518       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3519       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3520       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3521     } else { // WinBoard standard\r
3522       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3523       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3524       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3525     }\r
3526   }\r
3527 \r
3528 \r
3529   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3530     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3531     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3532     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3533     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3534     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3535     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3536     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3537     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3538     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3539     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3540     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3541     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3542     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3543     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3544     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3545     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3546     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3547     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3548     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3549     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3550     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3551     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3552     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3553     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3554     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3555     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3556     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3557     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3558     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3559     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3560 \r
3561     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3562       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3563       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3564       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3565       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3566       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3567       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3568       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3569       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3570       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3571       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3572       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3573       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3574     } else {\r
3575       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3576       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3577       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3578       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3579       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3580       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3581       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3582       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3583       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3584       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3585       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3586       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3587     }\r
3588 \r
3589   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3590     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3591     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3592     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3593     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3594     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3595     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3596     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3597     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3598     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3599     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3600     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3601     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3602     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3603     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3604   }\r
3605 \r
3606 \r
3607   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3608   /* special Shogi support in this size */\r
3609   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3610       for (piece = WhitePawn;\r
3611            (int) piece < (int) BlackPawn;\r
3612            piece = (ChessSquare) ((int) piece + 1)) {\r
3613         if (pieceBitmap[i][piece] != NULL)\r
3614           DeleteObject(pieceBitmap[i][piece]);\r
3615       }\r
3616     }\r
3617   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3618   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3619   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3620   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3621   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3622   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3623   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3624   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3625   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3626   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3627   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3628   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3629   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3630   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3631   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3632   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3633   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3634   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3635   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3636   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3637   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3638   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3639   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3640   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3641   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3642   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3643   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3644   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3645   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3646   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3647   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3648   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3649   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3650   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3651   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3652   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3653   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3654   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3655   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3656   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3657   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3658   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3659   minorSize = 0;\r
3660   }\r
3661 }\r
3662 \r
3663 HBITMAP\r
3664 PieceBitmap(ChessSquare p, int kind)\r
3665 {\r
3666   if ((int) p >= (int) BlackPawn)\r
3667     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3668 \r
3669   return pieceBitmap[kind][(int) p];\r
3670 }\r
3671 \r
3672 /***************************************************************/\r
3673 \r
3674 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3675 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3676 /*\r
3677 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3678 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3679 */\r
3680 \r
3681 VOID\r
3682 SquareToPos(int row, int column, int * x, int * y)\r
3683 {\r
3684   if (flipView) {\r
3685     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3686     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3687   } else {\r
3688     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3689     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3690   }\r
3691 }\r
3692 \r
3693 VOID\r
3694 DrawCoordsOnDC(HDC hdc)\r
3695 {\r
3696   static char files[24] = {'0', '1','2','3','4','5','6','7','8','9','0','1','1','0','9','8','7','6','5','4','3','2','1','0'};\r
3697   static char ranks[24] = {'l', 'k','j','i','h','g','f','e','d','c','b','a','a','b','c','d','e','f','g','h','i','j','k','l'};\r
3698   char str[2] = { NULLCHAR, NULLCHAR };\r
3699   int oldMode, oldAlign, x, y, start, i;\r
3700   HFONT oldFont;\r
3701   HBRUSH oldBrush;\r
3702 \r
3703   if (!appData.showCoords)\r
3704     return;\r
3705 \r
3706   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3707 \r
3708   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3709   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3710   oldAlign = GetTextAlign(hdc);\r
3711   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3712 \r
3713   y = boardRect.top + lineGap;\r
3714   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3715 \r
3716   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3717   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3718     str[0] = files[start + i];\r
3719     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3720     y += squareSize + lineGap;\r
3721   }\r
3722 \r
3723   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3724 \r
3725   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3726   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3727     str[0] = ranks[start + i];\r
3728     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3729     x += squareSize + lineGap;\r
3730   }    \r
3731 \r
3732   SelectObject(hdc, oldBrush);\r
3733   SetBkMode(hdc, oldMode);\r
3734   SetTextAlign(hdc, oldAlign);\r
3735   SelectObject(hdc, oldFont);\r
3736 }\r
3737 \r
3738 VOID\r
3739 DrawGridOnDC(HDC hdc)\r
3740 {\r
3741   HPEN oldPen;\r
3742  \r
3743   if (lineGap != 0) {\r
3744     oldPen = SelectObject(hdc, gridPen);\r
3745     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3746     SelectObject(hdc, oldPen);\r
3747   }\r
3748 }\r
3749 \r
3750 #define HIGHLIGHT_PEN 0\r
3751 #define PREMOVE_PEN   1\r
3752 \r
3753 VOID\r
3754 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3755 {\r
3756   int x1, y1;\r
3757   HPEN oldPen, hPen;\r
3758   if (lineGap == 0) return;\r
3759   if (flipView) {\r
3760     x1 = boardRect.left +\r
3761       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3762     y1 = boardRect.top +\r
3763       lineGap/2 + y * (squareSize + lineGap);\r
3764   } else {\r
3765     x1 = boardRect.left +\r
3766       lineGap/2 + x * (squareSize + lineGap);\r
3767     y1 = boardRect.top +\r
3768       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3769   }\r
3770   hPen = pen ? premovePen : highlightPen;\r
3771   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3772   MoveToEx(hdc, x1, y1, NULL);\r
3773   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3774   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3775   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3776   LineTo(hdc, x1, y1);\r
3777   SelectObject(hdc, oldPen);\r
3778 }\r
3779 \r
3780 VOID\r
3781 DrawHighlightsOnDC(HDC hdc)\r
3782 {\r
3783   int i;\r
3784   for (i=0; i<2; i++) {\r
3785     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3786       DrawHighlightOnDC(hdc, TRUE,\r
3787                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3788                         HIGHLIGHT_PEN);\r
3789   }\r
3790   for (i=0; i<2; i++) {\r
3791     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3792         premoveHighlightInfo.sq[i].y >= 0) {\r
3793         DrawHighlightOnDC(hdc, TRUE,\r
3794                           premoveHighlightInfo.sq[i].x, \r
3795                           premoveHighlightInfo.sq[i].y,\r
3796                           PREMOVE_PEN);\r
3797     }\r
3798   }\r
3799 }\r
3800 \r
3801 /* Note: sqcolor is used only in monoMode */\r
3802 /* Note that this code is largely duplicated in woptions.c,\r
3803    function DrawSampleSquare, so that needs to be updated too */\r
3804 VOID\r
3805 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3806 {\r
3807   HBITMAP oldBitmap;\r
3808   HBRUSH oldBrush;\r
3809   int tmpSize;\r
3810 \r
3811   if (appData.blindfold) return;\r
3812 \r
3813   /* [AS] Use font-based pieces if needed */\r
3814   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3815     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3816     CreatePiecesFromFont();\r
3817 \r
3818     if( fontBitmapSquareSize == squareSize ) {\r
3819         int index = TranslatePieceToFontPiece(piece);\r
3820 \r
3821         SelectObject( tmphdc, hPieceMask[ index ] );\r
3822 \r
3823         BitBlt( hdc,\r
3824             x, y,\r
3825             squareSize, squareSize,\r
3826             tmphdc,\r
3827             0, 0,\r
3828             SRCAND );\r
3829 \r
3830         SelectObject( tmphdc, hPieceFace[ index ] );\r
3831 \r
3832         BitBlt( hdc,\r
3833             x, y,\r
3834             squareSize, squareSize,\r
3835             tmphdc,\r
3836             0, 0,\r
3837             SRCPAINT );\r
3838 \r
3839         return;\r
3840     }\r
3841   }\r
3842 \r
3843   if (appData.monoMode) {\r
3844     SelectObject(tmphdc, PieceBitmap(piece, \r
3845       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3846     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3847            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3848   } else {\r
3849     tmpSize = squareSize;\r
3850     if(minorSize &&\r
3851         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3852          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3853       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3854       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3855       x += (squareSize - minorSize)>>1;\r
3856       y += squareSize - minorSize - 2;\r
3857       tmpSize = minorSize;\r
3858     }\r
3859     if (color || appData.allWhite ) {\r
3860       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3861       if( color )\r
3862               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3863       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3864       if(appData.upsideDown && color==flipView)\r
3865         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3866       else\r
3867         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3868 #if 0\r
3869       /* Use black piece color for outline of white pieces */\r
3870       /* Not sure this looks really good (though xboard does it).\r
3871          Maybe better to have another selectable color, default black */\r
3872       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3873       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3874       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3875 #else\r
3876       /* Use black for outline of white pieces */\r
3877       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3878       if(appData.upsideDown && color==flipView)\r
3879         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3880       else\r
3881         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3882 #endif\r
3883     } else {\r
3884 #if 0\r
3885       /* Use white piece color for details of black pieces */\r
3886       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3887          WHITE_PIECE ones aren't always the right shape. */\r
3888       /* Not sure this looks really good (though xboard does it).\r
3889          Maybe better to have another selectable color, default medium gray? */\r
3890       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3891       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3892       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3893       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3894       SelectObject(hdc, blackPieceBrush);\r
3895       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3896 #else\r
3897       /* Use square color for details of black pieces */\r
3898       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3899       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3900       if(appData.upsideDown && !flipView)\r
3901         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3902       else\r
3903         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3904 #endif\r
3905     }\r
3906     SelectObject(hdc, oldBrush);\r
3907     SelectObject(tmphdc, oldBitmap);\r
3908   }\r
3909 }\r
3910 \r
3911 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3912 int GetBackTextureMode( int algo )\r
3913 {\r
3914     int result = BACK_TEXTURE_MODE_DISABLED;\r
3915 \r
3916     switch( algo ) \r
3917     {\r
3918         case BACK_TEXTURE_MODE_PLAIN:\r
3919             result = 1; /* Always use identity map */\r
3920             break;\r
3921         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3922             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3923             break;\r
3924     }\r
3925 \r
3926     return result;\r
3927 }\r
3928 \r
3929 /* \r
3930     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3931     to handle redraws cleanly (as random numbers would always be different).\r
3932 */\r
3933 VOID RebuildTextureSquareInfo()\r
3934 {\r
3935     BITMAP bi;\r
3936     int lite_w = 0;\r
3937     int lite_h = 0;\r
3938     int dark_w = 0;\r
3939     int dark_h = 0;\r
3940     int row;\r
3941     int col;\r
3942 \r
3943     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3944 \r
3945     if( liteBackTexture != NULL ) {\r
3946         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3947             lite_w = bi.bmWidth;\r
3948             lite_h = bi.bmHeight;\r
3949         }\r
3950     }\r
3951 \r
3952     if( darkBackTexture != NULL ) {\r
3953         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3954             dark_w = bi.bmWidth;\r
3955             dark_h = bi.bmHeight;\r
3956         }\r
3957     }\r
3958 \r
3959     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3960         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3961             if( (col + row) & 1 ) {\r
3962                 /* Lite square */\r
3963                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3964                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3965                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3966                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3967                 }\r
3968             }\r
3969             else {\r
3970                 /* Dark square */\r
3971                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3972                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3973                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3974                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3975                 }\r
3976             }\r
3977         }\r
3978     }\r
3979 }\r
3980 \r
3981 /* [AS] Arrow highlighting support */\r
3982 \r
3983 static int A_WIDTH = 5; /* Width of arrow body */\r
3984 \r
3985 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3986 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3987 \r
3988 static double Sqr( double x )\r
3989 {\r
3990     return x*x;\r
3991 }\r
3992 \r
3993 static int Round( double x )\r
3994 {\r
3995     return (int) (x + 0.5);\r
3996 }\r
3997 \r
3998 /* Draw an arrow between two points using current settings */\r
3999 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
4000 {\r
4001     POINT arrow[7];\r
4002     double dx, dy, j, k, x, y;\r
4003 \r
4004     if( d_x == s_x ) {\r
4005         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4006 \r
4007         arrow[0].x = s_x + A_WIDTH;\r
4008         arrow[0].y = s_y;\r
4009 \r
4010         arrow[1].x = s_x + A_WIDTH;\r
4011         arrow[1].y = d_y - h;\r
4012 \r
4013         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
4014         arrow[2].y = d_y - h;\r
4015 \r
4016         arrow[3].x = d_x;\r
4017         arrow[3].y = d_y;\r
4018 \r
4019         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
4020         arrow[4].y = d_y - h;\r
4021 \r
4022         arrow[5].x = s_x - A_WIDTH;\r
4023         arrow[5].y = d_y - h;\r
4024 \r
4025         arrow[6].x = s_x - A_WIDTH;\r
4026         arrow[6].y = s_y;\r
4027     }\r
4028     else if( d_y == s_y ) {\r
4029         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4030 \r
4031         arrow[0].x = s_x;\r
4032         arrow[0].y = s_y + A_WIDTH;\r
4033 \r
4034         arrow[1].x = d_x - w;\r
4035         arrow[1].y = s_y + A_WIDTH;\r
4036 \r
4037         arrow[2].x = d_x - w;\r
4038         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
4039 \r
4040         arrow[3].x = d_x;\r
4041         arrow[3].y = d_y;\r
4042 \r
4043         arrow[4].x = d_x - w;\r
4044         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
4045 \r
4046         arrow[5].x = d_x - w;\r
4047         arrow[5].y = s_y - A_WIDTH;\r
4048 \r
4049         arrow[6].x = s_x;\r
4050         arrow[6].y = s_y - A_WIDTH;\r
4051     }\r
4052     else {\r
4053         /* [AS] Needed a lot of paper for this! :-) */\r
4054         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4055         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4056   \r
4057         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4058 \r
4059         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4060 \r
4061         x = s_x;\r
4062         y = s_y;\r
4063 \r
4064         arrow[0].x = Round(x - j);\r
4065         arrow[0].y = Round(y + j*dx);\r
4066 \r
4067         arrow[1].x = Round(x + j);\r
4068         arrow[1].y = Round(y - j*dx);\r
4069 \r
4070         if( d_x > s_x ) {\r
4071             x = (double) d_x - k;\r
4072             y = (double) d_y - k*dy;\r
4073         }\r
4074         else {\r
4075             x = (double) d_x + k;\r
4076             y = (double) d_y + k*dy;\r
4077         }\r
4078 \r
4079         arrow[2].x = Round(x + j);\r
4080         arrow[2].y = Round(y - j*dx);\r
4081 \r
4082         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4083         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4084 \r
4085         arrow[4].x = d_x;\r
4086         arrow[4].y = d_y;\r
4087 \r
4088         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4089         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4090 \r
4091         arrow[6].x = Round(x - j);\r
4092         arrow[6].y = Round(y + j*dx);\r
4093     }\r
4094 \r
4095     Polygon( hdc, arrow, 7 );\r
4096 }\r
4097 \r
4098 /* [AS] Draw an arrow between two squares */\r
4099 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4100 {\r
4101     int s_x, s_y, d_x, d_y;\r
4102     HPEN hpen;\r
4103     HPEN holdpen;\r
4104     HBRUSH hbrush;\r
4105     HBRUSH holdbrush;\r
4106     LOGBRUSH stLB;\r
4107 \r
4108     if( s_col == d_col && s_row == d_row ) {\r
4109         return;\r
4110     }\r
4111 \r
4112     /* Get source and destination points */\r
4113     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4114     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4115 \r
4116     if( d_y > s_y ) {\r
4117         d_y += squareSize / 4;\r
4118     }\r
4119     else if( d_y < s_y ) {\r
4120         d_y += 3 * squareSize / 4;\r
4121     }\r
4122     else {\r
4123         d_y += squareSize / 2;\r
4124     }\r
4125 \r
4126     if( d_x > s_x ) {\r
4127         d_x += squareSize / 4;\r
4128     }\r
4129     else if( d_x < s_x ) {\r
4130         d_x += 3 * squareSize / 4;\r
4131     }\r
4132     else {\r
4133         d_x += squareSize / 2;\r
4134     }\r
4135 \r
4136     s_x += squareSize / 2;\r
4137     s_y += squareSize / 2;\r
4138 \r
4139     /* Adjust width */\r
4140     A_WIDTH = squareSize / 14;\r
4141 \r
4142     /* Draw */\r
4143     stLB.lbStyle = BS_SOLID;\r
4144     stLB.lbColor = appData.highlightArrowColor;\r
4145     stLB.lbHatch = 0;\r
4146 \r
4147     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4148     holdpen = SelectObject( hdc, hpen );\r
4149     hbrush = CreateBrushIndirect( &stLB );\r
4150     holdbrush = SelectObject( hdc, hbrush );\r
4151 \r
4152     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4153 \r
4154     SelectObject( hdc, holdpen );\r
4155     SelectObject( hdc, holdbrush );\r
4156     DeleteObject( hpen );\r
4157     DeleteObject( hbrush );\r
4158 }\r
4159 \r
4160 BOOL HasHighlightInfo()\r
4161 {\r
4162     BOOL result = FALSE;\r
4163 \r
4164     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4165         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4166     {\r
4167         result = TRUE;\r
4168     }\r
4169 \r
4170     return result;\r
4171 }\r
4172 \r
4173 BOOL IsDrawArrowEnabled()\r
4174 {\r
4175     BOOL result = FALSE;\r
4176 \r
4177     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4178         result = TRUE;\r
4179     }\r
4180 \r
4181     return result;\r
4182 }\r
4183 \r
4184 VOID DrawArrowHighlight( HDC hdc )\r
4185 {\r
4186     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4187         DrawArrowBetweenSquares( hdc,\r
4188             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4189             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4190     }\r
4191 }\r
4192 \r
4193 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4194 {\r
4195     HRGN result = NULL;\r
4196 \r
4197     if( HasHighlightInfo() ) {\r
4198         int x1, y1, x2, y2;\r
4199         int sx, sy, dx, dy;\r
4200 \r
4201         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4202         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4203 \r
4204         sx = MIN( x1, x2 );\r
4205         sy = MIN( y1, y2 );\r
4206         dx = MAX( x1, x2 ) + squareSize;\r
4207         dy = MAX( y1, y2 ) + squareSize;\r
4208 \r
4209         result = CreateRectRgn( sx, sy, dx, dy );\r
4210     }\r
4211 \r
4212     return result;\r
4213 }\r
4214 \r
4215 /*\r
4216     Warning: this function modifies the behavior of several other functions. \r
4217     \r
4218     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4219     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4220     repaint is scattered all over the place, which is not good for features such as\r
4221     "arrow highlighting" that require a full repaint of the board.\r
4222 \r
4223     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4224     user interaction, when speed is not so important) but especially to avoid errors\r
4225     in the displayed graphics.\r
4226 \r
4227     In such patched places, I always try refer to this function so there is a single\r
4228     place to maintain knowledge.\r
4229     \r
4230     To restore the original behavior, just return FALSE unconditionally.\r
4231 */\r
4232 BOOL IsFullRepaintPreferrable()\r
4233 {\r
4234     BOOL result = FALSE;\r
4235 \r
4236     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4237         /* Arrow may appear on the board */\r
4238         result = TRUE;\r
4239     }\r
4240 \r
4241     return result;\r
4242 }\r
4243 \r
4244 /* \r
4245     This function is called by DrawPosition to know whether a full repaint must\r
4246     be forced or not.\r
4247 \r
4248     Only DrawPosition may directly call this function, which makes use of \r
4249     some state information. Other function should call DrawPosition specifying \r
4250     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4251 */\r
4252 BOOL DrawPositionNeedsFullRepaint()\r
4253 {\r
4254     BOOL result = FALSE;\r
4255 \r
4256     /* \r
4257         Probably a slightly better policy would be to trigger a full repaint\r
4258         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4259         but animation is fast enough that it's difficult to notice.\r
4260     */\r
4261     if( animInfo.piece == EmptySquare ) {\r
4262         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4263             result = TRUE;\r
4264         }\r
4265     }\r
4266 \r
4267     return result;\r
4268 }\r
4269 \r
4270 VOID\r
4271 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4272 {\r
4273   int row, column, x, y, square_color, piece_color;\r
4274   ChessSquare piece;\r
4275   HBRUSH oldBrush;\r
4276   HDC texture_hdc = NULL;\r
4277 \r
4278   /* [AS] Initialize background textures if needed */\r
4279   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4280       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4281       if( backTextureSquareSize != squareSize \r
4282        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4283           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4284           backTextureSquareSize = squareSize;\r
4285           RebuildTextureSquareInfo();\r
4286       }\r
4287 \r
4288       texture_hdc = CreateCompatibleDC( hdc );\r
4289   }\r
4290 \r
4291   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4292     for (column = 0; column < BOARD_WIDTH; column++) {\r
4293   \r
4294       SquareToPos(row, column, &x, &y);\r
4295 \r
4296       piece = board[row][column];\r
4297 \r
4298       square_color = ((column + row) % 2) == 1;\r
4299       if( gameInfo.variant == VariantXiangqi ) {\r
4300           square_color = !InPalace(row, column);\r
4301           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4302           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4303       }\r
4304       piece_color = (int) piece < (int) BlackPawn;\r
4305 \r
4306 \r
4307       /* [HGM] holdings file: light square or black */\r
4308       if(column == BOARD_LEFT-2) {\r
4309             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4310                 square_color = 1;\r
4311             else {\r
4312                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4313                 continue;\r
4314             }\r
4315       } else\r
4316       if(column == BOARD_RGHT + 1 ) {\r
4317             if( row < gameInfo.holdingsSize )\r
4318                 square_color = 1;\r
4319             else {\r
4320                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4321                 continue;\r
4322             }\r
4323       }\r
4324       if(column == BOARD_LEFT-1 ) /* left align */\r
4325             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4326       else if( column == BOARD_RGHT) /* right align */\r
4327             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4328       else\r
4329       if (appData.monoMode) {\r
4330         if (piece == EmptySquare) {\r
4331           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4332                  square_color ? WHITENESS : BLACKNESS);\r
4333         } else {\r
4334           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4335         }\r
4336       } \r
4337       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4338           /* [AS] Draw the square using a texture bitmap */\r
4339           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4340           int r = row, c = column; // [HGM] do not flip board in flipView\r
4341           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4342 \r
4343           DrawTile( x, y, \r
4344               squareSize, squareSize, \r
4345               hdc, \r
4346               texture_hdc,\r
4347               backTextureSquareInfo[r][c].mode,\r
4348               backTextureSquareInfo[r][c].x,\r
4349               backTextureSquareInfo[r][c].y );\r
4350 \r
4351           SelectObject( texture_hdc, hbm );\r
4352 \r
4353           if (piece != EmptySquare) {\r
4354               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4355           }\r
4356       }\r
4357       else {\r
4358         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4359 \r
4360         oldBrush = SelectObject(hdc, brush );\r
4361         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4362         SelectObject(hdc, oldBrush);\r
4363         if (piece != EmptySquare)\r
4364           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4365       }\r
4366     }\r
4367   }\r
4368 \r
4369   if( texture_hdc != NULL ) {\r
4370     DeleteDC( texture_hdc );\r
4371   }\r
4372 }\r
4373 \r
4374 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4375 void fputDW(FILE *f, int x)\r
4376 {\r
4377         fputc(x     & 255, f);\r
4378         fputc(x>>8  & 255, f);\r
4379         fputc(x>>16 & 255, f);\r
4380         fputc(x>>24 & 255, f);\r
4381 }\r
4382 \r
4383 #define MAX_CLIPS 200   /* more than enough */\r
4384 \r
4385 VOID\r
4386 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4387 {\r
4388 //  HBITMAP bufferBitmap;\r
4389   BITMAP bi;\r
4390 //  RECT Rect;\r
4391   HDC tmphdc;\r
4392   HBITMAP hbm;\r
4393   int w = 100, h = 50;\r
4394 \r
4395   if(logo == NULL) return;\r
4396 //  GetClientRect(hwndMain, &Rect);\r
4397 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4398 //                                      Rect.bottom-Rect.top+1);\r
4399   tmphdc = CreateCompatibleDC(hdc);\r
4400   hbm = SelectObject(tmphdc, logo);\r
4401   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4402             w = bi.bmWidth;\r
4403             h = bi.bmHeight;\r
4404   }\r
4405   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4406                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4407   SelectObject(tmphdc, hbm);\r
4408   DeleteDC(tmphdc);\r
4409 }\r
4410 \r
4411 VOID\r
4412 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4413 {\r
4414   static Board lastReq, lastDrawn;\r
4415   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4416   static int lastDrawnFlipView = 0;\r
4417   static int lastReqValid = 0, lastDrawnValid = 0;\r
4418   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4419   HDC tmphdc;\r
4420   HDC hdcmem;\r
4421   HBITMAP bufferBitmap;\r
4422   HBITMAP oldBitmap;\r
4423   RECT Rect;\r
4424   HRGN clips[MAX_CLIPS];\r
4425   ChessSquare dragged_piece = EmptySquare;\r
4426 \r
4427   /* I'm undecided on this - this function figures out whether a full\r
4428    * repaint is necessary on its own, so there's no real reason to have the\r
4429    * caller tell it that.  I think this can safely be set to FALSE - but\r
4430    * if we trust the callers not to request full repaints unnessesarily, then\r
4431    * we could skip some clipping work.  In other words, only request a full\r
4432    * redraw when the majority of pieces have changed positions (ie. flip, \r
4433    * gamestart and similar)  --Hawk\r
4434    */\r
4435   Boolean fullrepaint = repaint;\r
4436 \r
4437   if( DrawPositionNeedsFullRepaint() ) {\r
4438       fullrepaint = TRUE;\r
4439   }\r
4440 \r
4441 #if 0\r
4442   if( fullrepaint ) {\r
4443       static int repaint_count = 0;\r
4444       char buf[128];\r
4445 \r
4446       repaint_count++;\r
4447       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4448       OutputDebugString( buf );\r
4449   }\r
4450 #endif\r
4451 \r
4452   if (board == NULL) {\r
4453     if (!lastReqValid) {\r
4454       return;\r
4455     }\r
4456     board = lastReq;\r
4457   } else {\r
4458     CopyBoard(lastReq, board);\r
4459     lastReqValid = 1;\r
4460   }\r
4461 \r
4462   if (doingSizing) {\r
4463     return;\r
4464   }\r
4465 \r
4466   if (IsIconic(hwndMain)) {\r
4467     return;\r
4468   }\r
4469 \r
4470   if (hdc == NULL) {\r
4471     hdc = GetDC(hwndMain);\r
4472     if (!appData.monoMode) {\r
4473       SelectPalette(hdc, hPal, FALSE);\r
4474       RealizePalette(hdc);\r
4475     }\r
4476     releaseDC = TRUE;\r
4477   } else {\r
4478     releaseDC = FALSE;\r
4479   }\r
4480 \r
4481 #if 0\r
4482   fprintf(debugFP, "*******************************\n"\r
4483                    "repaint = %s\n"\r
4484                    "dragInfo.from (%d,%d)\n"\r
4485                    "dragInfo.start (%d,%d)\n"\r
4486                    "dragInfo.pos (%d,%d)\n"\r
4487                    "dragInfo.lastpos (%d,%d)\n", \r
4488                     repaint ? "TRUE" : "FALSE",\r
4489                     dragInfo.from.x, dragInfo.from.y, \r
4490                     dragInfo.start.x, dragInfo.start.y,\r
4491                     dragInfo.pos.x, dragInfo.pos.y,\r
4492                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4493   fprintf(debugFP, "prev:  ");\r
4494   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4495     for (column = 0; column < BOARD_WIDTH; column++) {\r
4496       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4497     }\r
4498   }\r
4499   fprintf(debugFP, "\n");\r
4500   fprintf(debugFP, "board: ");\r
4501   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4502     for (column = 0; column < BOARD_WIDTH; column++) {\r
4503       fprintf(debugFP, "%d ", board[row][column]);\r
4504     }\r
4505   }\r
4506   fprintf(debugFP, "\n");\r
4507   fflush(debugFP);\r
4508 #endif\r
4509 \r
4510   /* Create some work-DCs */\r
4511   hdcmem = CreateCompatibleDC(hdc);\r
4512   tmphdc = CreateCompatibleDC(hdc);\r
4513 \r
4514   /* If dragging is in progress, we temporarely remove the piece */\r
4515   /* [HGM] or temporarily decrease count if stacked              */\r
4516   /*       !! Moved to before board compare !!                   */\r
4517   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4518     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4519     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4520             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4521         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4522     } else \r
4523     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4524             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4525         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4526     } else \r
4527         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4528   }\r
4529 \r
4530   /* Figure out which squares need updating by comparing the \r
4531    * newest board with the last drawn board and checking if\r
4532    * flipping has changed.\r
4533    */\r
4534   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4535     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4536       for (column = 0; column < BOARD_WIDTH; column++) {\r
4537         if (lastDrawn[row][column] != board[row][column]) {\r
4538           SquareToPos(row, column, &x, &y);\r
4539           clips[num_clips++] =\r
4540             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4541         }\r
4542       }\r
4543     }\r
4544     for (i=0; i<2; i++) {\r
4545       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4546           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4547         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4548             lastDrawnHighlight.sq[i].y >= 0) {\r
4549           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4550                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4551           clips[num_clips++] =\r
4552             CreateRectRgn(x - lineGap, y - lineGap, \r
4553                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4554         }\r
4555         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4556           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4557           clips[num_clips++] =\r
4558             CreateRectRgn(x - lineGap, y - lineGap, \r
4559                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4560         }\r
4561       }\r
4562     }\r
4563     for (i=0; i<2; i++) {\r
4564       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4565           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4566         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4567             lastDrawnPremove.sq[i].y >= 0) {\r
4568           SquareToPos(lastDrawnPremove.sq[i].y,\r
4569                       lastDrawnPremove.sq[i].x, &x, &y);\r
4570           clips[num_clips++] =\r
4571             CreateRectRgn(x - lineGap, y - lineGap, \r
4572                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4573         }\r
4574         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4575             premoveHighlightInfo.sq[i].y >= 0) {\r
4576           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4577                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4578           clips[num_clips++] =\r
4579             CreateRectRgn(x - lineGap, y - lineGap, \r
4580                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4581         }\r
4582       }\r
4583     }\r
4584   } else {\r
4585     fullrepaint = TRUE;\r
4586   }\r
4587 \r
4588   /* Create a buffer bitmap - this is the actual bitmap\r
4589    * being written to.  When all the work is done, we can\r
4590    * copy it to the real DC (the screen).  This avoids\r
4591    * the problems with flickering.\r
4592    */\r
4593   GetClientRect(hwndMain, &Rect);\r
4594   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4595                                         Rect.bottom-Rect.top+1);\r
4596   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4597   if (!appData.monoMode) {\r
4598     SelectPalette(hdcmem, hPal, FALSE);\r
4599   }\r
4600 \r
4601   /* Create clips for dragging */\r
4602   if (!fullrepaint) {\r
4603     if (dragInfo.from.x >= 0) {\r
4604       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4605       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4606     }\r
4607     if (dragInfo.start.x >= 0) {\r
4608       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4609       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4610     }\r
4611     if (dragInfo.pos.x >= 0) {\r
4612       x = dragInfo.pos.x - squareSize / 2;\r
4613       y = dragInfo.pos.y - squareSize / 2;\r
4614       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4615     }\r
4616     if (dragInfo.lastpos.x >= 0) {\r
4617       x = dragInfo.lastpos.x - squareSize / 2;\r
4618       y = dragInfo.lastpos.y - squareSize / 2;\r
4619       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4620     }\r
4621   }\r
4622 \r
4623   /* Are we animating a move?  \r
4624    * If so, \r
4625    *   - remove the piece from the board (temporarely)\r
4626    *   - calculate the clipping region\r
4627    */\r
4628   if (!fullrepaint) {\r
4629     if (animInfo.piece != EmptySquare) {\r
4630       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4631       x = boardRect.left + animInfo.lastpos.x;\r
4632       y = boardRect.top + animInfo.lastpos.y;\r
4633       x2 = boardRect.left + animInfo.pos.x;\r
4634       y2 = boardRect.top + animInfo.pos.y;\r
4635       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4636       /* Slight kludge.  The real problem is that after AnimateMove is\r
4637          done, the position on the screen does not match lastDrawn.\r
4638          This currently causes trouble only on e.p. captures in\r
4639          atomic, where the piece moves to an empty square and then\r
4640          explodes.  The old and new positions both had an empty square\r
4641          at the destination, but animation has drawn a piece there and\r
4642          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4643       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4644     }\r
4645   }\r
4646 \r
4647   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4648   if (num_clips == 0)\r
4649     fullrepaint = TRUE;\r
4650 \r
4651   /* Set clipping on the memory DC */\r
4652   if (!fullrepaint) {\r
4653     SelectClipRgn(hdcmem, clips[0]);\r
4654     for (x = 1; x < num_clips; x++) {\r
4655       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4656         abort();  // this should never ever happen!\r
4657     }\r
4658   }\r
4659 \r
4660   /* Do all the drawing to the memory DC */\r
4661   if(explodeInfo.radius) { // [HGM] atomic\r
4662         HBRUSH oldBrush;\r
4663         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4664         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4665         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4666         x += squareSize/2;\r
4667         y += squareSize/2;\r
4668         if(!fullrepaint) {\r
4669           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4670           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4671         }\r
4672         DrawGridOnDC(hdcmem);\r
4673         DrawHighlightsOnDC(hdcmem);\r
4674         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4675         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4676         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4677         SelectObject(hdcmem, oldBrush);\r
4678   } else {\r
4679     DrawGridOnDC(hdcmem);\r
4680     DrawHighlightsOnDC(hdcmem);\r
4681     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4682   }\r
4683   if(logoHeight) {\r
4684         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4685         if(appData.autoLogo) {\r
4686           \r
4687           switch(gameMode) { // pick logos based on game mode\r
4688             case IcsObserving:\r
4689                 whiteLogo = second.programLogo; // ICS logo\r
4690                 blackLogo = second.programLogo;\r
4691             default:\r
4692                 break;\r
4693             case IcsPlayingWhite:\r
4694                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4695                 blackLogo = second.programLogo; // ICS logo\r
4696                 break;\r
4697             case IcsPlayingBlack:\r
4698                 whiteLogo = second.programLogo; // ICS logo\r
4699                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4700                 break;\r
4701             case TwoMachinesPlay:\r
4702                 if(first.twoMachinesColor[0] == 'b') {\r
4703                     whiteLogo = second.programLogo;\r
4704                     blackLogo = first.programLogo;\r
4705                 }\r
4706                 break;\r
4707             case MachinePlaysWhite:\r
4708                 blackLogo = userLogo;\r
4709                 break;\r
4710             case MachinePlaysBlack:\r
4711                 whiteLogo = userLogo;\r
4712                 blackLogo = first.programLogo;\r
4713           }\r
4714         }\r
4715         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4716         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4717   }\r
4718 \r
4719   if( appData.highlightMoveWithArrow ) {\r
4720     DrawArrowHighlight(hdcmem);\r
4721   }\r
4722 \r
4723   DrawCoordsOnDC(hdcmem);\r
4724 \r
4725   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4726                  /* to make sure lastDrawn contains what is actually drawn */\r
4727 \r
4728   /* Put the dragged piece back into place and draw it (out of place!) */\r
4729     if (dragged_piece != EmptySquare) {\r
4730     /* [HGM] or restack */\r
4731     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4732                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4733     else\r
4734     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4735                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4736     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4737     x = dragInfo.pos.x - squareSize / 2;\r
4738     y = dragInfo.pos.y - squareSize / 2;\r
4739     DrawPieceOnDC(hdcmem, dragged_piece,\r
4740                   ((int) dragged_piece < (int) BlackPawn), \r
4741                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4742   }   \r
4743   \r
4744   /* Put the animated piece back into place and draw it */\r
4745   if (animInfo.piece != EmptySquare) {\r
4746     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4747     x = boardRect.left + animInfo.pos.x;\r
4748     y = boardRect.top + animInfo.pos.y;\r
4749     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4750                   ((int) animInfo.piece < (int) BlackPawn),\r
4751                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4752   }\r
4753 \r
4754   /* Release the bufferBitmap by selecting in the old bitmap \r
4755    * and delete the memory DC\r
4756    */\r
4757   SelectObject(hdcmem, oldBitmap);\r
4758   DeleteDC(hdcmem);\r
4759 \r
4760   /* Set clipping on the target DC */\r
4761   if (!fullrepaint) {\r
4762     SelectClipRgn(hdc, clips[0]);\r
4763     for (x = 1; x < num_clips; x++) {\r
4764       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4765         abort();   // this should never ever happen!\r
4766     } \r
4767   }\r
4768 \r
4769   /* Copy the new bitmap onto the screen in one go.\r
4770    * This way we avoid any flickering\r
4771    */\r
4772   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4773   BitBlt(hdc, boardRect.left, boardRect.top,\r
4774          boardRect.right - boardRect.left,\r
4775          boardRect.bottom - boardRect.top,\r
4776          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4777   if(saveDiagFlag) { \r
4778     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4779     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4780 \r
4781     GetObject(bufferBitmap, sizeof(b), &b);\r
4782     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4783         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4784         bih.biWidth = b.bmWidth;\r
4785         bih.biHeight = b.bmHeight;\r
4786         bih.biPlanes = 1;\r
4787         bih.biBitCount = b.bmBitsPixel;\r
4788         bih.biCompression = 0;\r
4789         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4790         bih.biXPelsPerMeter = 0;\r
4791         bih.biYPelsPerMeter = 0;\r
4792         bih.biClrUsed = 0;\r
4793         bih.biClrImportant = 0;\r
4794 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4795 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4796         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4797 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4798 \r
4799 #if 1\r
4800         wb = b.bmWidthBytes;\r
4801         // count colors\r
4802         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4803                 int k = ((int*) pData)[i];\r
4804                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4805                 if(j >= 16) break;\r
4806                 color[j] = k;\r
4807                 if(j >= nrColors) nrColors = j+1;\r
4808         }\r
4809         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4810                 INT p = 0;\r
4811                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4812                     for(w=0; w<(wb>>2); w+=2) {\r
4813                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4814                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4815                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4816                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4817                         pData[p++] = m | j<<4;\r
4818                     }\r
4819                     while(p&3) pData[p++] = 0;\r
4820                 }\r
4821                 fac = 3;\r
4822                 wb = ((wb+31)>>5)<<2;\r
4823         }\r
4824         // write BITMAPFILEHEADER\r
4825         fprintf(diagFile, "BM");\r
4826         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4827         fputDW(diagFile, 0);\r
4828         fputDW(diagFile, 0x36 + (fac?64:0));\r
4829         // write BITMAPINFOHEADER\r
4830         fputDW(diagFile, 40);\r
4831         fputDW(diagFile, b.bmWidth);\r
4832         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4833         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4834         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4835         fputDW(diagFile, 0);\r
4836         fputDW(diagFile, 0);\r
4837         fputDW(diagFile, 0);\r
4838         fputDW(diagFile, 0);\r
4839         fputDW(diagFile, 0);\r
4840         fputDW(diagFile, 0);\r
4841         // write color table\r
4842         if(fac)\r
4843         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4844         // write bitmap data\r
4845         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4846                 fputc(pData[i], diagFile);\r
4847 #endif\r
4848      }\r
4849   }\r
4850 \r
4851   SelectObject(tmphdc, oldBitmap);\r
4852 \r
4853   /* Massive cleanup */\r
4854   for (x = 0; x < num_clips; x++)\r
4855     DeleteObject(clips[x]);\r
4856 \r
4857   DeleteDC(tmphdc);\r
4858   DeleteObject(bufferBitmap);\r
4859 \r
4860   if (releaseDC) \r
4861     ReleaseDC(hwndMain, hdc);\r
4862   \r
4863   if (lastDrawnFlipView != flipView) {\r
4864     if (flipView)\r
4865       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4866     else\r
4867       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4868   }\r
4869 \r
4870 /*  CopyBoard(lastDrawn, board);*/\r
4871   lastDrawnHighlight = highlightInfo;\r
4872   lastDrawnPremove   = premoveHighlightInfo;\r
4873   lastDrawnFlipView = flipView;\r
4874   lastDrawnValid = 1;\r
4875 }\r
4876 \r
4877 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4878 int\r
4879 SaveDiagram(f)\r
4880      FILE *f;\r
4881 {\r
4882     saveDiagFlag = 1; diagFile = f;\r
4883     HDCDrawPosition(NULL, TRUE, NULL);\r
4884 \r
4885     saveDiagFlag = 0;\r
4886 \r
4887 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4888     \r
4889     fclose(f);\r
4890     return TRUE;\r
4891 }\r
4892 \r
4893 \r
4894 /*---------------------------------------------------------------------------*\\r
4895 | CLIENT PAINT PROCEDURE\r
4896 |   This is the main event-handler for the WM_PAINT message.\r
4897 |\r
4898 \*---------------------------------------------------------------------------*/\r
4899 VOID\r
4900 PaintProc(HWND hwnd)\r
4901 {\r
4902   HDC         hdc;\r
4903   PAINTSTRUCT ps;\r
4904   HFONT       oldFont;\r
4905 \r
4906   if((hdc = BeginPaint(hwnd, &ps))) {\r
4907     if (IsIconic(hwnd)) {\r
4908       DrawIcon(hdc, 2, 2, iconCurrent);\r
4909     } else {\r
4910       if (!appData.monoMode) {\r
4911         SelectPalette(hdc, hPal, FALSE);\r
4912         RealizePalette(hdc);\r
4913       }\r
4914       HDCDrawPosition(hdc, 1, NULL);\r
4915       oldFont =\r
4916         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4917       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4918                  ETO_CLIPPED|ETO_OPAQUE,\r
4919                  &messageRect, messageText, strlen(messageText), NULL);\r
4920       SelectObject(hdc, oldFont);\r
4921       DisplayBothClocks();\r
4922     }\r
4923     EndPaint(hwnd,&ps);\r
4924   }\r
4925 \r
4926   return;\r
4927 }\r
4928 \r
4929 \r
4930 /*\r
4931  * If the user selects on a border boundary, return -1; if off the board,\r
4932  *   return -2.  Otherwise map the event coordinate to the square.\r
4933  * The offset boardRect.left or boardRect.top must already have been\r
4934  *   subtracted from x.\r
4935  */\r
4936 int\r
4937 EventToSquare(int x)\r
4938 {\r
4939   if (x <= 0)\r
4940     return -2;\r
4941   if (x < lineGap)\r
4942     return -1;\r
4943   x -= lineGap;\r
4944   if ((x % (squareSize + lineGap)) >= squareSize)\r
4945     return -1;\r
4946   x /= (squareSize + lineGap);\r
4947   if (x >= BOARD_SIZE)\r
4948     return -2;\r
4949   return x;\r
4950 }\r
4951 \r
4952 typedef struct {\r
4953   char piece;\r
4954   int command;\r
4955   char* name;\r
4956 } DropEnable;\r
4957 \r
4958 DropEnable dropEnables[] = {\r
4959   { 'P', DP_Pawn, "Pawn" },\r
4960   { 'N', DP_Knight, "Knight" },\r
4961   { 'B', DP_Bishop, "Bishop" },\r
4962   { 'R', DP_Rook, "Rook" },\r
4963   { 'Q', DP_Queen, "Queen" },\r
4964 };\r
4965 \r
4966 VOID\r
4967 SetupDropMenu(HMENU hmenu)\r
4968 {\r
4969   int i, count, enable;\r
4970   char *p;\r
4971   extern char white_holding[], black_holding[];\r
4972   char item[MSG_SIZ];\r
4973 \r
4974   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4975     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4976                dropEnables[i].piece);\r
4977     count = 0;\r
4978     while (p && *p++ == dropEnables[i].piece) count++;\r
4979     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4980     enable = count > 0 || !appData.testLegality\r
4981       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4982                       && !appData.icsActive);\r
4983     ModifyMenu(hmenu, dropEnables[i].command,\r
4984                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4985                dropEnables[i].command, item);\r
4986   }\r
4987 }\r
4988 \r
4989 /* Event handler for mouse messages */\r
4990 VOID\r
4991 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4992 {\r
4993   int x, y;\r
4994   POINT pt;\r
4995   static int recursive = 0;\r
4996   HMENU hmenu;\r
4997 //  BOOLEAN needsRedraw = FALSE;\r
4998   BOOLEAN saveAnimate;\r
4999   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
5000   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
5001   ChessMove moveType;\r
5002 \r
5003   if (recursive) {\r
5004     if (message == WM_MBUTTONUP) {\r
5005       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
5006          to the middle button: we simulate pressing the left button too!\r
5007          */\r
5008       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
5009       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
5010     }\r
5011     return;\r
5012   }\r
5013   recursive++;\r
5014   \r
5015   pt.x = LOWORD(lParam);\r
5016   pt.y = HIWORD(lParam);\r
5017   x = EventToSquare(pt.x - boardRect.left);\r
5018   y = EventToSquare(pt.y - boardRect.top);\r
5019   if (!flipView && y >= 0) {\r
5020     y = BOARD_HEIGHT - 1 - y;\r
5021   }\r
5022   if (flipView && x >= 0) {\r
5023     x = BOARD_WIDTH - 1 - x;\r
5024   }\r
5025 \r
5026   switch (message) {\r
5027   case WM_LBUTTONDOWN:\r
5028     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
5029         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
5030         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
5031         if(gameInfo.holdingsWidth && \r
5032                 (WhiteOnMove(currentMove) \r
5033                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
5034                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
5035             // click in right holdings, for determining promotion piece\r
5036             ChessSquare p = boards[currentMove][y][x];\r
5037             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
5038             if(p != EmptySquare) {\r
5039                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
5040                 fromX = fromY = -1;\r
5041                 break;\r
5042             }\r
5043         }\r
5044         DrawPosition(FALSE, boards[currentMove]);\r
5045         break;\r
5046     }\r
5047     ErrorPopDown();\r
5048     sameAgain = FALSE;\r
5049     if (y == -2) {\r
5050       /* Downclick vertically off board; check if on clock */\r
5051       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5052         if (gameMode == EditPosition) {\r
5053           SetWhiteToPlayEvent();\r
5054         } else if (gameMode == IcsPlayingBlack ||\r
5055                    gameMode == MachinePlaysWhite) {\r
5056           CallFlagEvent();\r
5057         } else if (gameMode == EditGame) {\r
5058           AdjustClock(flipClock, -1);\r
5059         }\r
5060       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5061         if (gameMode == EditPosition) {\r
5062           SetBlackToPlayEvent();\r
5063         } else if (gameMode == IcsPlayingWhite ||\r
5064                    gameMode == MachinePlaysBlack) {\r
5065           CallFlagEvent();\r
5066         } else if (gameMode == EditGame) {\r
5067           AdjustClock(!flipClock, -1);\r
5068         }\r
5069       }\r
5070       if (!appData.highlightLastMove) {\r
5071         ClearHighlights();\r
5072         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
5073       }\r
5074       fromX = fromY = -1;\r
5075       dragInfo.start.x = dragInfo.start.y = -1;\r
5076       dragInfo.from = dragInfo.start;\r
5077       break;\r
5078     } else if (x < 0 || y < 0\r
5079       /* [HGM] block clicks between board and holdings */\r
5080               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
5081               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
5082               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
5083         /* EditPosition, empty square, or different color piece;\r
5084            click-click move is possible */\r
5085                                ) {\r
5086       break;\r
5087     } else if (fromX == x && fromY == y) {\r
5088       /* Downclick on same square again */\r
5089       ClearHighlights();\r
5090       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5091       sameAgain = TRUE;  \r
5092     } else if (fromX != -1 &&\r
5093                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5094                                                                         ) {\r
5095       /* Downclick on different square. */\r
5096       /* [HGM] if on holdings file, should count as new first click ! */\r
5097       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5098         toX = x;\r
5099         toY = y;\r
5100         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5101            to make sure move is legal before showing promotion popup */\r
5102         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5103         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5104                 fromX = fromY = -1; \r
5105                 ClearHighlights();\r
5106                 DrawPosition(FALSE, boards[currentMove]);\r
5107                 break; \r
5108         } else \r
5109         if(moveType != ImpossibleMove) {\r
5110           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5111           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5112             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5113               appData.alwaysPromoteToQueen)) {\r
5114                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5115                   if (!appData.highlightLastMove) {\r
5116                       ClearHighlights();\r
5117                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5118                   }\r
5119           } else\r
5120           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5121                   SetHighlights(fromX, fromY, toX, toY);\r
5122                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5123                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5124                      If promotion to Q is legal, all are legal! */\r
5125                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5126                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5127                     // kludge to temporarily execute move on display, wthout promotng yet\r
5128                     promotionChoice = TRUE;\r
5129                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5130                     boards[currentMove][toY][toX] = p;\r
5131                     DrawPosition(FALSE, boards[currentMove]);\r
5132                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5133                     boards[currentMove][toY][toX] = q;\r
5134                   } else\r
5135                   PromotionPopup(hwnd);\r
5136           } else {       /* not a promotion */\r
5137              if (appData.animate || appData.highlightLastMove) {\r
5138                  SetHighlights(fromX, fromY, toX, toY);\r
5139              } else {\r
5140                  ClearHighlights();\r
5141              }\r
5142              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5143              fromX = fromY = -1;\r
5144              if (appData.animate && !appData.highlightLastMove) {\r
5145                   ClearHighlights();\r
5146                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5147              }\r
5148           }\r
5149           break;\r
5150         }\r
5151         if (gotPremove) {\r
5152             /* [HGM] it seemed that braces were missing here */\r
5153             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5154             fromX = fromY = -1;\r
5155             break;\r
5156         }\r
5157       }\r
5158       ClearHighlights();\r
5159       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5160     }\r
5161     /* First downclick, or restart on a square with same color piece */\r
5162     if (!frozen && OKToStartUserMove(x, y)) {\r
5163       fromX = x;\r
5164       fromY = y;\r
5165       dragInfo.lastpos = pt;\r
5166       dragInfo.from.x = fromX;\r
5167       dragInfo.from.y = fromY;\r
5168       dragInfo.start = dragInfo.from;\r
5169       SetCapture(hwndMain);\r
5170     } else {\r
5171       fromX = fromY = -1;\r
5172       dragInfo.start.x = dragInfo.start.y = -1;\r
5173       dragInfo.from = dragInfo.start;\r
5174       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5175     }\r
5176     break;\r
5177 \r
5178   case WM_LBUTTONUP:\r
5179     ReleaseCapture();\r
5180     if (fromX == -1) break;\r
5181     if (x == fromX && y == fromY) {\r
5182       dragInfo.from.x = dragInfo.from.y = -1;\r
5183       /* Upclick on same square */\r
5184       if (sameAgain) {\r
5185         /* Clicked same square twice: abort click-click move */\r
5186         fromX = fromY = -1;\r
5187         gotPremove = 0;\r
5188         ClearPremoveHighlights();\r
5189       } else {\r
5190         /* First square clicked: start click-click move */\r
5191         SetHighlights(fromX, fromY, -1, -1);\r
5192       }\r
5193       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5194     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5195       /* Errant click; ignore */\r
5196       break;\r
5197     } else {\r
5198       /* Finish drag move. */\r
5199     if (appData.debugMode) {\r
5200         fprintf(debugFP, "release\n");\r
5201     }\r
5202       dragInfo.from.x = dragInfo.from.y = -1;\r
5203       toX = x;\r
5204       toY = y;\r
5205       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5206       appData.animate = appData.animate && !appData.animateDragging;\r
5207       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5208       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5209                 fromX = fromY = -1; \r
5210                 ClearHighlights();\r
5211                 DrawPosition(FALSE, boards[currentMove]);\r
5212                 appData.animate = saveAnimate;\r
5213                 break; \r
5214       } else \r
5215       if(moveType != ImpossibleMove) {\r
5216           /* [HGM] use move type to determine if move is promotion.\r
5217              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5218           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5219             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5220               appData.alwaysPromoteToQueen)) \r
5221                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5222           else \r
5223           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5224                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5225                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5226                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5227                     // kludge to temporarily execute move on display, wthout promotng yet\r
5228                     promotionChoice = TRUE;\r
5229                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5230                     boards[currentMove][toY][toX] = p;\r
5231                     DrawPosition(FALSE, boards[currentMove]);\r
5232                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5233                     boards[currentMove][toY][toX] = q;\r
5234                     appData.animate = saveAnimate;\r
5235                     break;\r
5236                   } else\r
5237                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5238           } else {\r
5239             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5240                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5241                                         moveType == WhiteCapturesEnPassant || \r
5242                                         moveType == BlackCapturesEnPassant   ) )\r
5243                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5244             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5245           }\r
5246       }\r
5247       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5248       appData.animate = saveAnimate;\r
5249       fromX = fromY = -1;\r
5250       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5251         ClearHighlights();\r
5252       }\r
5253       if (appData.animate || appData.animateDragging ||\r
5254           appData.highlightDragging || gotPremove) {\r
5255         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5256       }\r
5257     }\r
5258     dragInfo.start.x = dragInfo.start.y = -1; \r
5259     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5260     break;\r
5261 \r
5262   case WM_MOUSEMOVE:\r
5263     if ((appData.animateDragging || appData.highlightDragging)\r
5264         && (wParam & MK_LBUTTON)\r
5265         && dragInfo.from.x >= 0) \r
5266     {\r
5267       BOOL full_repaint = FALSE;\r
5268 \r
5269       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5270       if (appData.animateDragging) {\r
5271         dragInfo.pos = pt;\r
5272       }\r
5273       if (appData.highlightDragging) {\r
5274         SetHighlights(fromX, fromY, x, y);\r
5275         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5276             full_repaint = TRUE;\r
5277         }\r
5278       }\r
5279       \r
5280       DrawPosition( full_repaint, NULL);\r
5281       \r
5282       dragInfo.lastpos = dragInfo.pos;\r
5283     }\r
5284     break;\r
5285 \r
5286   case WM_MOUSEWHEEL: // [DM]\r
5287     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5288        /* Mouse Wheel is being rolled forward\r
5289         * Play moves forward\r
5290         */\r
5291        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5292                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5293        /* Mouse Wheel is being rolled backward\r
5294         * Play moves backward\r
5295         */\r
5296        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5297                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5298     }\r
5299     break;\r
5300 \r
5301   case WM_MBUTTONDOWN:\r
5302   case WM_RBUTTONDOWN:\r
5303     ErrorPopDown();\r
5304     ReleaseCapture();\r
5305     fromX = fromY = -1;\r
5306     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5307     dragInfo.start.x = dragInfo.start.y = -1;\r
5308     dragInfo.from = dragInfo.start;\r
5309     dragInfo.lastpos = dragInfo.pos;\r
5310     if (appData.highlightDragging) {\r
5311       ClearHighlights();\r
5312     }\r
5313     if(y == -2) {\r
5314       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5315       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5316           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5317       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5318           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5319       }\r
5320     }\r
5321     DrawPosition(TRUE, NULL);\r
5322 \r
5323     switch (gameMode) {\r
5324     case EditPosition:\r
5325     case IcsExamining:\r
5326       if (x < 0 || y < 0) break;\r
5327       fromX = x;\r
5328       fromY = y;\r
5329       if (message == WM_MBUTTONDOWN) {\r
5330         buttonCount = 3;  /* even if system didn't think so */\r
5331         if (wParam & MK_SHIFT) \r
5332           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5333         else\r
5334           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5335       } else { /* message == WM_RBUTTONDOWN */\r
5336 #if 0\r
5337         if (buttonCount == 3) {\r
5338           if (wParam & MK_SHIFT) \r
5339             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5340           else\r
5341             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5342         } else {\r
5343           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5344         }\r
5345 #else\r
5346         /* Just have one menu, on the right button.  Windows users don't\r
5347            think to try the middle one, and sometimes other software steals\r
5348            it, or it doesn't really exist. */\r
5349         if(gameInfo.variant != VariantShogi)\r
5350             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5351         else\r
5352             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5353 #endif\r
5354       }\r
5355       break;\r
5356     case IcsPlayingWhite:\r
5357     case IcsPlayingBlack:\r
5358     case EditGame:\r
5359     case MachinePlaysWhite:\r
5360     case MachinePlaysBlack:\r
5361       if (appData.testLegality &&\r
5362           gameInfo.variant != VariantBughouse &&\r
5363           gameInfo.variant != VariantCrazyhouse) break;\r
5364       if (x < 0 || y < 0) break;\r
5365       fromX = x;\r
5366       fromY = y;\r
5367       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5368       SetupDropMenu(hmenu);\r
5369       MenuPopup(hwnd, pt, hmenu, -1);\r
5370       break;\r
5371     default:\r
5372       break;\r
5373     }\r
5374     break;\r
5375   }\r
5376 \r
5377   recursive--;\r
5378 }\r
5379 \r
5380 /* Preprocess messages for buttons in main window */\r
5381 LRESULT CALLBACK\r
5382 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5383 {\r
5384   int id = GetWindowLong(hwnd, GWL_ID);\r
5385   int i, dir;\r
5386 \r
5387   for (i=0; i<N_BUTTONS; i++) {\r
5388     if (buttonDesc[i].id == id) break;\r
5389   }\r
5390   if (i == N_BUTTONS) return 0;\r
5391   switch (message) {\r
5392   case WM_KEYDOWN:\r
5393     switch (wParam) {\r
5394     case VK_LEFT:\r
5395     case VK_RIGHT:\r
5396       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5397       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5398       return TRUE;\r
5399     }\r
5400     break;\r
5401   case WM_CHAR:\r
5402     switch (wParam) {\r
5403     case '\r':\r
5404       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5405       return TRUE;\r
5406     default:\r
5407       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5408         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5409         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5410         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5411         SetFocus(h);\r
5412         SendMessage(h, WM_CHAR, wParam, lParam);\r
5413         return TRUE;\r
5414       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5415         PopUpMoveDialog((char)wParam);\r
5416       }\r
5417       break;\r
5418     }\r
5419     break;\r
5420   }\r
5421   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5422 }\r
5423 \r
5424 /* Process messages for Promotion dialog box */\r
5425 LRESULT CALLBACK\r
5426 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5427 {\r
5428   char promoChar;\r
5429 \r
5430   switch (message) {\r
5431   case WM_INITDIALOG: /* message: initialize dialog box */\r
5432     /* Center the dialog over the application window */\r
5433     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5434     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5435       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5436        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5437                SW_SHOW : SW_HIDE);\r
5438     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5439     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5440        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5441          PieceToChar(WhiteAngel) != '~') ||\r
5442         (PieceToChar(BlackAngel) >= 'A' &&\r
5443          PieceToChar(BlackAngel) != '~')   ) ?\r
5444                SW_SHOW : SW_HIDE);\r
5445     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5446        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5447          PieceToChar(WhiteMarshall) != '~') ||\r
5448         (PieceToChar(BlackMarshall) >= 'A' &&\r
5449          PieceToChar(BlackMarshall) != '~')   ) ?\r
5450                SW_SHOW : SW_HIDE);\r
5451     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5452     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5453        gameInfo.variant != VariantShogi ?\r
5454                SW_SHOW : SW_HIDE);\r
5455     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5456        gameInfo.variant != VariantShogi ?\r
5457                SW_SHOW : SW_HIDE);\r
5458     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5459        gameInfo.variant == VariantShogi ?\r
5460                SW_SHOW : SW_HIDE);\r
5461     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5462        gameInfo.variant == VariantShogi ?\r
5463                SW_SHOW : SW_HIDE);\r
5464     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5465        gameInfo.variant == VariantSuper ?\r
5466                SW_SHOW : SW_HIDE);\r
5467     return TRUE;\r
5468 \r
5469   case WM_COMMAND: /* message: received a command */\r
5470     switch (LOWORD(wParam)) {\r
5471     case IDCANCEL:\r
5472       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5473       ClearHighlights();\r
5474       DrawPosition(FALSE, NULL);\r
5475       return TRUE;\r
5476     case PB_King:\r
5477       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5478       break;\r
5479     case PB_Queen:\r
5480       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5481       break;\r
5482     case PB_Rook:\r
5483       promoChar = PieceToChar(BlackRook);\r
5484       break;\r
5485     case PB_Bishop:\r
5486       promoChar = PieceToChar(BlackBishop);\r
5487       break;\r
5488     case PB_Chancellor:\r
5489       promoChar = PieceToChar(BlackMarshall);\r
5490       break;\r
5491     case PB_Archbishop:\r
5492       promoChar = PieceToChar(BlackAngel);\r
5493       break;\r
5494     case PB_Knight:\r
5495       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5496       break;\r
5497     default:\r
5498       return FALSE;\r
5499     }\r
5500     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5501     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5502        only show the popup when we are already sure the move is valid or\r
5503        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5504        will figure out it is a promotion from the promoChar. */\r
5505     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5506     if (!appData.highlightLastMove) {\r
5507       ClearHighlights();\r
5508       DrawPosition(FALSE, NULL);\r
5509     }\r
5510     return TRUE;\r
5511   }\r
5512   return FALSE;\r
5513 }\r
5514 \r
5515 /* Pop up promotion dialog */\r
5516 VOID\r
5517 PromotionPopup(HWND hwnd)\r
5518 {\r
5519   FARPROC lpProc;\r
5520 \r
5521   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5522   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5523     hwnd, (DLGPROC)lpProc);\r
5524   FreeProcInstance(lpProc);\r
5525 }\r
5526 \r
5527 /* Toggle ShowThinking */\r
5528 VOID\r
5529 ToggleShowThinking()\r
5530 {\r
5531   appData.showThinking = !appData.showThinking;\r
5532   ShowThinkingEvent();\r
5533 }\r
5534 \r
5535 VOID\r
5536 LoadGameDialog(HWND hwnd, char* title)\r
5537 {\r
5538   UINT number = 0;\r
5539   FILE *f;\r
5540   char fileTitle[MSG_SIZ];\r
5541   f = OpenFileDialog(hwnd, "rb", "",\r
5542                      appData.oldSaveStyle ? "gam" : "pgn",\r
5543                      GAME_FILT,\r
5544                      title, &number, fileTitle, NULL);\r
5545   if (f != NULL) {\r
5546     cmailMsgLoaded = FALSE;\r
5547     if (number == 0) {\r
5548       int error = GameListBuild(f);\r
5549       if (error) {\r
5550         DisplayError("Cannot build game list", error);\r
5551       } else if (!ListEmpty(&gameList) &&\r
5552                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5553         GameListPopUp(f, fileTitle);\r
5554         return;\r
5555       }\r
5556       GameListDestroy();\r
5557       number = 1;\r
5558     }\r
5559     LoadGame(f, number, fileTitle, FALSE);\r
5560   }\r
5561 }\r
5562 \r
5563 VOID\r
5564 ChangedConsoleFont()\r
5565 {\r
5566   CHARFORMAT cfmt;\r
5567   CHARRANGE tmpsel, sel;\r
5568   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5569   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5570   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5571   PARAFORMAT paraf;\r
5572 \r
5573   cfmt.cbSize = sizeof(CHARFORMAT);\r
5574   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5575   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5576   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5577    * size.  This was undocumented in the version of MSVC++ that I had\r
5578    * when I wrote the code, but is apparently documented now.\r
5579    */\r
5580   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5581   cfmt.bCharSet = f->lf.lfCharSet;\r
5582   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5583   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5584   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5585   /* Why are the following seemingly needed too? */\r
5586   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5587   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5588   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5589   tmpsel.cpMin = 0;\r
5590   tmpsel.cpMax = -1; /*999999?*/\r
5591   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5592   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5593   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5594    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5595    */\r
5596   paraf.cbSize = sizeof(paraf);\r
5597   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5598   paraf.dxStartIndent = 0;\r
5599   paraf.dxOffset = WRAP_INDENT;\r
5600   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5601   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5602 }\r
5603 \r
5604 /*---------------------------------------------------------------------------*\\r
5605  *\r
5606  * Window Proc for main window\r
5607  *\r
5608 \*---------------------------------------------------------------------------*/\r
5609 \r
5610 /* Process messages for main window, etc. */\r
5611 LRESULT CALLBACK\r
5612 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5613 {\r
5614   FARPROC lpProc;\r
5615   int wmId, wmEvent;\r
5616   char *defName;\r
5617   FILE *f;\r
5618   UINT number;\r
5619   char fileTitle[MSG_SIZ];\r
5620   char buf[MSG_SIZ];\r
5621   static SnapData sd;\r
5622 \r
5623   switch (message) {\r
5624 \r
5625   case WM_PAINT: /* message: repaint portion of window */\r
5626     PaintProc(hwnd);\r
5627     break;\r
5628 \r
5629   case WM_ERASEBKGND:\r
5630     if (IsIconic(hwnd)) {\r
5631       /* Cheat; change the message */\r
5632       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5633     } else {\r
5634       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5635     }\r
5636     break;\r
5637 \r
5638   case WM_LBUTTONDOWN:\r
5639   case WM_MBUTTONDOWN:\r
5640   case WM_RBUTTONDOWN:\r
5641   case WM_LBUTTONUP:\r
5642   case WM_MBUTTONUP:\r
5643   case WM_RBUTTONUP:\r
5644   case WM_MOUSEMOVE:\r
5645   case WM_MOUSEWHEEL:\r
5646     MouseEvent(hwnd, message, wParam, lParam);\r
5647     break;\r
5648 \r
5649   JAWS_KB_NAVIGATION\r
5650 \r
5651   case WM_CHAR:\r
5652     \r
5653     JAWS_ALT_INTERCEPT\r
5654 \r
5655     if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) { \r
5656         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5657         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5658         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5659         SetFocus(h);\r
5660         SendMessage(h, message, wParam, lParam);\r
5661     } else if(lParam != KF_REPEAT) {\r
5662         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5663                 PopUpMoveDialog((char)wParam);\r
5664         } else if((char)wParam == 003) CopyGameToClipboard();\r
5665          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5666     }\r
5667 \r
5668     break;\r
5669 \r
5670   case WM_PALETTECHANGED:\r
5671     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5672       int nnew;\r
5673       HDC hdc = GetDC(hwndMain);\r
5674       SelectPalette(hdc, hPal, TRUE);\r
5675       nnew = RealizePalette(hdc);\r
5676       if (nnew > 0) {\r
5677         paletteChanged = TRUE;\r
5678 #if 0\r
5679         UpdateColors(hdc);\r
5680 #else\r
5681         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5682 #endif\r
5683       }\r
5684       ReleaseDC(hwnd, hdc);\r
5685     }\r
5686     break;\r
5687 \r
5688   case WM_QUERYNEWPALETTE:\r
5689     if (!appData.monoMode /*&& paletteChanged*/) {\r
5690       int nnew;\r
5691       HDC hdc = GetDC(hwndMain);\r
5692       paletteChanged = FALSE;\r
5693       SelectPalette(hdc, hPal, FALSE);\r
5694       nnew = RealizePalette(hdc);\r
5695       if (nnew > 0) {\r
5696         InvalidateRect(hwnd, &boardRect, FALSE);\r
5697       }\r
5698       ReleaseDC(hwnd, hdc);\r
5699       return TRUE;\r
5700     }\r
5701     return FALSE;\r
5702 \r
5703   case WM_COMMAND: /* message: command from application menu */\r
5704     wmId    = LOWORD(wParam);\r
5705     wmEvent = HIWORD(wParam);\r
5706 \r
5707     switch (wmId) {\r
5708     case IDM_NewGame:\r
5709       ResetGameEvent();\r
5710       AnalysisPopDown();\r
5711       SAY("new game enter a move to play against the computer with white");\r
5712       break;\r
5713 \r
5714     case IDM_NewGameFRC:\r
5715       if( NewGameFRC() == 0 ) {\r
5716         ResetGameEvent();\r
5717         AnalysisPopDown();\r
5718       }\r
5719       break;\r
5720 \r
5721     case IDM_NewVariant:\r
5722       NewVariantPopup(hwnd);\r
5723       break;\r
5724 \r
5725     case IDM_LoadGame:\r
5726       LoadGameDialog(hwnd, "Load Game from File");\r
5727       break;\r
5728 \r
5729     case IDM_LoadNextGame:\r
5730       ReloadGame(1);\r
5731       break;\r
5732 \r
5733     case IDM_LoadPrevGame:\r
5734       ReloadGame(-1);\r
5735       break;\r
5736 \r
5737     case IDM_ReloadGame:\r
5738       ReloadGame(0);\r
5739       break;\r
5740 \r
5741     case IDM_LoadPosition:\r
5742       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5743         Reset(FALSE, TRUE);\r
5744       }\r
5745       number = 1;\r
5746       f = OpenFileDialog(hwnd, "rb", "",\r
5747                          appData.oldSaveStyle ? "pos" : "fen",\r
5748                          POSITION_FILT,\r
5749                          "Load Position from File", &number, fileTitle, NULL);\r
5750       if (f != NULL) {\r
5751         LoadPosition(f, number, fileTitle);\r
5752       }\r
5753       break;\r
5754 \r
5755     case IDM_LoadNextPosition:\r
5756       ReloadPosition(1);\r
5757       break;\r
5758 \r
5759     case IDM_LoadPrevPosition:\r
5760       ReloadPosition(-1);\r
5761       break;\r
5762 \r
5763     case IDM_ReloadPosition:\r
5764       ReloadPosition(0);\r
5765       break;\r
5766 \r
5767     case IDM_SaveGame:\r
5768       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5769       f = OpenFileDialog(hwnd, "a", defName,\r
5770                          appData.oldSaveStyle ? "gam" : "pgn",\r
5771                          GAME_FILT,\r
5772                          "Save Game to File", NULL, fileTitle, NULL);\r
5773       if (f != NULL) {\r
5774         SaveGame(f, 0, "");\r
5775       }\r
5776       break;\r
5777 \r
5778     case IDM_SavePosition:\r
5779       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5780       f = OpenFileDialog(hwnd, "a", defName,\r
5781                          appData.oldSaveStyle ? "pos" : "fen",\r
5782                          POSITION_FILT,\r
5783                          "Save Position to File", NULL, fileTitle, NULL);\r
5784       if (f != NULL) {\r
5785         SavePosition(f, 0, "");\r
5786       }\r
5787       break;\r
5788 \r
5789     case IDM_SaveDiagram:\r
5790       defName = "diagram";\r
5791       f = OpenFileDialog(hwnd, "wb", defName,\r
5792                          "bmp",\r
5793                          DIAGRAM_FILT,\r
5794                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5795       if (f != NULL) {\r
5796         SaveDiagram(f);\r
5797       }\r
5798       break;\r
5799 \r
5800     case IDM_CopyGame:\r
5801       CopyGameToClipboard();\r
5802       break;\r
5803 \r
5804     case IDM_PasteGame:\r
5805       PasteGameFromClipboard();\r
5806       break;\r
5807 \r
5808     case IDM_CopyGameListToClipboard:\r
5809       CopyGameListToClipboard();\r
5810       break;\r
5811 \r
5812     /* [AS] Autodetect FEN or PGN data */\r
5813     case IDM_PasteAny:\r
5814       PasteGameOrFENFromClipboard();\r
5815       break;\r
5816 \r
5817     /* [AS] Move history */\r
5818     case IDM_ShowMoveHistory:\r
5819         if( MoveHistoryIsUp() ) {\r
5820             MoveHistoryPopDown();\r
5821         }\r
5822         else {\r
5823             MoveHistoryPopUp();\r
5824         }\r
5825         break;\r
5826 \r
5827     /* [AS] Eval graph */\r
5828     case IDM_ShowEvalGraph:\r
5829         if( EvalGraphIsUp() ) {\r
5830             EvalGraphPopDown();\r
5831         }\r
5832         else {\r
5833             EvalGraphPopUp();\r
5834             SetFocus(hwndMain);\r
5835         }\r
5836         break;\r
5837 \r
5838     /* [AS] Engine output */\r
5839     case IDM_ShowEngineOutput:\r
5840         if( EngineOutputIsUp() ) {\r
5841             EngineOutputPopDown();\r
5842         }\r
5843         else {\r
5844             EngineOutputPopUp();\r
5845         }\r
5846         break;\r
5847 \r
5848     /* [AS] User adjudication */\r
5849     case IDM_UserAdjudication_White:\r
5850         UserAdjudicationEvent( +1 );\r
5851         break;\r
5852 \r
5853     case IDM_UserAdjudication_Black:\r
5854         UserAdjudicationEvent( -1 );\r
5855         break;\r
5856 \r
5857     case IDM_UserAdjudication_Draw:\r
5858         UserAdjudicationEvent( 0 );\r
5859         break;\r
5860 \r
5861     /* [AS] Game list options dialog */\r
5862     case IDM_GameListOptions:\r
5863       GameListOptions();\r
5864       break;\r
5865 \r
5866     case IDM_CopyPosition:\r
5867       CopyFENToClipboard();\r
5868       break;\r
5869 \r
5870     case IDM_PastePosition:\r
5871       PasteFENFromClipboard();\r
5872       break;\r
5873 \r
5874     case IDM_MailMove:\r
5875       MailMoveEvent();\r
5876       break;\r
5877 \r
5878     case IDM_ReloadCMailMsg:\r
5879       Reset(TRUE, TRUE);\r
5880       ReloadCmailMsgEvent(FALSE);\r
5881       break;\r
5882 \r
5883     case IDM_Minimize:\r
5884       ShowWindow(hwnd, SW_MINIMIZE);\r
5885       break;\r
5886 \r
5887     case IDM_Exit:\r
5888       ExitEvent(0);\r
5889       break;\r
5890 \r
5891     case IDM_MachineWhite:\r
5892       MachineWhiteEvent();\r
5893       /*\r
5894        * refresh the tags dialog only if it's visible\r
5895        */\r
5896       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5897           char *tags;\r
5898           tags = PGNTags(&gameInfo);\r
5899           TagsPopUp(tags, CmailMsg());\r
5900           free(tags);\r
5901       }\r
5902       SAY("computer starts playing white");\r
5903       break;\r
5904 \r
5905     case IDM_MachineBlack:\r
5906       MachineBlackEvent();\r
5907       /*\r
5908        * refresh the tags dialog only if it's visible\r
5909        */\r
5910       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5911           char *tags;\r
5912           tags = PGNTags(&gameInfo);\r
5913           TagsPopUp(tags, CmailMsg());\r
5914           free(tags);\r
5915       }\r
5916       SAY("computer starts playing black");\r
5917       break;\r
5918 \r
5919     case IDM_TwoMachines:\r
5920       TwoMachinesEvent();\r
5921       /*\r
5922        * refresh the tags dialog only if it's visible\r
5923        */\r
5924       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5925           char *tags;\r
5926           tags = PGNTags(&gameInfo);\r
5927           TagsPopUp(tags, CmailMsg());\r
5928           free(tags);\r
5929       }\r
5930       SAY("programs start playing each other");\r
5931       break;\r
5932 \r
5933     case IDM_AnalysisMode:\r
5934       if (!first.analysisSupport) {\r
5935         sprintf(buf, "%s does not support analysis", first.tidy);\r
5936         DisplayError(buf, 0);\r
5937       } else {\r
5938         SAY("analyzing current position");\r
5939         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5940         if (appData.icsActive) {\r
5941                if (gameMode != IcsObserving) {\r
5942                        sprintf(buf, "You are not observing a game");\r
5943                        DisplayError(buf, 0);\r
5944                        /* secure check */\r
5945                        if (appData.icsEngineAnalyze) {\r
5946                                if (appData.debugMode) \r
5947                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5948                                ExitAnalyzeMode();\r
5949                                ModeHighlight();\r
5950                                break;\r
5951                        }\r
5952                        break;\r
5953                } else {\r
5954                        /* if enable, user want disable icsEngineAnalyze */\r
5955                        if (appData.icsEngineAnalyze) {\r
5956                                ExitAnalyzeMode();\r
5957                                ModeHighlight();\r
5958                                break;\r
5959                        }\r
5960                        appData.icsEngineAnalyze = TRUE;\r
5961                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5962                }\r
5963         } \r
5964         if (!appData.showThinking) ToggleShowThinking();\r
5965         AnalyzeModeEvent();\r
5966       }\r
5967       break;\r
5968 \r
5969     case IDM_AnalyzeFile:\r
5970       if (!first.analysisSupport) {\r
5971         char buf[MSG_SIZ];\r
5972         sprintf(buf, "%s does not support analysis", first.tidy);\r
5973         DisplayError(buf, 0);\r
5974       } else {\r
5975         if (!appData.showThinking) ToggleShowThinking();\r
5976         AnalyzeFileEvent();\r
5977         LoadGameDialog(hwnd, "Analyze Game from File");\r
5978         AnalysisPeriodicEvent(1);\r
5979       }\r
5980       break;\r
5981 \r
5982     case IDM_IcsClient:\r
5983       IcsClientEvent();\r
5984       break;\r
5985 \r
5986     case IDM_EditGame:\r
5987       EditGameEvent();\r
5988       SAY("edit game");\r
5989       break;\r
5990 \r
5991     case IDM_EditPosition:\r
5992       EditPositionEvent();\r
5993       SAY("to set up a position type a FEN");\r
5994       break;\r
5995 \r
5996     case IDM_Training:\r
5997       TrainingEvent();\r
5998       break;\r
5999 \r
6000     case IDM_ShowGameList:\r
6001       ShowGameListProc();\r
6002       break;\r
6003 \r
6004     case IDM_EditTags:\r
6005       EditTagsProc();\r
6006       break;\r
6007 \r
6008     case IDM_EditComment:\r
6009       if (commentDialogUp && editComment) {\r
6010         CommentPopDown();\r
6011       } else {\r
6012         EditCommentEvent();\r
6013       }\r
6014       break;\r
6015 \r
6016     case IDM_Pause:\r
6017       PauseEvent();\r
6018       break;\r
6019 \r
6020     case IDM_Accept:\r
6021       AcceptEvent();\r
6022       break;\r
6023 \r
6024     case IDM_Decline:\r
6025       DeclineEvent();\r
6026       break;\r
6027 \r
6028     case IDM_Rematch:\r
6029       RematchEvent();\r
6030       break;\r
6031 \r
6032     case IDM_CallFlag:\r
6033       CallFlagEvent();\r
6034       break;\r
6035 \r
6036     case IDM_Draw:\r
6037       DrawEvent();\r
6038       break;\r
6039 \r
6040     case IDM_Adjourn:\r
6041       AdjournEvent();\r
6042       break;\r
6043 \r
6044     case IDM_Abort:\r
6045       AbortEvent();\r
6046       break;\r
6047 \r
6048     case IDM_Resign:\r
6049       ResignEvent();\r
6050       break;\r
6051 \r
6052     case IDM_StopObserving:\r
6053       StopObservingEvent();\r
6054       break;\r
6055 \r
6056     case IDM_StopExamining:\r
6057       StopExaminingEvent();\r
6058       break;\r
6059 \r
6060     case IDM_TypeInMove:\r
6061       PopUpMoveDialog('\000');\r
6062       break;\r
6063 \r
6064     case IDM_TypeInName:\r
6065       PopUpNameDialog('\000');\r
6066       break;\r
6067 \r
6068     case IDM_Backward:\r
6069       BackwardEvent();\r
6070       SetFocus(hwndMain);\r
6071       break;\r
6072 \r
6073     JAWS_MENU_ITEMS\r
6074 \r
6075     case IDM_Forward:\r
6076       ForwardEvent();\r
6077       SetFocus(hwndMain);\r
6078       break;\r
6079 \r
6080     case IDM_ToStart:\r
6081       ToStartEvent();\r
6082       SetFocus(hwndMain);\r
6083       break;\r
6084 \r
6085     case IDM_ToEnd:\r
6086       ToEndEvent();\r
6087       SetFocus(hwndMain);\r
6088       break;\r
6089 \r
6090     case IDM_Revert:\r
6091       RevertEvent();\r
6092       break;\r
6093 \r
6094     case IDM_TruncateGame:\r
6095       TruncateGameEvent();\r
6096       break;\r
6097 \r
6098     case IDM_MoveNow:\r
6099       MoveNowEvent();\r
6100       break;\r
6101 \r
6102     case IDM_RetractMove:\r
6103       RetractMoveEvent();\r
6104       break;\r
6105 \r
6106     case IDM_FlipView:\r
6107       flipView = !flipView;\r
6108       DrawPosition(FALSE, NULL);\r
6109       break;\r
6110 \r
6111     case IDM_FlipClock:\r
6112       flipClock = !flipClock;\r
6113       DisplayBothClocks();\r
6114       DrawPosition(FALSE, NULL);\r
6115       break;\r
6116 \r
6117     case IDM_GeneralOptions:\r
6118       GeneralOptionsPopup(hwnd);\r
6119       DrawPosition(TRUE, NULL);\r
6120       break;\r
6121 \r
6122     case IDM_BoardOptions:\r
6123       BoardOptionsPopup(hwnd);\r
6124       break;\r
6125 \r
6126     case IDM_EnginePlayOptions:\r
6127       EnginePlayOptionsPopup(hwnd);\r
6128       break;\r
6129 \r
6130     case IDM_OptionsUCI:\r
6131       UciOptionsPopup(hwnd);\r
6132       break;\r
6133 \r
6134     case IDM_IcsOptions:\r
6135       IcsOptionsPopup(hwnd);\r
6136       break;\r
6137 \r
6138     case IDM_Fonts:\r
6139       FontsOptionsPopup(hwnd);\r
6140       break;\r
6141 \r
6142     case IDM_Sounds:\r
6143       SoundOptionsPopup(hwnd);\r
6144       break;\r
6145 \r
6146     case IDM_CommPort:\r
6147       CommPortOptionsPopup(hwnd);\r
6148       break;\r
6149 \r
6150     case IDM_LoadOptions:\r
6151       LoadOptionsPopup(hwnd);\r
6152       break;\r
6153 \r
6154     case IDM_SaveOptions:\r
6155       SaveOptionsPopup(hwnd);\r
6156       break;\r
6157 \r
6158     case IDM_TimeControl:\r
6159       TimeControlOptionsPopup(hwnd);\r
6160       break;\r
6161 \r
6162     case IDM_SaveSettings:\r
6163       SaveSettings(settingsFileName);\r
6164       break;\r
6165 \r
6166     case IDM_SaveSettingsOnExit:\r
6167       saveSettingsOnExit = !saveSettingsOnExit;\r
6168       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6169                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6170                                          MF_CHECKED : MF_UNCHECKED));\r
6171       break;\r
6172 \r
6173     case IDM_Hint:\r
6174       HintEvent();\r
6175       break;\r
6176 \r
6177     case IDM_Book:\r
6178       BookEvent();\r
6179       break;\r
6180 \r
6181     case IDM_AboutGame:\r
6182       AboutGameEvent();\r
6183       break;\r
6184 \r
6185     case IDM_Debug:\r
6186       appData.debugMode = !appData.debugMode;\r
6187       if (appData.debugMode) {\r
6188         char dir[MSG_SIZ];\r
6189         GetCurrentDirectory(MSG_SIZ, dir);\r
6190         SetCurrentDirectory(installDir);\r
6191         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6192         SetCurrentDirectory(dir);\r
6193         setbuf(debugFP, NULL);\r
6194       } else {\r
6195         fclose(debugFP);\r
6196         debugFP = NULL;\r
6197       }\r
6198       break;\r
6199 \r
6200     case IDM_HELPCONTENTS:\r
6201       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6202           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6203           MessageBox (GetFocus(),\r
6204                     "Unable to activate help",\r
6205                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6206       }\r
6207       break;\r
6208 \r
6209     case IDM_HELPSEARCH:\r
6210         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6211             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6212         MessageBox (GetFocus(),\r
6213                     "Unable to activate help",\r
6214                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6215       }\r
6216       break;\r
6217 \r
6218     case IDM_HELPHELP:\r
6219       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6220         MessageBox (GetFocus(),\r
6221                     "Unable to activate help",\r
6222                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6223       }\r
6224       break;\r
6225 \r
6226     case IDM_ABOUT:\r
6227       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6228       DialogBox(hInst, \r
6229         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6230         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6231       FreeProcInstance(lpProc);\r
6232       break;\r
6233 \r
6234     case IDM_DirectCommand1:\r
6235       AskQuestionEvent("Direct Command",\r
6236                        "Send to chess program:", "", "1");\r
6237       break;\r
6238     case IDM_DirectCommand2:\r
6239       AskQuestionEvent("Direct Command",\r
6240                        "Send to second chess program:", "", "2");\r
6241       break;\r
6242 \r
6243     case EP_WhitePawn:\r
6244       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6245       fromX = fromY = -1;\r
6246       break;\r
6247 \r
6248     case EP_WhiteKnight:\r
6249       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6250       fromX = fromY = -1;\r
6251       break;\r
6252 \r
6253     case EP_WhiteBishop:\r
6254       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6255       fromX = fromY = -1;\r
6256       break;\r
6257 \r
6258     case EP_WhiteRook:\r
6259       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6260       fromX = fromY = -1;\r
6261       break;\r
6262 \r
6263     case EP_WhiteQueen:\r
6264       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6265       fromX = fromY = -1;\r
6266       break;\r
6267 \r
6268     case EP_WhiteFerz:\r
6269       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6270       fromX = fromY = -1;\r
6271       break;\r
6272 \r
6273     case EP_WhiteWazir:\r
6274       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6275       fromX = fromY = -1;\r
6276       break;\r
6277 \r
6278     case EP_WhiteAlfil:\r
6279       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6280       fromX = fromY = -1;\r
6281       break;\r
6282 \r
6283     case EP_WhiteCannon:\r
6284       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6285       fromX = fromY = -1;\r
6286       break;\r
6287 \r
6288     case EP_WhiteCardinal:\r
6289       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6290       fromX = fromY = -1;\r
6291       break;\r
6292 \r
6293     case EP_WhiteMarshall:\r
6294       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6295       fromX = fromY = -1;\r
6296       break;\r
6297 \r
6298     case EP_WhiteKing:\r
6299       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6300       fromX = fromY = -1;\r
6301       break;\r
6302 \r
6303     case EP_BlackPawn:\r
6304       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6305       fromX = fromY = -1;\r
6306       break;\r
6307 \r
6308     case EP_BlackKnight:\r
6309       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6310       fromX = fromY = -1;\r
6311       break;\r
6312 \r
6313     case EP_BlackBishop:\r
6314       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6315       fromX = fromY = -1;\r
6316       break;\r
6317 \r
6318     case EP_BlackRook:\r
6319       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6320       fromX = fromY = -1;\r
6321       break;\r
6322 \r
6323     case EP_BlackQueen:\r
6324       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6325       fromX = fromY = -1;\r
6326       break;\r
6327 \r
6328     case EP_BlackFerz:\r
6329       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6330       fromX = fromY = -1;\r
6331       break;\r
6332 \r
6333     case EP_BlackWazir:\r
6334       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6335       fromX = fromY = -1;\r
6336       break;\r
6337 \r
6338     case EP_BlackAlfil:\r
6339       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6340       fromX = fromY = -1;\r
6341       break;\r
6342 \r
6343     case EP_BlackCannon:\r
6344       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6345       fromX = fromY = -1;\r
6346       break;\r
6347 \r
6348     case EP_BlackCardinal:\r
6349       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6350       fromX = fromY = -1;\r
6351       break;\r
6352 \r
6353     case EP_BlackMarshall:\r
6354       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6355       fromX = fromY = -1;\r
6356       break;\r
6357 \r
6358     case EP_BlackKing:\r
6359       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6360       fromX = fromY = -1;\r
6361       break;\r
6362 \r
6363     case EP_EmptySquare:\r
6364       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6365       fromX = fromY = -1;\r
6366       break;\r
6367 \r
6368     case EP_ClearBoard:\r
6369       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6370       fromX = fromY = -1;\r
6371       break;\r
6372 \r
6373     case EP_White:\r
6374       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6375       fromX = fromY = -1;\r
6376       break;\r
6377 \r
6378     case EP_Black:\r
6379       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6380       fromX = fromY = -1;\r
6381       break;\r
6382 \r
6383     case EP_Promote:\r
6384       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6385       fromX = fromY = -1;\r
6386       break;\r
6387 \r
6388     case EP_Demote:\r
6389       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6390       fromX = fromY = -1;\r
6391       break;\r
6392 \r
6393     case DP_Pawn:\r
6394       DropMenuEvent(WhitePawn, fromX, fromY);\r
6395       fromX = fromY = -1;\r
6396       break;\r
6397 \r
6398     case DP_Knight:\r
6399       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6400       fromX = fromY = -1;\r
6401       break;\r
6402 \r
6403     case DP_Bishop:\r
6404       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6405       fromX = fromY = -1;\r
6406       break;\r
6407 \r
6408     case DP_Rook:\r
6409       DropMenuEvent(WhiteRook, fromX, fromY);\r
6410       fromX = fromY = -1;\r
6411       break;\r
6412 \r
6413     case DP_Queen:\r
6414       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6415       fromX = fromY = -1;\r
6416       break;\r
6417 \r
6418     default:\r
6419       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6420     }\r
6421     break;\r
6422 \r
6423   case WM_TIMER:\r
6424     switch (wParam) {\r
6425     case CLOCK_TIMER_ID:\r
6426       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6427       clockTimerEvent = 0;\r
6428       DecrementClocks(); /* call into back end */\r
6429       break;\r
6430     case LOAD_GAME_TIMER_ID:\r
6431       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6432       loadGameTimerEvent = 0;\r
6433       AutoPlayGameLoop(); /* call into back end */\r
6434       break;\r
6435     case ANALYSIS_TIMER_ID:\r
6436       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6437                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6438         AnalysisPeriodicEvent(0);\r
6439       } else {\r
6440         KillTimer(hwnd, analysisTimerEvent);\r
6441         analysisTimerEvent = 0;\r
6442       }\r
6443       break;\r
6444     case DELAYED_TIMER_ID:\r
6445       KillTimer(hwnd, delayedTimerEvent);\r
6446       delayedTimerEvent = 0;\r
6447       delayedTimerCallback();\r
6448       break;\r
6449     }\r
6450     break;\r
6451 \r
6452   case WM_USER_Input:\r
6453     InputEvent(hwnd, message, wParam, lParam);\r
6454     break;\r
6455 \r
6456   /* [AS] Also move "attached" child windows */\r
6457   case WM_WINDOWPOSCHANGING:\r
6458 \r
6459     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6460         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6461 \r
6462         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6463             /* Window is moving */\r
6464             RECT rcMain;\r
6465 \r
6466 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6467             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6468             rcMain.right  = boardX + winWidth;\r
6469             rcMain.top    = boardY;\r
6470             rcMain.bottom = boardY + winHeight;\r
6471             \r
6472             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6473             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6474             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6475             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6476             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6477             boardX = lpwp->x;\r
6478             boardY = lpwp->y;\r
6479         }\r
6480     }\r
6481     break;\r
6482 \r
6483   /* [AS] Snapping */\r
6484   case WM_ENTERSIZEMOVE:\r
6485     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6486     if (hwnd == hwndMain) {\r
6487       doingSizing = TRUE;\r
6488       lastSizing = 0;\r
6489     }\r
6490     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6491     break;\r
6492 \r
6493   case WM_SIZING:\r
6494     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6495     if (hwnd == hwndMain) {\r
6496       lastSizing = wParam;\r
6497     }\r
6498     break;\r
6499 \r
6500   case WM_MOVING:\r
6501     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6502       return OnMoving( &sd, hwnd, wParam, lParam );\r
6503 \r
6504   case WM_EXITSIZEMOVE:\r
6505     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6506     if (hwnd == hwndMain) {\r
6507       RECT client;\r
6508       doingSizing = FALSE;\r
6509       InvalidateRect(hwnd, &boardRect, FALSE);\r
6510       GetClientRect(hwnd, &client);\r
6511       ResizeBoard(client.right, client.bottom, lastSizing);\r
6512       lastSizing = 0;\r
6513       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6514     }\r
6515     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6516     break;\r
6517 \r
6518   case WM_DESTROY: /* message: window being destroyed */\r
6519     PostQuitMessage(0);\r
6520     break;\r
6521 \r
6522   case WM_CLOSE:\r
6523     if (hwnd == hwndMain) {\r
6524       ExitEvent(0);\r
6525     }\r
6526     break;\r
6527 \r
6528   default:      /* Passes it on if unprocessed */\r
6529     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6530   }\r
6531   return 0;\r
6532 }\r
6533 \r
6534 /*---------------------------------------------------------------------------*\\r
6535  *\r
6536  * Misc utility routines\r
6537  *\r
6538 \*---------------------------------------------------------------------------*/\r
6539 \r
6540 /*\r
6541  * Decent random number generator, at least not as bad as Windows\r
6542  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6543  */\r
6544 unsigned int randstate;\r
6545 \r
6546 int\r
6547 myrandom(void)\r
6548 {\r
6549   randstate = randstate * 1664525 + 1013904223;\r
6550   return (int) randstate & 0x7fffffff;\r
6551 }\r
6552 \r
6553 void\r
6554 mysrandom(unsigned int seed)\r
6555 {\r
6556   randstate = seed;\r
6557 }\r
6558 \r
6559 \r
6560 /* \r
6561  * returns TRUE if user selects a different color, FALSE otherwise \r
6562  */\r
6563 \r
6564 BOOL\r
6565 ChangeColor(HWND hwnd, COLORREF *which)\r
6566 {\r
6567   static BOOL firstTime = TRUE;\r
6568   static DWORD customColors[16];\r
6569   CHOOSECOLOR cc;\r
6570   COLORREF newcolor;\r
6571   int i;\r
6572   ColorClass ccl;\r
6573 \r
6574   if (firstTime) {\r
6575     /* Make initial colors in use available as custom colors */\r
6576     /* Should we put the compiled-in defaults here instead? */\r
6577     i = 0;\r
6578     customColors[i++] = lightSquareColor & 0xffffff;\r
6579     customColors[i++] = darkSquareColor & 0xffffff;\r
6580     customColors[i++] = whitePieceColor & 0xffffff;\r
6581     customColors[i++] = blackPieceColor & 0xffffff;\r
6582     customColors[i++] = highlightSquareColor & 0xffffff;\r
6583     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6584 \r
6585     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6586       customColors[i++] = textAttribs[ccl].color;\r
6587     }\r
6588     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6589     firstTime = FALSE;\r
6590   }\r
6591 \r
6592   cc.lStructSize = sizeof(cc);\r
6593   cc.hwndOwner = hwnd;\r
6594   cc.hInstance = NULL;\r
6595   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6596   cc.lpCustColors = (LPDWORD) customColors;\r
6597   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6598 \r
6599   if (!ChooseColor(&cc)) return FALSE;\r
6600 \r
6601   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6602   if (newcolor == *which) return FALSE;\r
6603   *which = newcolor;\r
6604   return TRUE;\r
6605 \r
6606   /*\r
6607   InitDrawingColors();\r
6608   InvalidateRect(hwnd, &boardRect, FALSE);\r
6609   */\r
6610 }\r
6611 \r
6612 BOOLEAN\r
6613 MyLoadSound(MySound *ms)\r
6614 {\r
6615   BOOL ok = FALSE;\r
6616   struct stat st;\r
6617   FILE *f;\r
6618 \r
6619   if (ms->data) free(ms->data);\r
6620   ms->data = NULL;\r
6621 \r
6622   switch (ms->name[0]) {\r
6623   case NULLCHAR:\r
6624     /* Silence */\r
6625     ok = TRUE;\r
6626     break;\r
6627   case '$':\r
6628     /* System sound from Control Panel.  Don't preload here. */\r
6629     ok = TRUE;\r
6630     break;\r
6631   case '!':\r
6632     if (ms->name[1] == NULLCHAR) {\r
6633       /* "!" alone = silence */\r
6634       ok = TRUE;\r
6635     } else {\r
6636       /* Builtin wave resource.  Error if not found. */\r
6637       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6638       if (h == NULL) break;\r
6639       ms->data = (void *)LoadResource(hInst, h);\r
6640       if (h == NULL) break;\r
6641       ok = TRUE;\r
6642     }\r
6643     break;\r
6644   default:\r
6645     /* .wav file.  Error if not found. */\r
6646     f = fopen(ms->name, "rb");\r
6647     if (f == NULL) break;\r
6648     if (fstat(fileno(f), &st) < 0) break;\r
6649     ms->data = malloc(st.st_size);\r
6650     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6651     fclose(f);\r
6652     ok = TRUE;\r
6653     break;\r
6654   }\r
6655   if (!ok) {\r
6656     char buf[MSG_SIZ];\r
6657     sprintf(buf, "Error loading sound %s", ms->name);\r
6658     DisplayError(buf, GetLastError());\r
6659   }\r
6660   return ok;\r
6661 }\r
6662 \r
6663 BOOLEAN\r
6664 MyPlaySound(MySound *ms)\r
6665 {\r
6666   BOOLEAN ok = FALSE;\r
6667 \r
6668   switch (ms->name[0]) {\r
6669   case NULLCHAR:\r
6670         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6671     /* Silence */\r
6672     ok = TRUE;\r
6673     break;\r
6674   case '$':\r
6675     /* System sound from Control Panel (deprecated feature).\r
6676        "$" alone or an unset sound name gets default beep (still in use). */\r
6677     if (ms->name[1]) {\r
6678       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6679     }\r
6680     if (!ok) ok = MessageBeep(MB_OK);\r
6681     break; \r
6682   case '!':\r
6683     /* Builtin wave resource, or "!" alone for silence */\r
6684     if (ms->name[1]) {\r
6685       if (ms->data == NULL) return FALSE;\r
6686       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6687     } else {\r
6688       ok = TRUE;\r
6689     }\r
6690     break;\r
6691   default:\r
6692     /* .wav file.  Error if not found. */\r
6693     if (ms->data == NULL) return FALSE;\r
6694     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6695     break;\r
6696   }\r
6697   /* Don't print an error: this can happen innocently if the sound driver\r
6698      is busy; for instance, if another instance of WinBoard is playing\r
6699      a sound at about the same time. */\r
6700 #if 0\r
6701   if (!ok) {\r
6702     char buf[MSG_SIZ];\r
6703     sprintf(buf, "Error playing sound %s", ms->name);\r
6704     DisplayError(buf, GetLastError());\r
6705   }\r
6706 #endif\r
6707   return ok;\r
6708 }\r
6709 \r
6710 \r
6711 LRESULT CALLBACK\r
6712 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6713 {\r
6714   BOOL ok;\r
6715   OPENFILENAME *ofn;\r
6716   static UINT *number; /* gross that this is static */\r
6717 \r
6718   switch (message) {\r
6719   case WM_INITDIALOG: /* message: initialize dialog box */\r
6720     /* Center the dialog over the application window */\r
6721     ofn = (OPENFILENAME *) lParam;\r
6722     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6723       number = (UINT *) ofn->lCustData;\r
6724       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6725     } else {\r
6726       number = NULL;\r
6727     }\r
6728     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6729     return FALSE;  /* Allow for further processing */\r
6730 \r
6731   case WM_COMMAND:\r
6732     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6733       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6734     }\r
6735     return FALSE;  /* Allow for further processing */\r
6736   }\r
6737   return FALSE;\r
6738 }\r
6739 \r
6740 UINT APIENTRY\r
6741 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6742 {\r
6743   static UINT *number;\r
6744   OPENFILENAME *ofname;\r
6745   OFNOTIFY *ofnot;\r
6746   switch (uiMsg) {\r
6747   case WM_INITDIALOG:\r
6748     ofname = (OPENFILENAME *)lParam;\r
6749     number = (UINT *)(ofname->lCustData);\r
6750     break;\r
6751   case WM_NOTIFY:\r
6752     ofnot = (OFNOTIFY *)lParam;\r
6753     if (ofnot->hdr.code == CDN_FILEOK) {\r
6754       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6755     }\r
6756     break;\r
6757   }\r
6758   return 0;\r
6759 }\r
6760 \r
6761 \r
6762 FILE *\r
6763 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6764                char *nameFilt, char *dlgTitle, UINT *number,\r
6765                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6766 {\r
6767   OPENFILENAME openFileName;\r
6768   char buf1[MSG_SIZ];\r
6769   FILE *f;\r
6770 \r
6771   if (fileName == NULL) fileName = buf1;\r
6772   if (defName == NULL) {\r
6773     strcpy(fileName, "*.");\r
6774     strcat(fileName, defExt);\r
6775   } else {\r
6776     strcpy(fileName, defName);\r
6777   }\r
6778   if (fileTitle) strcpy(fileTitle, "");\r
6779   if (number) *number = 0;\r
6780 \r
6781   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6782   openFileName.hwndOwner         = hwnd;\r
6783   openFileName.hInstance         = (HANDLE) hInst;\r
6784   openFileName.lpstrFilter       = nameFilt;\r
6785   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6786   openFileName.nMaxCustFilter    = 0L;\r
6787   openFileName.nFilterIndex      = 1L;\r
6788   openFileName.lpstrFile         = fileName;\r
6789   openFileName.nMaxFile          = MSG_SIZ;\r
6790   openFileName.lpstrFileTitle    = fileTitle;\r
6791   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6792   openFileName.lpstrInitialDir   = NULL;\r
6793   openFileName.lpstrTitle        = dlgTitle;\r
6794   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6795     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6796     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6797     | (oldDialog ? 0 : OFN_EXPLORER);\r
6798   openFileName.nFileOffset       = 0;\r
6799   openFileName.nFileExtension    = 0;\r
6800   openFileName.lpstrDefExt       = defExt;\r
6801   openFileName.lCustData         = (LONG) number;\r
6802   openFileName.lpfnHook          = oldDialog ?\r
6803     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6804   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6805 \r
6806   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6807                         GetOpenFileName(&openFileName)) {\r
6808     /* open the file */\r
6809     f = fopen(openFileName.lpstrFile, write);\r
6810     if (f == NULL) {\r
6811       MessageBox(hwnd, "File open failed", NULL,\r
6812                  MB_OK|MB_ICONEXCLAMATION);\r
6813       return NULL;\r
6814     }\r
6815   } else {\r
6816     int err = CommDlgExtendedError();\r
6817     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6818     return FALSE;\r
6819   }\r
6820   return f;\r
6821 }\r
6822 \r
6823 \r
6824 \r
6825 VOID APIENTRY\r
6826 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6827 {\r
6828   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6829 \r
6830   /*\r
6831    * Get the first pop-up menu in the menu template. This is the\r
6832    * menu that TrackPopupMenu displays.\r
6833    */\r
6834   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6835 \r
6836   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6837 \r
6838   /*\r
6839    * TrackPopup uses screen coordinates, so convert the\r
6840    * coordinates of the mouse click to screen coordinates.\r
6841    */\r
6842   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6843 \r
6844   /* Draw and track the floating pop-up menu. */\r
6845   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6846                  pt.x, pt.y, 0, hwnd, NULL);\r
6847 \r
6848   /* Destroy the menu.*/\r
6849   DestroyMenu(hmenu);\r
6850 }\r
6851    \r
6852 typedef struct {\r
6853   HWND hDlg, hText;\r
6854   int sizeX, sizeY, newSizeX, newSizeY;\r
6855   HDWP hdwp;\r
6856 } ResizeEditPlusButtonsClosure;\r
6857 \r
6858 BOOL CALLBACK\r
6859 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6860 {\r
6861   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6862   RECT rect;\r
6863   POINT pt;\r
6864 \r
6865   if (hChild == cl->hText) return TRUE;\r
6866   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6867   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6868   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6869   ScreenToClient(cl->hDlg, &pt);\r
6870   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6871     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6872   return TRUE;\r
6873 }\r
6874 \r
6875 /* Resize a dialog that has a (rich) edit field filling most of\r
6876    the top, with a row of buttons below */\r
6877 VOID\r
6878 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6879 {\r
6880   RECT rectText;\r
6881   int newTextHeight, newTextWidth;\r
6882   ResizeEditPlusButtonsClosure cl;\r
6883   \r
6884   /*if (IsIconic(hDlg)) return;*/\r
6885   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6886   \r
6887   cl.hdwp = BeginDeferWindowPos(8);\r
6888 \r
6889   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6890   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6891   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6892   if (newTextHeight < 0) {\r
6893     newSizeY += -newTextHeight;\r
6894     newTextHeight = 0;\r
6895   }\r
6896   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6897     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6898 \r
6899   cl.hDlg = hDlg;\r
6900   cl.hText = hText;\r
6901   cl.sizeX = sizeX;\r
6902   cl.sizeY = sizeY;\r
6903   cl.newSizeX = newSizeX;\r
6904   cl.newSizeY = newSizeY;\r
6905   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6906 \r
6907   EndDeferWindowPos(cl.hdwp);\r
6908 }\r
6909 \r
6910 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6911 {\r
6912     RECT    rChild, rParent;\r
6913     int     wChild, hChild, wParent, hParent;\r
6914     int     wScreen, hScreen, xNew, yNew;\r
6915     HDC     hdc;\r
6916 \r
6917     /* Get the Height and Width of the child window */\r
6918     GetWindowRect (hwndChild, &rChild);\r
6919     wChild = rChild.right - rChild.left;\r
6920     hChild = rChild.bottom - rChild.top;\r
6921 \r
6922     /* Get the Height and Width of the parent window */\r
6923     GetWindowRect (hwndParent, &rParent);\r
6924     wParent = rParent.right - rParent.left;\r
6925     hParent = rParent.bottom - rParent.top;\r
6926 \r
6927     /* Get the display limits */\r
6928     hdc = GetDC (hwndChild);\r
6929     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6930     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6931     ReleaseDC(hwndChild, hdc);\r
6932 \r
6933     /* Calculate new X position, then adjust for screen */\r
6934     xNew = rParent.left + ((wParent - wChild) /2);\r
6935     if (xNew < 0) {\r
6936         xNew = 0;\r
6937     } else if ((xNew+wChild) > wScreen) {\r
6938         xNew = wScreen - wChild;\r
6939     }\r
6940 \r
6941     /* Calculate new Y position, then adjust for screen */\r
6942     if( mode == 0 ) {\r
6943         yNew = rParent.top  + ((hParent - hChild) /2);\r
6944     }\r
6945     else {\r
6946         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6947     }\r
6948 \r
6949     if (yNew < 0) {\r
6950         yNew = 0;\r
6951     } else if ((yNew+hChild) > hScreen) {\r
6952         yNew = hScreen - hChild;\r
6953     }\r
6954 \r
6955     /* Set it, and return */\r
6956     return SetWindowPos (hwndChild, NULL,\r
6957                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6958 }\r
6959 \r
6960 /* Center one window over another */\r
6961 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6962 {\r
6963     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6964 }\r
6965 \r
6966 /*---------------------------------------------------------------------------*\\r
6967  *\r
6968  * Startup Dialog functions\r
6969  *\r
6970 \*---------------------------------------------------------------------------*/\r
6971 void\r
6972 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6973 {\r
6974   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6975 \r
6976   while (*cd != NULL) {\r
6977     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6978     cd++;\r
6979   }\r
6980 }\r
6981 \r
6982 void\r
6983 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6984 {\r
6985   char buf1[ARG_MAX];\r
6986   int len;\r
6987 \r
6988   if (str[0] == '@') {\r
6989     FILE* f = fopen(str + 1, "r");\r
6990     if (f == NULL) {\r
6991       DisplayFatalError(str + 1, errno, 2);\r
6992       return;\r
6993     }\r
6994     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6995     fclose(f);\r
6996     buf1[len] = NULLCHAR;\r
6997     str = buf1;\r
6998   }\r
6999 \r
7000   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7001 \r
7002   for (;;) {\r
7003     char buf[MSG_SIZ];\r
7004     char *end = strchr(str, '\n');\r
7005     if (end == NULL) return;\r
7006     memcpy(buf, str, end - str);\r
7007     buf[end - str] = NULLCHAR;\r
7008     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
7009     str = end + 1;\r
7010   }\r
7011 }\r
7012 \r
7013 void\r
7014 SetStartupDialogEnables(HWND hDlg)\r
7015 {\r
7016   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
7017     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7018     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
7019   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7020     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
7021   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
7022     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
7023   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
7024     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
7025   EnableWindow(GetDlgItem(hDlg, IDOK),\r
7026     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7027     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
7028     IsDlgButtonChecked(hDlg, OPT_View));\r
7029 }\r
7030 \r
7031 char *\r
7032 QuoteForFilename(char *filename)\r
7033 {\r
7034   int dquote, space;\r
7035   dquote = strchr(filename, '"') != NULL;\r
7036   space = strchr(filename, ' ') != NULL;\r
7037   if (dquote || space) {\r
7038     if (dquote) {\r
7039       return "'";\r
7040     } else {\r
7041       return "\"";\r
7042     }\r
7043   } else {\r
7044     return "";\r
7045   }\r
7046 }\r
7047 \r
7048 VOID\r
7049 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
7050 {\r
7051   char buf[MSG_SIZ];\r
7052   char *q;\r
7053 \r
7054   InitComboStringsFromOption(hwndCombo, nthnames);\r
7055   q = QuoteForFilename(nthcp);\r
7056   sprintf(buf, "%s%s%s", q, nthcp, q);\r
7057   if (*nthdir != NULLCHAR) {\r
7058     q = QuoteForFilename(nthdir);\r
7059     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
7060   }\r
7061   if (*nthcp == NULLCHAR) {\r
7062     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7063   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7064     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7065     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7066   }\r
7067 }\r
7068 \r
7069 LRESULT CALLBACK\r
7070 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7071 {\r
7072   char buf[MSG_SIZ];\r
7073   HANDLE hwndCombo;\r
7074   char *p;\r
7075 \r
7076   switch (message) {\r
7077   case WM_INITDIALOG:\r
7078     /* Center the dialog */\r
7079     CenterWindow (hDlg, GetDesktopWindow());\r
7080     /* Initialize the dialog items */\r
7081     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7082                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7083                   firstChessProgramNames);\r
7084     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7085                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7086                   secondChessProgramNames);\r
7087     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7088     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7089     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7090     if (*appData.icsHelper != NULLCHAR) {\r
7091       char *q = QuoteForFilename(appData.icsHelper);\r
7092       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7093     }\r
7094     if (*appData.icsHost == NULLCHAR) {\r
7095       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7096       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7097     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7098       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7099       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7100     }\r
7101 \r
7102     if (appData.icsActive) {\r
7103       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7104     }\r
7105     else if (appData.noChessProgram) {\r
7106       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7107     }\r
7108     else {\r
7109       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7110     }\r
7111 \r
7112     SetStartupDialogEnables(hDlg);\r
7113     return TRUE;\r
7114 \r
7115   case WM_COMMAND:\r
7116     switch (LOWORD(wParam)) {\r
7117     case IDOK:\r
7118       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7119         strcpy(buf, "/fcp=");\r
7120         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7121         p = buf;\r
7122         ParseArgs(StringGet, &p);\r
7123         strcpy(buf, "/scp=");\r
7124         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7125         p = buf;\r
7126         ParseArgs(StringGet, &p);\r
7127         appData.noChessProgram = FALSE;\r
7128         appData.icsActive = FALSE;\r
7129       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7130         strcpy(buf, "/ics /icshost=");\r
7131         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7132         p = buf;\r
7133         ParseArgs(StringGet, &p);\r
7134         if (appData.zippyPlay) {\r
7135           strcpy(buf, "/fcp=");\r
7136           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7137           p = buf;\r
7138           ParseArgs(StringGet, &p);\r
7139         }\r
7140       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7141         appData.noChessProgram = TRUE;\r
7142         appData.icsActive = FALSE;\r
7143       } else {\r
7144         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7145                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7146         return TRUE;\r
7147       }\r
7148       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7149         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7150         p = buf;\r
7151         ParseArgs(StringGet, &p);\r
7152       }\r
7153       EndDialog(hDlg, TRUE);\r
7154       return TRUE;\r
7155 \r
7156     case IDCANCEL:\r
7157       ExitEvent(0);\r
7158       return TRUE;\r
7159 \r
7160     case IDM_HELPCONTENTS:\r
7161       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7162         MessageBox (GetFocus(),\r
7163                     "Unable to activate help",\r
7164                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7165       }\r
7166       break;\r
7167 \r
7168     default:\r
7169       SetStartupDialogEnables(hDlg);\r
7170       break;\r
7171     }\r
7172     break;\r
7173   }\r
7174   return FALSE;\r
7175 }\r
7176 \r
7177 /*---------------------------------------------------------------------------*\\r
7178  *\r
7179  * About box dialog functions\r
7180  *\r
7181 \*---------------------------------------------------------------------------*/\r
7182 \r
7183 /* Process messages for "About" dialog box */\r
7184 LRESULT CALLBACK\r
7185 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7186 {\r
7187   switch (message) {\r
7188   case WM_INITDIALOG: /* message: initialize dialog box */\r
7189     /* Center the dialog over the application window */\r
7190     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7191     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7192     JAWS_COPYRIGHT\r
7193     return (TRUE);\r
7194 \r
7195   case WM_COMMAND: /* message: received a command */\r
7196     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7197         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7198       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7199       return (TRUE);\r
7200     }\r
7201     break;\r
7202   }\r
7203   return (FALSE);\r
7204 }\r
7205 \r
7206 /*---------------------------------------------------------------------------*\\r
7207  *\r
7208  * Comment Dialog functions\r
7209  *\r
7210 \*---------------------------------------------------------------------------*/\r
7211 \r
7212 LRESULT CALLBACK\r
7213 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7214 {\r
7215   static HANDLE hwndText = NULL;\r
7216   int len, newSizeX, newSizeY, flags;\r
7217   static int sizeX, sizeY;\r
7218   char *str;\r
7219   RECT rect;\r
7220   MINMAXINFO *mmi;\r
7221 \r
7222   switch (message) {\r
7223   case WM_INITDIALOG: /* message: initialize dialog box */\r
7224     /* Initialize the dialog items */\r
7225     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7226     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7227     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7228     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7229     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7230     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7231     SetWindowText(hDlg, commentTitle);\r
7232     if (editComment) {\r
7233       SetFocus(hwndText);\r
7234     } else {\r
7235       SetFocus(GetDlgItem(hDlg, IDOK));\r
7236     }\r
7237     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7238                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7239                 MAKELPARAM(FALSE, 0));\r
7240     /* Size and position the dialog */\r
7241     if (!commentDialog) {\r
7242       commentDialog = hDlg;\r
7243       flags = SWP_NOZORDER;\r
7244       GetClientRect(hDlg, &rect);\r
7245       sizeX = rect.right;\r
7246       sizeY = rect.bottom;\r
7247       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7248           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7249         WINDOWPLACEMENT wp;\r
7250         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7251         wp.length = sizeof(WINDOWPLACEMENT);\r
7252         wp.flags = 0;\r
7253         wp.showCmd = SW_SHOW;\r
7254         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7255         wp.rcNormalPosition.left = commentX;\r
7256         wp.rcNormalPosition.right = commentX + commentW;\r
7257         wp.rcNormalPosition.top = commentY;\r
7258         wp.rcNormalPosition.bottom = commentY + commentH;\r
7259         SetWindowPlacement(hDlg, &wp);\r
7260 \r
7261         GetClientRect(hDlg, &rect);\r
7262         newSizeX = rect.right;\r
7263         newSizeY = rect.bottom;\r
7264         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7265                               newSizeX, newSizeY);\r
7266         sizeX = newSizeX;\r
7267         sizeY = newSizeY;\r
7268       }\r
7269     }\r
7270     return FALSE;\r
7271 \r
7272   case WM_COMMAND: /* message: received a command */\r
7273     switch (LOWORD(wParam)) {\r
7274     case IDOK:\r
7275       if (editComment) {\r
7276         char *p, *q;\r
7277         /* Read changed options from the dialog box */\r
7278         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7279         len = GetWindowTextLength(hwndText);\r
7280         str = (char *) malloc(len + 1);\r
7281         GetWindowText(hwndText, str, len + 1);\r
7282         p = q = str;\r
7283         while (*q) {\r
7284           if (*q == '\r')\r
7285             q++;\r
7286           else\r
7287             *p++ = *q++;\r
7288         }\r
7289         *p = NULLCHAR;\r
7290         ReplaceComment(commentIndex, str);\r
7291         free(str);\r
7292       }\r
7293       CommentPopDown();\r
7294       return TRUE;\r
7295 \r
7296     case IDCANCEL:\r
7297     case OPT_CancelComment:\r
7298       CommentPopDown();\r
7299       return TRUE;\r
7300 \r
7301     case OPT_ClearComment:\r
7302       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7303       break;\r
7304 \r
7305     case OPT_EditComment:\r
7306       EditCommentEvent();\r
7307       return TRUE;\r
7308 \r
7309     default:\r
7310       break;\r
7311     }\r
7312     break;\r
7313 \r
7314   case WM_SIZE:\r
7315     newSizeX = LOWORD(lParam);\r
7316     newSizeY = HIWORD(lParam);\r
7317     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7318     sizeX = newSizeX;\r
7319     sizeY = newSizeY;\r
7320     break;\r
7321 \r
7322   case WM_GETMINMAXINFO:\r
7323     /* Prevent resizing window too small */\r
7324     mmi = (MINMAXINFO *) lParam;\r
7325     mmi->ptMinTrackSize.x = 100;\r
7326     mmi->ptMinTrackSize.y = 100;\r
7327     break;\r
7328   }\r
7329   return FALSE;\r
7330 }\r
7331 \r
7332 VOID\r
7333 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7334 {\r
7335   FARPROC lpProc;\r
7336   char *p, *q;\r
7337 \r
7338   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7339 \r
7340   if (str == NULL) str = "";\r
7341   p = (char *) malloc(2 * strlen(str) + 2);\r
7342   q = p;\r
7343   while (*str) {\r
7344     if (*str == '\n') *q++ = '\r';\r
7345     *q++ = *str++;\r
7346   }\r
7347   *q = NULLCHAR;\r
7348   if (commentText != NULL) free(commentText);\r
7349 \r
7350   commentIndex = index;\r
7351   commentTitle = title;\r
7352   commentText = p;\r
7353   editComment = edit;\r
7354 \r
7355   if (commentDialog) {\r
7356     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7357     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7358   } else {\r
7359     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7360     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7361                  hwndMain, (DLGPROC)lpProc);\r
7362     FreeProcInstance(lpProc);\r
7363   }\r
7364   commentDialogUp = TRUE;\r
7365 }\r
7366 \r
7367 \r
7368 /*---------------------------------------------------------------------------*\\r
7369  *\r
7370  * Type-in move dialog functions\r
7371  * \r
7372 \*---------------------------------------------------------------------------*/\r
7373 \r
7374 LRESULT CALLBACK\r
7375 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7376 {\r
7377   char move[MSG_SIZ];\r
7378   HWND hInput;\r
7379   ChessMove moveType;\r
7380   int fromX, fromY, toX, toY;\r
7381   char promoChar;\r
7382 \r
7383   switch (message) {\r
7384   case WM_INITDIALOG:\r
7385     move[0] = (char) lParam;\r
7386     move[1] = NULLCHAR;\r
7387     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7388     hInput = GetDlgItem(hDlg, OPT_Move);\r
7389     SetWindowText(hInput, move);\r
7390     SetFocus(hInput);\r
7391     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7392     return FALSE;\r
7393 \r
7394   case WM_COMMAND:\r
7395     switch (LOWORD(wParam)) {\r
7396     case IDOK:\r
7397       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7398       { int n; Board board;\r
7399         // [HGM] FENedit\r
7400         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7401                 EditPositionPasteFEN(move);\r
7402                 EndDialog(hDlg, TRUE);\r
7403                 return TRUE;\r
7404         }\r
7405         // [HGM] movenum: allow move number to be typed in any mode\r
7406         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7407           currentMove = 2*n-1;\r
7408           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7409           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7410           EndDialog(hDlg, TRUE);\r
7411           DrawPosition(TRUE, boards[currentMove]);\r
7412           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7413           else DisplayMessage("", "");\r
7414           return TRUE;\r
7415         }\r
7416       }\r
7417       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7418         gameMode != Training) {\r
7419         DisplayMoveError("Displayed move is not current");\r
7420       } else {\r
7421 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7422         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7423           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7424         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7425         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7426           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7427           if (gameMode != Training)\r
7428               forwardMostMove = currentMove;\r
7429           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7430         } else {\r
7431           DisplayMoveError("Could not parse move");\r
7432         }\r
7433       }\r
7434       EndDialog(hDlg, TRUE);\r
7435       return TRUE;\r
7436     case IDCANCEL:\r
7437       EndDialog(hDlg, FALSE);\r
7438       return TRUE;\r
7439     default:\r
7440       break;\r
7441     }\r
7442     break;\r
7443   }\r
7444   return FALSE;\r
7445 }\r
7446 \r
7447 VOID\r
7448 PopUpMoveDialog(char firstchar)\r
7449 {\r
7450     FARPROC lpProc;\r
7451     \r
7452     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7453         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7454         gameMode == AnalyzeMode || gameMode == EditGame || \r
7455         gameMode == EditPosition || gameMode == IcsExamining ||\r
7456         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7457         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7458                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7459                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7460         gameMode == Training) {\r
7461       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7462       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7463         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7464       FreeProcInstance(lpProc);\r
7465     }\r
7466 }\r
7467 \r
7468 /*---------------------------------------------------------------------------*\\r
7469  *\r
7470  * Type-in name dialog functions\r
7471  * \r
7472 \*---------------------------------------------------------------------------*/\r
7473 \r
7474 LRESULT CALLBACK\r
7475 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7476 {\r
7477   char move[MSG_SIZ];\r
7478   HWND hInput;\r
7479 \r
7480   switch (message) {\r
7481   case WM_INITDIALOG:\r
7482     move[0] = (char) lParam;\r
7483     move[1] = NULLCHAR;\r
7484     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7485     hInput = GetDlgItem(hDlg, OPT_Name);\r
7486     SetWindowText(hInput, move);\r
7487     SetFocus(hInput);\r
7488     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7489     return FALSE;\r
7490 \r
7491   case WM_COMMAND:\r
7492     switch (LOWORD(wParam)) {\r
7493     case IDOK:\r
7494       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7495       appData.userName = strdup(move);\r
7496       SetUserLogo();\r
7497 \r
7498       EndDialog(hDlg, TRUE);\r
7499       return TRUE;\r
7500     case IDCANCEL:\r
7501       EndDialog(hDlg, FALSE);\r
7502       return TRUE;\r
7503     default:\r
7504       break;\r
7505     }\r
7506     break;\r
7507   }\r
7508   return FALSE;\r
7509 }\r
7510 \r
7511 VOID\r
7512 PopUpNameDialog(char firstchar)\r
7513 {\r
7514     FARPROC lpProc;\r
7515     \r
7516       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7517       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7518         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7519       FreeProcInstance(lpProc);\r
7520 }\r
7521 \r
7522 /*---------------------------------------------------------------------------*\\r
7523  *\r
7524  *  Error dialogs\r
7525  * \r
7526 \*---------------------------------------------------------------------------*/\r
7527 \r
7528 /* Nonmodal error box */\r
7529 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7530                              WPARAM wParam, LPARAM lParam);\r
7531 \r
7532 VOID\r
7533 ErrorPopUp(char *title, char *content)\r
7534 {\r
7535   FARPROC lpProc;\r
7536   char *p, *q;\r
7537   BOOLEAN modal = hwndMain == NULL;\r
7538 \r
7539   p = content;\r
7540   q = errorMessage;\r
7541   while (*p) {\r
7542     if (*p == '\n') {\r
7543       if (modal) {\r
7544         *q++ = ' ';\r
7545         p++;\r
7546       } else {\r
7547         *q++ = '\r';\r
7548         *q++ = *p++;\r
7549       }\r
7550     } else {\r
7551       *q++ = *p++;\r
7552     }\r
7553   }\r
7554   *q = NULLCHAR;\r
7555   strncpy(errorTitle, title, sizeof(errorTitle));\r
7556   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7557   \r
7558   if (modal) {\r
7559     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7560   } else {\r
7561     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7562     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7563                  hwndMain, (DLGPROC)lpProc);\r
7564     FreeProcInstance(lpProc);\r
7565   }\r
7566 }\r
7567 \r
7568 VOID\r
7569 ErrorPopDown()\r
7570 {\r
7571   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7572   if (errorDialog == NULL) return;\r
7573   DestroyWindow(errorDialog);\r
7574   errorDialog = NULL;\r
7575 }\r
7576 \r
7577 LRESULT CALLBACK\r
7578 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7579 {\r
7580   HANDLE hwndText;\r
7581   RECT rChild;\r
7582 \r
7583   switch (message) {\r
7584   case WM_INITDIALOG:\r
7585     GetWindowRect(hDlg, &rChild);\r
7586 \r
7587     /*\r
7588     SetWindowPos(hDlg, NULL, rChild.left,\r
7589       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7590       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7591     */\r
7592 \r
7593     /* \r
7594         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7595         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7596         and it doesn't work when you resize the dialog.\r
7597         For now, just give it a default position.\r
7598     */\r
7599     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7600 \r
7601     errorDialog = hDlg;\r
7602     SetWindowText(hDlg, errorTitle);\r
7603     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7604     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7605     return FALSE;\r
7606 \r
7607   case WM_COMMAND:\r
7608     switch (LOWORD(wParam)) {\r
7609     case IDOK:\r
7610     case IDCANCEL:\r
7611       if (errorDialog == hDlg) errorDialog = NULL;\r
7612       DestroyWindow(hDlg);\r
7613       return TRUE;\r
7614 \r
7615     default:\r
7616       break;\r
7617     }\r
7618     break;\r
7619   }\r
7620   return FALSE;\r
7621 }\r
7622 \r
7623 #ifdef GOTHIC\r
7624 HWND gothicDialog = NULL;\r
7625 \r
7626 LRESULT CALLBACK\r
7627 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7628 {\r
7629   HANDLE hwndText;\r
7630   RECT rChild;\r
7631   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7632 \r
7633   switch (message) {\r
7634   case WM_INITDIALOG:\r
7635     GetWindowRect(hDlg, &rChild);\r
7636 \r
7637     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7638                                                              SWP_NOZORDER);\r
7639 \r
7640     /* \r
7641         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7642         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7643         and it doesn't work when you resize the dialog.\r
7644         For now, just give it a default position.\r
7645     */\r
7646     gothicDialog = hDlg;\r
7647     SetWindowText(hDlg, errorTitle);\r
7648     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7649     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7650     return FALSE;\r
7651 \r
7652   case WM_COMMAND:\r
7653     switch (LOWORD(wParam)) {\r
7654     case IDOK:\r
7655     case IDCANCEL:\r
7656       if (errorDialog == hDlg) errorDialog = NULL;\r
7657       DestroyWindow(hDlg);\r
7658       return TRUE;\r
7659 \r
7660     default:\r
7661       break;\r
7662     }\r
7663     break;\r
7664   }\r
7665   return FALSE;\r
7666 }\r
7667 \r
7668 VOID\r
7669 GothicPopUp(char *title, VariantClass variant)\r
7670 {\r
7671   FARPROC lpProc;\r
7672   static char *lastTitle;\r
7673 \r
7674   strncpy(errorTitle, title, sizeof(errorTitle));\r
7675   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7676 \r
7677   if(lastTitle != title && gothicDialog != NULL) {\r
7678     DestroyWindow(gothicDialog);\r
7679     gothicDialog = NULL;\r
7680   }\r
7681   if(variant != VariantNormal && gothicDialog == NULL) {\r
7682     title = lastTitle;\r
7683     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7684     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7685                  hwndMain, (DLGPROC)lpProc);\r
7686     FreeProcInstance(lpProc);\r
7687   }\r
7688 }\r
7689 #endif\r
7690 \r
7691 /*---------------------------------------------------------------------------*\\r
7692  *\r
7693  *  Ics Interaction console functions\r
7694  *\r
7695 \*---------------------------------------------------------------------------*/\r
7696 \r
7697 #define HISTORY_SIZE 64\r
7698 static char *history[HISTORY_SIZE];\r
7699 int histIn = 0, histP = 0;\r
7700 \r
7701 VOID\r
7702 SaveInHistory(char *cmd)\r
7703 {\r
7704   if (history[histIn] != NULL) {\r
7705     free(history[histIn]);\r
7706     history[histIn] = NULL;\r
7707   }\r
7708   if (*cmd == NULLCHAR) return;\r
7709   history[histIn] = StrSave(cmd);\r
7710   histIn = (histIn + 1) % HISTORY_SIZE;\r
7711   if (history[histIn] != NULL) {\r
7712     free(history[histIn]);\r
7713     history[histIn] = NULL;\r
7714   }\r
7715   histP = histIn;\r
7716 }\r
7717 \r
7718 char *\r
7719 PrevInHistory(char *cmd)\r
7720 {\r
7721   int newhp;\r
7722   if (histP == histIn) {\r
7723     if (history[histIn] != NULL) free(history[histIn]);\r
7724     history[histIn] = StrSave(cmd);\r
7725   }\r
7726   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7727   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7728   histP = newhp;\r
7729   return history[histP];\r
7730 }\r
7731 \r
7732 char *\r
7733 NextInHistory()\r
7734 {\r
7735   if (histP == histIn) return NULL;\r
7736   histP = (histP + 1) % HISTORY_SIZE;\r
7737   return history[histP];\r
7738 }\r
7739 \r
7740 typedef struct {\r
7741   char *item;\r
7742   char *command;\r
7743   BOOLEAN getname;\r
7744   BOOLEAN immediate;\r
7745 } IcsTextMenuEntry;\r
7746 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7747 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7748 \r
7749 void\r
7750 ParseIcsTextMenu(char *icsTextMenuString)\r
7751 {\r
7752 //  int flags = 0;\r
7753   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7754   char *p = icsTextMenuString;\r
7755   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7756     free(e->item);\r
7757     e->item = NULL;\r
7758     if (e->command != NULL) {\r
7759       free(e->command);\r
7760       e->command = NULL;\r
7761     }\r
7762     e++;\r
7763   }\r
7764   e = icsTextMenuEntry;\r
7765   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7766     if (*p == ';' || *p == '\n') {\r
7767       e->item = strdup("-");\r
7768       e->command = NULL;\r
7769       p++;\r
7770     } else if (*p == '-') {\r
7771       e->item = strdup("-");\r
7772       e->command = NULL;\r
7773       p++;\r
7774       if (*p) p++;\r
7775     } else {\r
7776       char *q, *r, *s, *t;\r
7777       char c;\r
7778       q = strchr(p, ',');\r
7779       if (q == NULL) break;\r
7780       *q = NULLCHAR;\r
7781       r = strchr(q + 1, ',');\r
7782       if (r == NULL) break;\r
7783       *r = NULLCHAR;\r
7784       s = strchr(r + 1, ',');\r
7785       if (s == NULL) break;\r
7786       *s = NULLCHAR;\r
7787       c = ';';\r
7788       t = strchr(s + 1, c);\r
7789       if (t == NULL) {\r
7790         c = '\n';\r
7791         t = strchr(s + 1, c);\r
7792       }\r
7793       if (t != NULL) *t = NULLCHAR;\r
7794       e->item = strdup(p);\r
7795       e->command = strdup(q + 1);\r
7796       e->getname = *(r + 1) != '0';\r
7797       e->immediate = *(s + 1) != '0';\r
7798       *q = ',';\r
7799       *r = ',';\r
7800       *s = ',';\r
7801       if (t == NULL) break;\r
7802       *t = c;\r
7803       p = t + 1;\r
7804     }\r
7805     e++;\r
7806   } \r
7807 }\r
7808 \r
7809 HMENU\r
7810 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7811 {\r
7812   HMENU hmenu, h;\r
7813   int i = 0;\r
7814   hmenu = LoadMenu(hInst, "TextMenu");\r
7815   h = GetSubMenu(hmenu, 0);\r
7816   while (e->item) {\r
7817     if (strcmp(e->item, "-") == 0) {\r
7818       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7819     } else {\r
7820       if (e->item[0] == '|') {\r
7821         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7822                    IDM_CommandX + i, &e->item[1]);\r
7823       } else {\r
7824         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7825       }\r
7826     }\r
7827     e++;\r
7828     i++;\r
7829   } \r
7830   return hmenu;\r
7831 }\r
7832 \r
7833 WNDPROC consoleTextWindowProc;\r
7834 \r
7835 void\r
7836 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7837 {\r
7838   char buf[MSG_SIZ], name[MSG_SIZ];\r
7839   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7840   CHARRANGE sel;\r
7841 \r
7842   if (!getname) {\r
7843     SetWindowText(hInput, command);\r
7844     if (immediate) {\r
7845       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7846     } else {\r
7847       sel.cpMin = 999999;\r
7848       sel.cpMax = 999999;\r
7849       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7850       SetFocus(hInput);\r
7851     }\r
7852     return;\r
7853   }    \r
7854   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7855   if (sel.cpMin == sel.cpMax) {\r
7856     /* Expand to surrounding word */\r
7857     TEXTRANGE tr;\r
7858     do {\r
7859       tr.chrg.cpMax = sel.cpMin;\r
7860       tr.chrg.cpMin = --sel.cpMin;\r
7861       if (sel.cpMin < 0) break;\r
7862       tr.lpstrText = name;\r
7863       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7864     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7865     sel.cpMin++;\r
7866 \r
7867     do {\r
7868       tr.chrg.cpMin = sel.cpMax;\r
7869       tr.chrg.cpMax = ++sel.cpMax;\r
7870       tr.lpstrText = name;\r
7871       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7872     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7873     sel.cpMax--;\r
7874 \r
7875     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7876       MessageBeep(MB_ICONEXCLAMATION);\r
7877       return;\r
7878     }\r
7879     tr.chrg = sel;\r
7880     tr.lpstrText = name;\r
7881     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7882   } else {\r
7883     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7884       MessageBeep(MB_ICONEXCLAMATION);\r
7885       return;\r
7886     }\r
7887     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7888   }\r
7889   if (immediate) {\r
7890     sprintf(buf, "%s %s", command, name);\r
7891     SetWindowText(hInput, buf);\r
7892     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7893   } else {\r
7894     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7895     SetWindowText(hInput, buf);\r
7896     sel.cpMin = 999999;\r
7897     sel.cpMax = 999999;\r
7898     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7899     SetFocus(hInput);\r
7900   }\r
7901 }\r
7902 \r
7903 LRESULT CALLBACK \r
7904 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7905 {\r
7906   HWND hInput;\r
7907   CHARRANGE sel;\r
7908 \r
7909   switch (message) {\r
7910   case WM_KEYDOWN:\r
7911     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7912     switch (wParam) {\r
7913     case VK_PRIOR:\r
7914       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7915       return 0;\r
7916     case VK_NEXT:\r
7917       sel.cpMin = 999999;\r
7918       sel.cpMax = 999999;\r
7919       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7920       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7921       return 0;\r
7922     }\r
7923     break;\r
7924   case WM_CHAR:\r
7925    if(wParam != '\022') {\r
7926     if (wParam == '\t') {\r
7927       if (GetKeyState(VK_SHIFT) < 0) {\r
7928         /* shifted */\r
7929         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7930         if (buttonDesc[0].hwnd) {\r
7931           SetFocus(buttonDesc[0].hwnd);\r
7932         } else {\r
7933           SetFocus(hwndMain);\r
7934         }\r
7935       } else {\r
7936         /* unshifted */\r
7937         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7938       }\r
7939     } else {\r
7940       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7941       JAWS_DELETE( SetFocus(hInput); )\r
7942       SendMessage(hInput, message, wParam, lParam);\r
7943     }\r
7944     return 0;\r
7945    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7946   case WM_RBUTTONUP:\r
7947     if (GetKeyState(VK_SHIFT) & ~1) {\r
7948       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7949         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7950     } else {\r
7951       POINT pt;\r
7952       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7953       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7954       if (sel.cpMin == sel.cpMax) {\r
7955         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7956         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7957       }\r
7958       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7959         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7960       }\r
7961       pt.x = LOWORD(lParam);\r
7962       pt.y = HIWORD(lParam);\r
7963       MenuPopup(hwnd, pt, hmenu, -1);\r
7964     }\r
7965     return 0;\r
7966   case WM_PASTE:\r
7967     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7968     SetFocus(hInput);\r
7969     return SendMessage(hInput, message, wParam, lParam);\r
7970   case WM_MBUTTONDOWN:\r
7971     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7972   case WM_RBUTTONDOWN:\r
7973     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7974       /* Move selection here if it was empty */\r
7975       POINT pt;\r
7976       pt.x = LOWORD(lParam);\r
7977       pt.y = HIWORD(lParam);\r
7978       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7979       if (sel.cpMin == sel.cpMax) {\r
7980         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7981         sel.cpMax = sel.cpMin;\r
7982         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7983       }\r
7984       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7985     }\r
7986     return 0;\r
7987   case WM_COMMAND:\r
7988     switch (LOWORD(wParam)) {\r
7989     case IDM_QuickPaste:\r
7990       {\r
7991         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7992         if (sel.cpMin == sel.cpMax) {\r
7993           MessageBeep(MB_ICONEXCLAMATION);\r
7994           return 0;\r
7995         }\r
7996         SendMessage(hwnd, WM_COPY, 0, 0);\r
7997         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7998         SendMessage(hInput, WM_PASTE, 0, 0);\r
7999         SetFocus(hInput);\r
8000         return 0;\r
8001       }\r
8002     case IDM_Cut:\r
8003       SendMessage(hwnd, WM_CUT, 0, 0);\r
8004       return 0;\r
8005     case IDM_Paste:\r
8006       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8007       return 0;\r
8008     case IDM_Copy:\r
8009       SendMessage(hwnd, WM_COPY, 0, 0);\r
8010       return 0;\r
8011     default:\r
8012       {\r
8013         int i = LOWORD(wParam) - IDM_CommandX;\r
8014         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
8015             icsTextMenuEntry[i].command != NULL) {\r
8016           CommandX(hwnd, icsTextMenuEntry[i].command,\r
8017                    icsTextMenuEntry[i].getname,\r
8018                    icsTextMenuEntry[i].immediate);\r
8019           return 0;\r
8020         }\r
8021       }\r
8022       break;\r
8023     }\r
8024     break;\r
8025   }\r
8026   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
8027 }\r
8028 \r
8029 WNDPROC consoleInputWindowProc;\r
8030 \r
8031 LRESULT CALLBACK\r
8032 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8033 {\r
8034   char buf[MSG_SIZ];\r
8035   char *p;\r
8036   static BOOL sendNextChar = FALSE;\r
8037   static BOOL quoteNextChar = FALSE;\r
8038   InputSource *is = consoleInputSource;\r
8039   CHARFORMAT cf;\r
8040   CHARRANGE sel;\r
8041 \r
8042   switch (message) {\r
8043   case WM_CHAR:\r
8044     if (!appData.localLineEditing || sendNextChar) {\r
8045       is->buf[0] = (CHAR) wParam;\r
8046       is->count = 1;\r
8047       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8048       sendNextChar = FALSE;\r
8049       return 0;\r
8050     }\r
8051     if (quoteNextChar) {\r
8052       buf[0] = (char) wParam;\r
8053       buf[1] = NULLCHAR;\r
8054       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
8055       quoteNextChar = FALSE;\r
8056       return 0;\r
8057     }\r
8058     switch (wParam) {\r
8059     case '\r':   /* Enter key */\r
8060       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
8061       if (consoleEcho) SaveInHistory(is->buf);\r
8062       is->buf[is->count++] = '\n';\r
8063       is->buf[is->count] = NULLCHAR;\r
8064       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8065       if (consoleEcho) {\r
8066         ConsoleOutput(is->buf, is->count, TRUE);\r
8067       } else if (appData.localLineEditing) {\r
8068         ConsoleOutput("\n", 1, TRUE);\r
8069       }\r
8070       /* fall thru */\r
8071     case '\033': /* Escape key */\r
8072       SetWindowText(hwnd, "");\r
8073       cf.cbSize = sizeof(CHARFORMAT);\r
8074       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8075       if (consoleEcho) {\r
8076         cf.crTextColor = textAttribs[ColorNormal].color;\r
8077       } else {\r
8078         cf.crTextColor = COLOR_ECHOOFF;\r
8079       }\r
8080       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8081       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8082       return 0;\r
8083     case '\t':   /* Tab key */\r
8084       if (GetKeyState(VK_SHIFT) < 0) {\r
8085         /* shifted */\r
8086         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8087       } else {\r
8088         /* unshifted */\r
8089         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8090         if (buttonDesc[0].hwnd) {\r
8091           SetFocus(buttonDesc[0].hwnd);\r
8092         } else {\r
8093           SetFocus(hwndMain);\r
8094         }\r
8095       }\r
8096       return 0;\r
8097     case '\023': /* Ctrl+S */\r
8098       sendNextChar = TRUE;\r
8099       return 0;\r
8100     case '\021': /* Ctrl+Q */\r
8101       quoteNextChar = TRUE;\r
8102       return 0;\r
8103     JAWS_REPLAY\r
8104     default:\r
8105       break;\r
8106     }\r
8107     break;\r
8108   case WM_KEYDOWN:\r
8109     switch (wParam) {\r
8110     case VK_UP:\r
8111       GetWindowText(hwnd, buf, MSG_SIZ);\r
8112       p = PrevInHistory(buf);\r
8113       if (p != NULL) {\r
8114         SetWindowText(hwnd, p);\r
8115         sel.cpMin = 999999;\r
8116         sel.cpMax = 999999;\r
8117         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8118         return 0;\r
8119       }\r
8120       break;\r
8121     case VK_DOWN:\r
8122       p = NextInHistory();\r
8123       if (p != NULL) {\r
8124         SetWindowText(hwnd, p);\r
8125         sel.cpMin = 999999;\r
8126         sel.cpMax = 999999;\r
8127         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8128         return 0;\r
8129       }\r
8130       break;\r
8131     case VK_HOME:\r
8132     case VK_END:\r
8133       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8134       /* fall thru */\r
8135     case VK_PRIOR:\r
8136     case VK_NEXT:\r
8137       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8138       return 0;\r
8139     }\r
8140     break;\r
8141   case WM_MBUTTONDOWN:\r
8142     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8143       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8144     break;\r
8145   case WM_RBUTTONUP:\r
8146     if (GetKeyState(VK_SHIFT) & ~1) {\r
8147       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8148         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8149     } else {\r
8150       POINT pt;\r
8151       HMENU hmenu;\r
8152       hmenu = LoadMenu(hInst, "InputMenu");\r
8153       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8154       if (sel.cpMin == sel.cpMax) {\r
8155         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8156         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8157       }\r
8158       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8159         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8160       }\r
8161       pt.x = LOWORD(lParam);\r
8162       pt.y = HIWORD(lParam);\r
8163       MenuPopup(hwnd, pt, hmenu, -1);\r
8164     }\r
8165     return 0;\r
8166   case WM_COMMAND:\r
8167     switch (LOWORD(wParam)) { \r
8168     case IDM_Undo:\r
8169       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8170       return 0;\r
8171     case IDM_SelectAll:\r
8172       sel.cpMin = 0;\r
8173       sel.cpMax = -1; /*999999?*/\r
8174       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8175       return 0;\r
8176     case IDM_Cut:\r
8177       SendMessage(hwnd, WM_CUT, 0, 0);\r
8178       return 0;\r
8179     case IDM_Paste:\r
8180       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8181       return 0;\r
8182     case IDM_Copy:\r
8183       SendMessage(hwnd, WM_COPY, 0, 0);\r
8184       return 0;\r
8185     }\r
8186     break;\r
8187   }\r
8188   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8189 }\r
8190 \r
8191 #define CO_MAX  100000\r
8192 #define CO_TRIM   1000\r
8193 \r
8194 LRESULT CALLBACK\r
8195 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8196 {\r
8197   static SnapData sd;\r
8198   static HWND hText, hInput /*, hFocus*/;\r
8199 //  InputSource *is = consoleInputSource;\r
8200   RECT rect;\r
8201   static int sizeX, sizeY;\r
8202   int newSizeX, newSizeY;\r
8203   MINMAXINFO *mmi;\r
8204 \r
8205   switch (message) {\r
8206   case WM_INITDIALOG: /* message: initialize dialog box */\r
8207     hwndConsole = hDlg;\r
8208     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8209     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8210     SetFocus(hInput);\r
8211     consoleTextWindowProc = (WNDPROC)\r
8212       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8213     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8214     consoleInputWindowProc = (WNDPROC)\r
8215       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8216     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8217     Colorize(ColorNormal, TRUE);\r
8218     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8219     ChangedConsoleFont();\r
8220     GetClientRect(hDlg, &rect);\r
8221     sizeX = rect.right;\r
8222     sizeY = rect.bottom;\r
8223     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8224         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8225       WINDOWPLACEMENT wp;\r
8226       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8227       wp.length = sizeof(WINDOWPLACEMENT);\r
8228       wp.flags = 0;\r
8229       wp.showCmd = SW_SHOW;\r
8230       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8231       wp.rcNormalPosition.left = wpConsole.x;\r
8232       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8233       wp.rcNormalPosition.top = wpConsole.y;\r
8234       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8235       SetWindowPlacement(hDlg, &wp);\r
8236     }\r
8237 #if 1\r
8238    // [HGM] Chessknight's change 2004-07-13\r
8239    else { /* Determine Defaults */\r
8240        WINDOWPLACEMENT wp;\r
8241        wpConsole.x = winWidth + 1;\r
8242        wpConsole.y = boardY;\r
8243        wpConsole.width = screenWidth -  winWidth;\r
8244        wpConsole.height = winHeight;\r
8245        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8246        wp.length = sizeof(WINDOWPLACEMENT);\r
8247        wp.flags = 0;\r
8248        wp.showCmd = SW_SHOW;\r
8249        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8250        wp.rcNormalPosition.left = wpConsole.x;\r
8251        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8252        wp.rcNormalPosition.top = wpConsole.y;\r
8253        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8254        SetWindowPlacement(hDlg, &wp);\r
8255     }\r
8256 #endif\r
8257     return FALSE;\r
8258 \r
8259   case WM_SETFOCUS:\r
8260     SetFocus(hInput);\r
8261     return 0;\r
8262 \r
8263   case WM_CLOSE:\r
8264     ExitEvent(0);\r
8265     /* not reached */\r
8266     break;\r
8267 \r
8268   case WM_SIZE:\r
8269     if (IsIconic(hDlg)) break;\r
8270     newSizeX = LOWORD(lParam);\r
8271     newSizeY = HIWORD(lParam);\r
8272     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8273       RECT rectText, rectInput;\r
8274       POINT pt;\r
8275       int newTextHeight, newTextWidth;\r
8276       GetWindowRect(hText, &rectText);\r
8277       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8278       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8279       if (newTextHeight < 0) {\r
8280         newSizeY += -newTextHeight;\r
8281         newTextHeight = 0;\r
8282       }\r
8283       SetWindowPos(hText, NULL, 0, 0,\r
8284         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8285       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8286       pt.x = rectInput.left;\r
8287       pt.y = rectInput.top + newSizeY - sizeY;\r
8288       ScreenToClient(hDlg, &pt);\r
8289       SetWindowPos(hInput, NULL, \r
8290         pt.x, pt.y, /* needs client coords */   \r
8291         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8292         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8293     }\r
8294     sizeX = newSizeX;\r
8295     sizeY = newSizeY;\r
8296     break;\r
8297 \r
8298   case WM_GETMINMAXINFO:\r
8299     /* Prevent resizing window too small */\r
8300     mmi = (MINMAXINFO *) lParam;\r
8301     mmi->ptMinTrackSize.x = 100;\r
8302     mmi->ptMinTrackSize.y = 100;\r
8303     break;\r
8304 \r
8305   /* [AS] Snapping */\r
8306   case WM_ENTERSIZEMOVE:\r
8307     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8308 \r
8309   case WM_SIZING:\r
8310     return OnSizing( &sd, hDlg, wParam, lParam );\r
8311 \r
8312   case WM_MOVING:\r
8313     return OnMoving( &sd, hDlg, wParam, lParam );\r
8314 \r
8315   case WM_EXITSIZEMOVE:\r
8316     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8317   }\r
8318 \r
8319   return DefWindowProc(hDlg, message, wParam, lParam);\r
8320 }\r
8321 \r
8322 \r
8323 VOID\r
8324 ConsoleCreate()\r
8325 {\r
8326   HWND hCons;\r
8327   if (hwndConsole) return;\r
8328   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8329   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8330 }\r
8331 \r
8332 \r
8333 VOID\r
8334 ConsoleOutput(char* data, int length, int forceVisible)\r
8335 {\r
8336   HWND hText;\r
8337   int trim, exlen;\r
8338   char *p, *q;\r
8339   char buf[CO_MAX+1];\r
8340   POINT pEnd;\r
8341   RECT rect;\r
8342   static int delayLF = 0;\r
8343   CHARRANGE savesel, sel;\r
8344 \r
8345   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8346   p = data;\r
8347   q = buf;\r
8348   if (delayLF) {\r
8349     *q++ = '\r';\r
8350     *q++ = '\n';\r
8351     delayLF = 0;\r
8352   }\r
8353   while (length--) {\r
8354     if (*p == '\n') {\r
8355       if (*++p) {\r
8356         *q++ = '\r';\r
8357         *q++ = '\n';\r
8358       } else {\r
8359         delayLF = 1;\r
8360       }\r
8361     } else if (*p == '\007') {\r
8362        MyPlaySound(&sounds[(int)SoundBell]);\r
8363        p++;\r
8364     } else {\r
8365       *q++ = *p++;\r
8366     }\r
8367   }\r
8368   *q = NULLCHAR;\r
8369   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8370   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8371   /* Save current selection */\r
8372   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8373   exlen = GetWindowTextLength(hText);\r
8374   /* Find out whether current end of text is visible */\r
8375   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8376   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8377   /* Trim existing text if it's too long */\r
8378   if (exlen + (q - buf) > CO_MAX) {\r
8379     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8380     sel.cpMin = 0;\r
8381     sel.cpMax = trim;\r
8382     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8383     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8384     exlen -= trim;\r
8385     savesel.cpMin -= trim;\r
8386     savesel.cpMax -= trim;\r
8387     if (exlen < 0) exlen = 0;\r
8388     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8389     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8390   }\r
8391   /* Append the new text */\r
8392   sel.cpMin = exlen;\r
8393   sel.cpMax = exlen;\r
8394   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8395   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8396   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8397   if (forceVisible || exlen == 0 ||\r
8398       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8399        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8400     /* Scroll to make new end of text visible if old end of text\r
8401        was visible or new text is an echo of user typein */\r
8402     sel.cpMin = 9999999;\r
8403     sel.cpMax = 9999999;\r
8404     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8405     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8406     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8407     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8408   }\r
8409   if (savesel.cpMax == exlen || forceVisible) {\r
8410     /* Move insert point to new end of text if it was at the old\r
8411        end of text or if the new text is an echo of user typein */\r
8412     sel.cpMin = 9999999;\r
8413     sel.cpMax = 9999999;\r
8414     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8415   } else {\r
8416     /* Restore previous selection */\r
8417     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8418   }\r
8419   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8420 }\r
8421 \r
8422 /*---------*/\r
8423 \r
8424 \r
8425 void\r
8426 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8427 {\r
8428   char buf[100];\r
8429   char *str;\r
8430   COLORREF oldFg, oldBg;\r
8431   HFONT oldFont;\r
8432   RECT rect;\r
8433 \r
8434   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8435 \r
8436   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8437   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8438   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8439 \r
8440   rect.left = x;\r
8441   rect.right = x + squareSize;\r
8442   rect.top  = y;\r
8443   rect.bottom = y + squareSize;\r
8444   str = buf;\r
8445 \r
8446   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8447                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8448              y, ETO_CLIPPED|ETO_OPAQUE,\r
8449              &rect, str, strlen(str), NULL);\r
8450 \r
8451   (void) SetTextColor(hdc, oldFg);\r
8452   (void) SetBkColor(hdc, oldBg);\r
8453   (void) SelectObject(hdc, oldFont);\r
8454 }\r
8455 \r
8456 void\r
8457 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8458               RECT *rect, char *color, char *flagFell)\r
8459 {\r
8460   char buf[100];\r
8461   char *str;\r
8462   COLORREF oldFg, oldBg;\r
8463   HFONT oldFont;\r
8464 \r
8465   if (appData.clockMode) {\r
8466     if (tinyLayout)\r
8467       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8468     else\r
8469       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8470     str = buf;\r
8471   } else {\r
8472     str = color;\r
8473   }\r
8474 \r
8475   if (highlight) {\r
8476     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8477     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8478   } else {\r
8479     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8480     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8481   }\r
8482   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8483 \r
8484   JAWS_SILENCE\r
8485 \r
8486   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8487              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8488              rect, str, strlen(str), NULL);\r
8489   if(logoHeight > 0 && appData.clockMode) {\r
8490       RECT r;\r
8491       sprintf(buf, "%s %s", buf+7, flagFell);\r
8492       r.top = rect->top + logoHeight/2;\r
8493       r.left = rect->left;\r
8494       r.right = rect->right;\r
8495       r.bottom = rect->bottom;\r
8496       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8497                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8498                  &r, str, strlen(str), NULL);\r
8499   }\r
8500   (void) SetTextColor(hdc, oldFg);\r
8501   (void) SetBkColor(hdc, oldBg);\r
8502   (void) SelectObject(hdc, oldFont);\r
8503 }\r
8504 \r
8505 \r
8506 int\r
8507 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8508            OVERLAPPED *ovl)\r
8509 {\r
8510   int ok, err;\r
8511 \r
8512   /* [AS]  */\r
8513   if( count <= 0 ) {\r
8514     if (appData.debugMode) {\r
8515       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8516     }\r
8517 \r
8518     return ERROR_INVALID_USER_BUFFER;\r
8519   }\r
8520 \r
8521   ResetEvent(ovl->hEvent);\r
8522   ovl->Offset = ovl->OffsetHigh = 0;\r
8523   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8524   if (ok) {\r
8525     err = NO_ERROR;\r
8526   } else {\r
8527     err = GetLastError();\r
8528     if (err == ERROR_IO_PENDING) {\r
8529       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8530       if (ok)\r
8531         err = NO_ERROR;\r
8532       else\r
8533         err = GetLastError();\r
8534     }\r
8535   }\r
8536   return err;\r
8537 }\r
8538 \r
8539 int\r
8540 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8541             OVERLAPPED *ovl)\r
8542 {\r
8543   int ok, err;\r
8544 \r
8545   ResetEvent(ovl->hEvent);\r
8546   ovl->Offset = ovl->OffsetHigh = 0;\r
8547   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8548   if (ok) {\r
8549     err = NO_ERROR;\r
8550   } else {\r
8551     err = GetLastError();\r
8552     if (err == ERROR_IO_PENDING) {\r
8553       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8554       if (ok)\r
8555         err = NO_ERROR;\r
8556       else\r
8557         err = GetLastError();\r
8558     }\r
8559   }\r
8560   return err;\r
8561 }\r
8562 \r
8563 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8564 void CheckForInputBufferFull( InputSource * is )\r
8565 {\r
8566     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8567         /* Look for end of line */\r
8568         char * p = is->buf;\r
8569         \r
8570         while( p < is->next && *p != '\n' ) {\r
8571             p++;\r
8572         }\r
8573 \r
8574         if( p >= is->next ) {\r
8575             if (appData.debugMode) {\r
8576                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8577             }\r
8578 \r
8579             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8580             is->count = (DWORD) -1;\r
8581             is->next = is->buf;\r
8582         }\r
8583     }\r
8584 }\r
8585 \r
8586 DWORD\r
8587 InputThread(LPVOID arg)\r
8588 {\r
8589   InputSource *is;\r
8590   OVERLAPPED ovl;\r
8591 \r
8592   is = (InputSource *) arg;\r
8593   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8594   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8595   while (is->hThread != NULL) {\r
8596     is->error = DoReadFile(is->hFile, is->next,\r
8597                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8598                            &is->count, &ovl);\r
8599     if (is->error == NO_ERROR) {\r
8600       is->next += is->count;\r
8601     } else {\r
8602       if (is->error == ERROR_BROKEN_PIPE) {\r
8603         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8604         is->count = 0;\r
8605       } else {\r
8606         is->count = (DWORD) -1;\r
8607         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8608         break; \r
8609       }\r
8610     }\r
8611 \r
8612     CheckForInputBufferFull( is );\r
8613 \r
8614     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8615 \r
8616     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8617 \r
8618     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8619   }\r
8620 \r
8621   CloseHandle(ovl.hEvent);\r
8622   CloseHandle(is->hFile);\r
8623 \r
8624   if (appData.debugMode) {\r
8625     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8626   }\r
8627 \r
8628   return 0;\r
8629 }\r
8630 \r
8631 \r
8632 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8633 DWORD\r
8634 NonOvlInputThread(LPVOID arg)\r
8635 {\r
8636   InputSource *is;\r
8637   char *p, *q;\r
8638   int i;\r
8639   char prev;\r
8640 \r
8641   is = (InputSource *) arg;\r
8642   while (is->hThread != NULL) {\r
8643     is->error = ReadFile(is->hFile, is->next,\r
8644                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8645                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8646     if (is->error == NO_ERROR) {\r
8647       /* Change CRLF to LF */\r
8648       if (is->next > is->buf) {\r
8649         p = is->next - 1;\r
8650         i = is->count + 1;\r
8651       } else {\r
8652         p = is->next;\r
8653         i = is->count;\r
8654       }\r
8655       q = p;\r
8656       prev = NULLCHAR;\r
8657       while (i > 0) {\r
8658         if (prev == '\r' && *p == '\n') {\r
8659           *(q-1) = '\n';\r
8660           is->count--;\r
8661         } else { \r
8662           *q++ = *p;\r
8663         }\r
8664         prev = *p++;\r
8665         i--;\r
8666       }\r
8667       *q = NULLCHAR;\r
8668       is->next = q;\r
8669     } else {\r
8670       if (is->error == ERROR_BROKEN_PIPE) {\r
8671         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8672         is->count = 0; \r
8673       } else {\r
8674         is->count = (DWORD) -1;\r
8675       }\r
8676     }\r
8677 \r
8678     CheckForInputBufferFull( is );\r
8679 \r
8680     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8681 \r
8682     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8683 \r
8684     if (is->count < 0) break;  /* Quit on error */\r
8685   }\r
8686   CloseHandle(is->hFile);\r
8687   return 0;\r
8688 }\r
8689 \r
8690 DWORD\r
8691 SocketInputThread(LPVOID arg)\r
8692 {\r
8693   InputSource *is;\r
8694 \r
8695   is = (InputSource *) arg;\r
8696   while (is->hThread != NULL) {\r
8697     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8698     if ((int)is->count == SOCKET_ERROR) {\r
8699       is->count = (DWORD) -1;\r
8700       is->error = WSAGetLastError();\r
8701     } else {\r
8702       is->error = NO_ERROR;\r
8703       is->next += is->count;\r
8704       if (is->count == 0 && is->second == is) {\r
8705         /* End of file on stderr; quit with no message */\r
8706         break;\r
8707       }\r
8708     }\r
8709     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8710 \r
8711     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8712 \r
8713     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8714   }\r
8715   return 0;\r
8716 }\r
8717 \r
8718 VOID\r
8719 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8720 {\r
8721   InputSource *is;\r
8722 \r
8723   is = (InputSource *) lParam;\r
8724   if (is->lineByLine) {\r
8725     /* Feed in lines one by one */\r
8726     char *p = is->buf;\r
8727     char *q = p;\r
8728     while (q < is->next) {\r
8729       if (*q++ == '\n') {\r
8730         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8731         p = q;\r
8732       }\r
8733     }\r
8734     \r
8735     /* Move any partial line to the start of the buffer */\r
8736     q = is->buf;\r
8737     while (p < is->next) {\r
8738       *q++ = *p++;\r
8739     }\r
8740     is->next = q;\r
8741 \r
8742     if (is->error != NO_ERROR || is->count == 0) {\r
8743       /* Notify backend of the error.  Note: If there was a partial\r
8744          line at the end, it is not flushed through. */\r
8745       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8746     }\r
8747   } else {\r
8748     /* Feed in the whole chunk of input at once */\r
8749     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8750     is->next = is->buf;\r
8751   }\r
8752 }\r
8753 \r
8754 /*---------------------------------------------------------------------------*\\r
8755  *\r
8756  *  Menu enables. Used when setting various modes.\r
8757  *\r
8758 \*---------------------------------------------------------------------------*/\r
8759 \r
8760 typedef struct {\r
8761   int item;\r
8762   int flags;\r
8763 } Enables;\r
8764 \r
8765 VOID\r
8766 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8767 {\r
8768   while (enab->item > 0) {\r
8769     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8770     enab++;\r
8771   }\r
8772 }\r
8773 \r
8774 Enables gnuEnables[] = {\r
8775   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8776   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8777   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8778   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8779   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8780   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8781   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8782   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8783   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8784   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8785   { -1, -1 }\r
8786 };\r
8787 \r
8788 Enables icsEnables[] = {\r
8789   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8790   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8791   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8792   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8793   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8794   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8795   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8796   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8797   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8798   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8799   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8800   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8801   { -1, -1 }\r
8802 };\r
8803 \r
8804 #ifdef ZIPPY\r
8805 Enables zippyEnables[] = {\r
8806   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8807   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8808   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8809   { -1, -1 }\r
8810 };\r
8811 #endif\r
8812 \r
8813 Enables ncpEnables[] = {\r
8814   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8815   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8816   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8817   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8818   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8819   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8820   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8821   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8822   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8823   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8824   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8825   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8826   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8827   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8828   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8829   { -1, -1 }\r
8830 };\r
8831 \r
8832 Enables trainingOnEnables[] = {\r
8833   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8834   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8835   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8836   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8837   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8838   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8839   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8840   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8841   { -1, -1 }\r
8842 };\r
8843 \r
8844 Enables trainingOffEnables[] = {\r
8845   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8846   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8847   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8848   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8849   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8850   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8851   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8852   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8853   { -1, -1 }\r
8854 };\r
8855 \r
8856 /* These modify either ncpEnables or gnuEnables */\r
8857 Enables cmailEnables[] = {\r
8858   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8859   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8860   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8861   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8862   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8863   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8864   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8865   { -1, -1 }\r
8866 };\r
8867 \r
8868 Enables machineThinkingEnables[] = {\r
8869   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8870   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8871   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8872   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8873   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8874   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8875   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8876   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8877   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8878   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8879   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8880   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8881   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8882   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8883   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8884   { -1, -1 }\r
8885 };\r
8886 \r
8887 Enables userThinkingEnables[] = {\r
8888   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8889   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8890   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8891   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8892   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8893   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8894   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8895   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8896   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8897   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8898   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8899   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8900   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8901   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8902   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8903   { -1, -1 }\r
8904 };\r
8905 \r
8906 /*---------------------------------------------------------------------------*\\r
8907  *\r
8908  *  Front-end interface functions exported by XBoard.\r
8909  *  Functions appear in same order as prototypes in frontend.h.\r
8910  * \r
8911 \*---------------------------------------------------------------------------*/\r
8912 VOID\r
8913 ModeHighlight()\r
8914 {\r
8915   static UINT prevChecked = 0;\r
8916   static int prevPausing = 0;\r
8917   UINT nowChecked;\r
8918 \r
8919   if (pausing != prevPausing) {\r
8920     prevPausing = pausing;\r
8921     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8922                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8923     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8924   }\r
8925 \r
8926   switch (gameMode) {\r
8927   case BeginningOfGame:\r
8928     if (appData.icsActive)\r
8929       nowChecked = IDM_IcsClient;\r
8930     else if (appData.noChessProgram)\r
8931       nowChecked = IDM_EditGame;\r
8932     else\r
8933       nowChecked = IDM_MachineBlack;\r
8934     break;\r
8935   case MachinePlaysBlack:\r
8936     nowChecked = IDM_MachineBlack;\r
8937     break;\r
8938   case MachinePlaysWhite:\r
8939     nowChecked = IDM_MachineWhite;\r
8940     break;\r
8941   case TwoMachinesPlay:\r
8942     nowChecked = IDM_TwoMachines;\r
8943     break;\r
8944   case AnalyzeMode:\r
8945     nowChecked = IDM_AnalysisMode;\r
8946     break;\r
8947   case AnalyzeFile:\r
8948     nowChecked = IDM_AnalyzeFile;\r
8949     break;\r
8950   case EditGame:\r
8951     nowChecked = IDM_EditGame;\r
8952     break;\r
8953   case PlayFromGameFile:\r
8954     nowChecked = IDM_LoadGame;\r
8955     break;\r
8956   case EditPosition:\r
8957     nowChecked = IDM_EditPosition;\r
8958     break;\r
8959   case Training:\r
8960     nowChecked = IDM_Training;\r
8961     break;\r
8962   case IcsPlayingWhite:\r
8963   case IcsPlayingBlack:\r
8964   case IcsObserving:\r
8965   case IcsIdle:\r
8966     nowChecked = IDM_IcsClient;\r
8967     break;\r
8968   default:\r
8969   case EndOfGame:\r
8970     nowChecked = 0;\r
8971     break;\r
8972   }\r
8973   if (prevChecked != 0)\r
8974     (void) CheckMenuItem(GetMenu(hwndMain),\r
8975                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8976   if (nowChecked != 0)\r
8977     (void) CheckMenuItem(GetMenu(hwndMain),\r
8978                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8979 \r
8980   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8981     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8982                           MF_BYCOMMAND|MF_ENABLED);\r
8983   } else {\r
8984     (void) EnableMenuItem(GetMenu(hwndMain), \r
8985                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8986   }\r
8987 \r
8988   prevChecked = nowChecked;\r
8989 \r
8990   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8991   if (appData.icsActive) {\r
8992        if (appData.icsEngineAnalyze) {\r
8993                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8994                        MF_BYCOMMAND|MF_CHECKED);\r
8995        } else {\r
8996                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8997                        MF_BYCOMMAND|MF_UNCHECKED);\r
8998        }\r
8999   }\r
9000 }\r
9001 \r
9002 VOID\r
9003 SetICSMode()\r
9004 {\r
9005   HMENU hmenu = GetMenu(hwndMain);\r
9006   SetMenuEnables(hmenu, icsEnables);\r
9007   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
9008     MF_BYPOSITION|MF_ENABLED);\r
9009 #ifdef ZIPPY\r
9010   if (appData.zippyPlay) {\r
9011     SetMenuEnables(hmenu, zippyEnables);\r
9012     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
9013          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9014           MF_BYCOMMAND|MF_ENABLED);\r
9015   }\r
9016 #endif\r
9017 }\r
9018 \r
9019 VOID\r
9020 SetGNUMode()\r
9021 {\r
9022   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
9023 }\r
9024 \r
9025 VOID\r
9026 SetNCPMode()\r
9027 {\r
9028   HMENU hmenu = GetMenu(hwndMain);\r
9029   SetMenuEnables(hmenu, ncpEnables);\r
9030   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
9031     MF_BYPOSITION|MF_GRAYED);\r
9032     DrawMenuBar(hwndMain);\r
9033 }\r
9034 \r
9035 VOID\r
9036 SetCmailMode()\r
9037 {\r
9038   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9039 }\r
9040 \r
9041 VOID \r
9042 SetTrainingModeOn()\r
9043 {\r
9044   int i;\r
9045   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9046   for (i = 0; i < N_BUTTONS; i++) {\r
9047     if (buttonDesc[i].hwnd != NULL)\r
9048       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9049   }\r
9050   CommentPopDown();\r
9051 }\r
9052 \r
9053 VOID SetTrainingModeOff()\r
9054 {\r
9055   int i;\r
9056   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9057   for (i = 0; i < N_BUTTONS; i++) {\r
9058     if (buttonDesc[i].hwnd != NULL)\r
9059       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9060   }\r
9061 }\r
9062 \r
9063 \r
9064 VOID\r
9065 SetUserThinkingEnables()\r
9066 {\r
9067   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9068 }\r
9069 \r
9070 VOID\r
9071 SetMachineThinkingEnables()\r
9072 {\r
9073   HMENU hMenu = GetMenu(hwndMain);\r
9074   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9075 \r
9076   SetMenuEnables(hMenu, machineThinkingEnables);\r
9077 \r
9078   if (gameMode == MachinePlaysBlack) {\r
9079     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9080   } else if (gameMode == MachinePlaysWhite) {\r
9081     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9082   } else if (gameMode == TwoMachinesPlay) {\r
9083     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9084   }\r
9085 }\r
9086 \r
9087 \r
9088 VOID\r
9089 DisplayTitle(char *str)\r
9090 {\r
9091   char title[MSG_SIZ], *host;\r
9092   if (str[0] != NULLCHAR) {\r
9093     strcpy(title, str);\r
9094   } else if (appData.icsActive) {\r
9095     if (appData.icsCommPort[0] != NULLCHAR)\r
9096       host = "ICS";\r
9097     else \r
9098       host = appData.icsHost;\r
9099     sprintf(title, "%s: %s", szTitle, host);\r
9100   } else if (appData.noChessProgram) {\r
9101     strcpy(title, szTitle);\r
9102   } else {\r
9103     strcpy(title, szTitle);\r
9104     strcat(title, ": ");\r
9105     strcat(title, first.tidy);\r
9106   }\r
9107   SetWindowText(hwndMain, title);\r
9108 }\r
9109 \r
9110 \r
9111 VOID\r
9112 DisplayMessage(char *str1, char *str2)\r
9113 {\r
9114   HDC hdc;\r
9115   HFONT oldFont;\r
9116   int remain = MESSAGE_TEXT_MAX - 1;\r
9117   int len;\r
9118 \r
9119   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9120   messageText[0] = NULLCHAR;\r
9121   if (*str1) {\r
9122     len = strlen(str1);\r
9123     if (len > remain) len = remain;\r
9124     strncpy(messageText, str1, len);\r
9125     messageText[len] = NULLCHAR;\r
9126     remain -= len;\r
9127   }\r
9128   if (*str2 && remain >= 2) {\r
9129     if (*str1) {\r
9130       strcat(messageText, "  ");\r
9131       remain -= 2;\r
9132     }\r
9133     len = strlen(str2);\r
9134     if (len > remain) len = remain;\r
9135     strncat(messageText, str2, len);\r
9136   }\r
9137   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9138 \r
9139   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9140 \r
9141   SAYMACHINEMOVE();\r
9142 \r
9143   hdc = GetDC(hwndMain);\r
9144   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9145   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9146              &messageRect, messageText, strlen(messageText), NULL);\r
9147   (void) SelectObject(hdc, oldFont);\r
9148   (void) ReleaseDC(hwndMain, hdc);\r
9149 }\r
9150 \r
9151 VOID\r
9152 DisplayError(char *str, int error)\r
9153 {\r
9154   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9155   int len;\r
9156 \r
9157   if (error == 0) {\r
9158     strcpy(buf, str);\r
9159   } else {\r
9160     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9161                         NULL, error, LANG_NEUTRAL,\r
9162                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9163     if (len > 0) {\r
9164       sprintf(buf, "%s:\n%s", str, buf2);\r
9165     } else {\r
9166       ErrorMap *em = errmap;\r
9167       while (em->err != 0 && em->err != error) em++;\r
9168       if (em->err != 0) {\r
9169         sprintf(buf, "%s:\n%s", str, em->msg);\r
9170       } else {\r
9171         sprintf(buf, "%s:\nError code %d", str, error);\r
9172       }\r
9173     }\r
9174   }\r
9175   \r
9176   ErrorPopUp("Error", buf);\r
9177 }\r
9178 \r
9179 \r
9180 VOID\r
9181 DisplayMoveError(char *str)\r
9182 {\r
9183   fromX = fromY = -1;\r
9184   ClearHighlights();\r
9185   DrawPosition(FALSE, NULL);\r
9186   if (appData.popupMoveErrors) {\r
9187     ErrorPopUp("Error", str);\r
9188   } else {\r
9189     DisplayMessage(str, "");\r
9190     moveErrorMessageUp = TRUE;\r
9191   }\r
9192 }\r
9193 \r
9194 VOID\r
9195 DisplayFatalError(char *str, int error, int exitStatus)\r
9196 {\r
9197   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9198   int len;\r
9199   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9200 \r
9201   if (error != 0) {\r
9202     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9203                         NULL, error, LANG_NEUTRAL,\r
9204                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9205     if (len > 0) {\r
9206       sprintf(buf, "%s:\n%s", str, buf2);\r
9207     } else {\r
9208       ErrorMap *em = errmap;\r
9209       while (em->err != 0 && em->err != error) em++;\r
9210       if (em->err != 0) {\r
9211         sprintf(buf, "%s:\n%s", str, em->msg);\r
9212       } else {\r
9213         sprintf(buf, "%s:\nError code %d", str, error);\r
9214       }\r
9215     }\r
9216     str = buf;\r
9217   }\r
9218   if (appData.debugMode) {\r
9219     fprintf(debugFP, "%s: %s\n", label, str);\r
9220   }\r
9221   if (appData.popupExitMessage) {\r
9222     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9223                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9224   }\r
9225   ExitEvent(exitStatus);\r
9226 }\r
9227 \r
9228 \r
9229 VOID\r
9230 DisplayInformation(char *str)\r
9231 {\r
9232   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9233 }\r
9234 \r
9235 \r
9236 VOID\r
9237 DisplayNote(char *str)\r
9238 {\r
9239   ErrorPopUp("Note", str);\r
9240 }\r
9241 \r
9242 \r
9243 typedef struct {\r
9244   char *title, *question, *replyPrefix;\r
9245   ProcRef pr;\r
9246 } QuestionParams;\r
9247 \r
9248 LRESULT CALLBACK\r
9249 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9250 {\r
9251   static QuestionParams *qp;\r
9252   char reply[MSG_SIZ];\r
9253   int len, err;\r
9254 \r
9255   switch (message) {\r
9256   case WM_INITDIALOG:\r
9257     qp = (QuestionParams *) lParam;\r
9258     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9259     SetWindowText(hDlg, qp->title);\r
9260     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9261     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9262     return FALSE;\r
9263 \r
9264   case WM_COMMAND:\r
9265     switch (LOWORD(wParam)) {\r
9266     case IDOK:\r
9267       strcpy(reply, qp->replyPrefix);\r
9268       if (*reply) strcat(reply, " ");\r
9269       len = strlen(reply);\r
9270       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9271       strcat(reply, "\n");\r
9272       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9273       EndDialog(hDlg, TRUE);\r
9274       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9275       return TRUE;\r
9276     case IDCANCEL:\r
9277       EndDialog(hDlg, FALSE);\r
9278       return TRUE;\r
9279     default:\r
9280       break;\r
9281     }\r
9282     break;\r
9283   }\r
9284   return FALSE;\r
9285 }\r
9286 \r
9287 VOID\r
9288 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9289 {\r
9290     QuestionParams qp;\r
9291     FARPROC lpProc;\r
9292     \r
9293     qp.title = title;\r
9294     qp.question = question;\r
9295     qp.replyPrefix = replyPrefix;\r
9296     qp.pr = pr;\r
9297     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9298     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9299       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9300     FreeProcInstance(lpProc);\r
9301 }\r
9302 \r
9303 /* [AS] Pick FRC position */\r
9304 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9305 {\r
9306     static int * lpIndexFRC;\r
9307     BOOL index_is_ok;\r
9308     char buf[16];\r
9309 \r
9310     switch( message )\r
9311     {\r
9312     case WM_INITDIALOG:\r
9313         lpIndexFRC = (int *) lParam;\r
9314 \r
9315         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9316 \r
9317         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9318         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9319         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9320         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9321 \r
9322         break;\r
9323 \r
9324     case WM_COMMAND:\r
9325         switch( LOWORD(wParam) ) {\r
9326         case IDOK:\r
9327             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9328             EndDialog( hDlg, 0 );\r
9329             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9330             return TRUE;\r
9331         case IDCANCEL:\r
9332             EndDialog( hDlg, 1 );   \r
9333             return TRUE;\r
9334         case IDC_NFG_Edit:\r
9335             if( HIWORD(wParam) == EN_CHANGE ) {\r
9336                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9337 \r
9338                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9339             }\r
9340             return TRUE;\r
9341         case IDC_NFG_Random:\r
9342             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9343             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9344             return TRUE;\r
9345         }\r
9346 \r
9347         break;\r
9348     }\r
9349 \r
9350     return FALSE;\r
9351 }\r
9352 \r
9353 int NewGameFRC()\r
9354 {\r
9355     int result;\r
9356     int index = appData.defaultFrcPosition;\r
9357     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9358 \r
9359     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9360 \r
9361     if( result == 0 ) {\r
9362         appData.defaultFrcPosition = index;\r
9363     }\r
9364 \r
9365     return result;\r
9366 }\r
9367 \r
9368 /* [AS] Game list options */\r
9369 typedef struct {\r
9370     char id;\r
9371     char * name;\r
9372 } GLT_Item;\r
9373 \r
9374 static GLT_Item GLT_ItemInfo[] = {\r
9375     { GLT_EVENT,      "Event" },\r
9376     { GLT_SITE,       "Site" },\r
9377     { GLT_DATE,       "Date" },\r
9378     { GLT_ROUND,      "Round" },\r
9379     { GLT_PLAYERS,    "Players" },\r
9380     { GLT_RESULT,     "Result" },\r
9381     { GLT_WHITE_ELO,  "White Rating" },\r
9382     { GLT_BLACK_ELO,  "Black Rating" },\r
9383     { GLT_TIME_CONTROL,"Time Control" },\r
9384     { GLT_VARIANT,    "Variant" },\r
9385     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9386     { 0, 0 }\r
9387 };\r
9388 \r
9389 const char * GLT_FindItem( char id )\r
9390 {\r
9391     const char * result = 0;\r
9392 \r
9393     GLT_Item * list = GLT_ItemInfo;\r
9394 \r
9395     while( list->id != 0 ) {\r
9396         if( list->id == id ) {\r
9397             result = list->name;\r
9398             break;\r
9399         }\r
9400 \r
9401         list++;\r
9402     }\r
9403 \r
9404     return result;\r
9405 }\r
9406 \r
9407 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9408 {\r
9409     const char * name = GLT_FindItem( id );\r
9410 \r
9411     if( name != 0 ) {\r
9412         if( index >= 0 ) {\r
9413             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9414         }\r
9415         else {\r
9416             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9417         }\r
9418     }\r
9419 }\r
9420 \r
9421 void GLT_TagsToList( HWND hDlg, char * tags )\r
9422 {\r
9423     char * pc = tags;\r
9424 \r
9425     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9426 \r
9427     while( *pc ) {\r
9428         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9429         pc++;\r
9430     }\r
9431 \r
9432     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9433 \r
9434     pc = GLT_ALL_TAGS;\r
9435 \r
9436     while( *pc ) {\r
9437         if( strchr( tags, *pc ) == 0 ) {\r
9438             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9439         }\r
9440         pc++;\r
9441     }\r
9442 \r
9443     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9444 }\r
9445 \r
9446 char GLT_ListItemToTag( HWND hDlg, int index )\r
9447 {\r
9448     char result = '\0';\r
9449     char name[128];\r
9450 \r
9451     GLT_Item * list = GLT_ItemInfo;\r
9452 \r
9453     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9454         while( list->id != 0 ) {\r
9455             if( strcmp( list->name, name ) == 0 ) {\r
9456                 result = list->id;\r
9457                 break;\r
9458             }\r
9459 \r
9460             list++;\r
9461         }\r
9462     }\r
9463 \r
9464     return result;\r
9465 }\r
9466 \r
9467 void GLT_MoveSelection( HWND hDlg, int delta )\r
9468 {\r
9469     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9470     int idx2 = idx1 + delta;\r
9471     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9472 \r
9473     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9474         char buf[128];\r
9475 \r
9476         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9477         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9478         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9479         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9480     }\r
9481 }\r
9482 \r
9483 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9484 {\r
9485     static char glt[64];\r
9486     static char * lpUserGLT;\r
9487 \r
9488     switch( message )\r
9489     {\r
9490     case WM_INITDIALOG:\r
9491         lpUserGLT = (char *) lParam;\r
9492         \r
9493         strcpy( glt, lpUserGLT );\r
9494 \r
9495         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9496 \r
9497         /* Initialize list */\r
9498         GLT_TagsToList( hDlg, glt );\r
9499 \r
9500         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9501 \r
9502         break;\r
9503 \r
9504     case WM_COMMAND:\r
9505         switch( LOWORD(wParam) ) {\r
9506         case IDOK:\r
9507             {\r
9508                 char * pc = lpUserGLT;\r
9509                 int idx = 0;\r
9510 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9511                 char id;\r
9512 \r
9513                 do {\r
9514                     id = GLT_ListItemToTag( hDlg, idx );\r
9515 \r
9516                     *pc++ = id;\r
9517                     idx++;\r
9518                 } while( id != '\0' );\r
9519             }\r
9520             EndDialog( hDlg, 0 );\r
9521             return TRUE;\r
9522         case IDCANCEL:\r
9523             EndDialog( hDlg, 1 );\r
9524             return TRUE;\r
9525 \r
9526         case IDC_GLT_Default:\r
9527             strcpy( glt, GLT_DEFAULT_TAGS );\r
9528             GLT_TagsToList( hDlg, glt );\r
9529             return TRUE;\r
9530 \r
9531         case IDC_GLT_Restore:\r
9532             strcpy( glt, lpUserGLT );\r
9533             GLT_TagsToList( hDlg, glt );\r
9534             return TRUE;\r
9535 \r
9536         case IDC_GLT_Up:\r
9537             GLT_MoveSelection( hDlg, -1 );\r
9538             return TRUE;\r
9539 \r
9540         case IDC_GLT_Down:\r
9541             GLT_MoveSelection( hDlg, +1 );\r
9542             return TRUE;\r
9543         }\r
9544 \r
9545         break;\r
9546     }\r
9547 \r
9548     return FALSE;\r
9549 }\r
9550 \r
9551 int GameListOptions()\r
9552 {\r
9553     char glt[64];\r
9554     int result;\r
9555     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9556 \r
9557     strcpy( glt, appData.gameListTags );\r
9558 \r
9559     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9560 \r
9561     if( result == 0 ) {\r
9562         /* [AS] Memory leak here! */\r
9563         appData.gameListTags = strdup( glt ); \r
9564     }\r
9565 \r
9566     return result;\r
9567 }\r
9568 \r
9569 \r
9570 VOID\r
9571 DisplayIcsInteractionTitle(char *str)\r
9572 {\r
9573   char consoleTitle[MSG_SIZ];\r
9574 \r
9575   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9576   SetWindowText(hwndConsole, consoleTitle);\r
9577 }\r
9578 \r
9579 void\r
9580 DrawPosition(int fullRedraw, Board board)\r
9581 {\r
9582   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9583 }\r
9584 \r
9585 \r
9586 VOID\r
9587 ResetFrontEnd()\r
9588 {\r
9589   fromX = fromY = -1;\r
9590   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9591     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9592     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9593     dragInfo.lastpos = dragInfo.pos;\r
9594     dragInfo.start.x = dragInfo.start.y = -1;\r
9595     dragInfo.from = dragInfo.start;\r
9596     ReleaseCapture();\r
9597     DrawPosition(TRUE, NULL);\r
9598   }\r
9599 }\r
9600 \r
9601 \r
9602 VOID\r
9603 CommentPopUp(char *title, char *str)\r
9604 {\r
9605   HWND hwnd = GetActiveWindow();\r
9606   EitherCommentPopUp(0, title, str, FALSE);\r
9607   SetActiveWindow(hwnd);\r
9608 }\r
9609 \r
9610 VOID\r
9611 CommentPopDown(void)\r
9612 {\r
9613   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9614   if (commentDialog) {\r
9615     ShowWindow(commentDialog, SW_HIDE);\r
9616   }\r
9617   commentDialogUp = FALSE;\r
9618 }\r
9619 \r
9620 VOID\r
9621 EditCommentPopUp(int index, char *title, char *str)\r
9622 {\r
9623   EitherCommentPopUp(index, title, str, TRUE);\r
9624 }\r
9625 \r
9626 \r
9627 VOID\r
9628 RingBell()\r
9629 {\r
9630   MyPlaySound(&sounds[(int)SoundMove]);\r
9631 }\r
9632 \r
9633 VOID PlayIcsWinSound()\r
9634 {\r
9635   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9636 }\r
9637 \r
9638 VOID PlayIcsLossSound()\r
9639 {\r
9640   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9641 }\r
9642 \r
9643 VOID PlayIcsDrawSound()\r
9644 {\r
9645   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9646 }\r
9647 \r
9648 VOID PlayIcsUnfinishedSound()\r
9649 {\r
9650   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9651 }\r
9652 \r
9653 VOID\r
9654 PlayAlarmSound()\r
9655 {\r
9656   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9657 }\r
9658 \r
9659 \r
9660 VOID\r
9661 EchoOn()\r
9662 {\r
9663   HWND hInput;\r
9664   consoleEcho = TRUE;\r
9665   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9666   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9667   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9668 }\r
9669 \r
9670 \r
9671 VOID\r
9672 EchoOff()\r
9673 {\r
9674   CHARFORMAT cf;\r
9675   HWND hInput;\r
9676   consoleEcho = FALSE;\r
9677   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9678   /* This works OK: set text and background both to the same color */\r
9679   cf = consoleCF;\r
9680   cf.crTextColor = COLOR_ECHOOFF;\r
9681   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9682   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9683 }\r
9684 \r
9685 /* No Raw()...? */\r
9686 \r
9687 void Colorize(ColorClass cc, int continuation)\r
9688 {\r
9689   currentColorClass = cc;\r
9690   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9691   consoleCF.crTextColor = textAttribs[cc].color;\r
9692   consoleCF.dwEffects = textAttribs[cc].effects;\r
9693   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9694 }\r
9695 \r
9696 char *\r
9697 UserName()\r
9698 {\r
9699   static char buf[MSG_SIZ];\r
9700   DWORD bufsiz = MSG_SIZ;\r
9701 \r
9702   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9703         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9704   }\r
9705   if (!GetUserName(buf, &bufsiz)) {\r
9706     /*DisplayError("Error getting user name", GetLastError());*/\r
9707     strcpy(buf, "User");\r
9708   }\r
9709   return buf;\r
9710 }\r
9711 \r
9712 char *\r
9713 HostName()\r
9714 {\r
9715   static char buf[MSG_SIZ];\r
9716   DWORD bufsiz = MSG_SIZ;\r
9717 \r
9718   if (!GetComputerName(buf, &bufsiz)) {\r
9719     /*DisplayError("Error getting host name", GetLastError());*/\r
9720     strcpy(buf, "Unknown");\r
9721   }\r
9722   return buf;\r
9723 }\r
9724 \r
9725 \r
9726 int\r
9727 ClockTimerRunning()\r
9728 {\r
9729   return clockTimerEvent != 0;\r
9730 }\r
9731 \r
9732 int\r
9733 StopClockTimer()\r
9734 {\r
9735   if (clockTimerEvent == 0) return FALSE;\r
9736   KillTimer(hwndMain, clockTimerEvent);\r
9737   clockTimerEvent = 0;\r
9738   return TRUE;\r
9739 }\r
9740 \r
9741 void\r
9742 StartClockTimer(long millisec)\r
9743 {\r
9744   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9745                              (UINT) millisec, NULL);\r
9746 }\r
9747 \r
9748 void\r
9749 DisplayWhiteClock(long timeRemaining, int highlight)\r
9750 {\r
9751   HDC hdc;\r
9752   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9753 \r
9754   if(appData.noGUI) return;\r
9755   hdc = GetDC(hwndMain);\r
9756   if (!IsIconic(hwndMain)) {\r
9757     DisplayAClock(hdc, timeRemaining, highlight, \r
9758                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9759   }\r
9760   if (highlight && iconCurrent == iconBlack) {\r
9761     iconCurrent = iconWhite;\r
9762     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9763     if (IsIconic(hwndMain)) {\r
9764       DrawIcon(hdc, 2, 2, iconCurrent);\r
9765     }\r
9766   }\r
9767   (void) ReleaseDC(hwndMain, hdc);\r
9768   if (hwndConsole)\r
9769     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9770 }\r
9771 \r
9772 void\r
9773 DisplayBlackClock(long timeRemaining, int highlight)\r
9774 {\r
9775   HDC hdc;\r
9776   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9777 \r
9778   if(appData.noGUI) return;\r
9779   hdc = GetDC(hwndMain);\r
9780   if (!IsIconic(hwndMain)) {\r
9781     DisplayAClock(hdc, timeRemaining, highlight, \r
9782                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9783   }\r
9784   if (highlight && iconCurrent == iconWhite) {\r
9785     iconCurrent = iconBlack;\r
9786     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9787     if (IsIconic(hwndMain)) {\r
9788       DrawIcon(hdc, 2, 2, iconCurrent);\r
9789     }\r
9790   }\r
9791   (void) ReleaseDC(hwndMain, hdc);\r
9792   if (hwndConsole)\r
9793     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9794 }\r
9795 \r
9796 \r
9797 int\r
9798 LoadGameTimerRunning()\r
9799 {\r
9800   return loadGameTimerEvent != 0;\r
9801 }\r
9802 \r
9803 int\r
9804 StopLoadGameTimer()\r
9805 {\r
9806   if (loadGameTimerEvent == 0) return FALSE;\r
9807   KillTimer(hwndMain, loadGameTimerEvent);\r
9808   loadGameTimerEvent = 0;\r
9809   return TRUE;\r
9810 }\r
9811 \r
9812 void\r
9813 StartLoadGameTimer(long millisec)\r
9814 {\r
9815   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9816                                 (UINT) millisec, NULL);\r
9817 }\r
9818 \r
9819 void\r
9820 AutoSaveGame()\r
9821 {\r
9822   char *defName;\r
9823   FILE *f;\r
9824   char fileTitle[MSG_SIZ];\r
9825 \r
9826   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9827   f = OpenFileDialog(hwndMain, "a", defName,\r
9828                      appData.oldSaveStyle ? "gam" : "pgn",\r
9829                      GAME_FILT, \r
9830                      "Save Game to File", NULL, fileTitle, NULL);\r
9831   if (f != NULL) {\r
9832     SaveGame(f, 0, "");\r
9833     fclose(f);\r
9834   }\r
9835 }\r
9836 \r
9837 \r
9838 void\r
9839 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9840 {\r
9841   if (delayedTimerEvent != 0) {\r
9842     if (appData.debugMode) {\r
9843       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9844     }\r
9845     KillTimer(hwndMain, delayedTimerEvent);\r
9846     delayedTimerEvent = 0;\r
9847     delayedTimerCallback();\r
9848   }\r
9849   delayedTimerCallback = cb;\r
9850   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9851                                 (UINT) millisec, NULL);\r
9852 }\r
9853 \r
9854 DelayedEventCallback\r
9855 GetDelayedEvent()\r
9856 {\r
9857   if (delayedTimerEvent) {\r
9858     return delayedTimerCallback;\r
9859   } else {\r
9860     return NULL;\r
9861   }\r
9862 }\r
9863 \r
9864 void\r
9865 CancelDelayedEvent()\r
9866 {\r
9867   if (delayedTimerEvent) {\r
9868     KillTimer(hwndMain, delayedTimerEvent);\r
9869     delayedTimerEvent = 0;\r
9870   }\r
9871 }\r
9872 \r
9873 DWORD GetWin32Priority(int nice)\r
9874 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9875 /*\r
9876 REALTIME_PRIORITY_CLASS     0x00000100\r
9877 HIGH_PRIORITY_CLASS         0x00000080\r
9878 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9879 NORMAL_PRIORITY_CLASS       0x00000020\r
9880 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9881 IDLE_PRIORITY_CLASS         0x00000040\r
9882 */\r
9883         if (nice < -15) return 0x00000080;\r
9884         if (nice < 0)   return 0x00008000;\r
9885         if (nice == 0)  return 0x00000020;\r
9886         if (nice < 15)  return 0x00004000;\r
9887         return 0x00000040;\r
9888 }\r
9889 \r
9890 /* Start a child process running the given program.\r
9891    The process's standard output can be read from "from", and its\r
9892    standard input can be written to "to".\r
9893    Exit with fatal error if anything goes wrong.\r
9894    Returns an opaque pointer that can be used to destroy the process\r
9895    later.\r
9896 */\r
9897 int\r
9898 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9899 {\r
9900 #define BUFSIZE 4096\r
9901 \r
9902   HANDLE hChildStdinRd, hChildStdinWr,\r
9903     hChildStdoutRd, hChildStdoutWr;\r
9904   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9905   SECURITY_ATTRIBUTES saAttr;\r
9906   BOOL fSuccess;\r
9907   PROCESS_INFORMATION piProcInfo;\r
9908   STARTUPINFO siStartInfo;\r
9909   ChildProc *cp;\r
9910   char buf[MSG_SIZ];\r
9911   DWORD err;\r
9912 \r
9913   if (appData.debugMode) {\r
9914     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9915   }\r
9916 \r
9917   *pr = NoProc;\r
9918 \r
9919   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9920   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9921   saAttr.bInheritHandle = TRUE;\r
9922   saAttr.lpSecurityDescriptor = NULL;\r
9923 \r
9924   /*\r
9925    * The steps for redirecting child's STDOUT:\r
9926    *     1. Create anonymous pipe to be STDOUT for child.\r
9927    *     2. Create a noninheritable duplicate of read handle,\r
9928    *         and close the inheritable read handle.\r
9929    */\r
9930 \r
9931   /* Create a pipe for the child's STDOUT. */\r
9932   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9933     return GetLastError();\r
9934   }\r
9935 \r
9936   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9937   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9938                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9939                              FALSE,     /* not inherited */\r
9940                              DUPLICATE_SAME_ACCESS);\r
9941   if (! fSuccess) {\r
9942     return GetLastError();\r
9943   }\r
9944   CloseHandle(hChildStdoutRd);\r
9945 \r
9946   /*\r
9947    * The steps for redirecting child's STDIN:\r
9948    *     1. Create anonymous pipe to be STDIN for child.\r
9949    *     2. Create a noninheritable duplicate of write handle,\r
9950    *         and close the inheritable write handle.\r
9951    */\r
9952 \r
9953   /* Create a pipe for the child's STDIN. */\r
9954   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9955     return GetLastError();\r
9956   }\r
9957 \r
9958   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9959   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9960                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9961                              FALSE,     /* not inherited */\r
9962                              DUPLICATE_SAME_ACCESS);\r
9963   if (! fSuccess) {\r
9964     return GetLastError();\r
9965   }\r
9966   CloseHandle(hChildStdinWr);\r
9967 \r
9968   /* Arrange to (1) look in dir for the child .exe file, and\r
9969    * (2) have dir be the child's working directory.  Interpret\r
9970    * dir relative to the directory WinBoard loaded from. */\r
9971   GetCurrentDirectory(MSG_SIZ, buf);\r
9972   SetCurrentDirectory(installDir);\r
9973   SetCurrentDirectory(dir);\r
9974 \r
9975   /* Now create the child process. */\r
9976 \r
9977   siStartInfo.cb = sizeof(STARTUPINFO);\r
9978   siStartInfo.lpReserved = NULL;\r
9979   siStartInfo.lpDesktop = NULL;\r
9980   siStartInfo.lpTitle = NULL;\r
9981   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9982   siStartInfo.cbReserved2 = 0;\r
9983   siStartInfo.lpReserved2 = NULL;\r
9984   siStartInfo.hStdInput = hChildStdinRd;\r
9985   siStartInfo.hStdOutput = hChildStdoutWr;\r
9986   siStartInfo.hStdError = hChildStdoutWr;\r
9987 \r
9988   fSuccess = CreateProcess(NULL,\r
9989                            cmdLine,        /* command line */\r
9990                            NULL,           /* process security attributes */\r
9991                            NULL,           /* primary thread security attrs */\r
9992                            TRUE,           /* handles are inherited */\r
9993                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9994                            NULL,           /* use parent's environment */\r
9995                            NULL,\r
9996                            &siStartInfo, /* STARTUPINFO pointer */\r
9997                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9998 \r
9999   err = GetLastError();\r
10000   SetCurrentDirectory(buf); /* return to prev directory */\r
10001   if (! fSuccess) {\r
10002     return err;\r
10003   }\r
10004 \r
10005   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
10006     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
10007     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
10008   }\r
10009 \r
10010   /* Close the handles we don't need in the parent */\r
10011   CloseHandle(piProcInfo.hThread);\r
10012   CloseHandle(hChildStdinRd);\r
10013   CloseHandle(hChildStdoutWr);\r
10014 \r
10015   /* Prepare return value */\r
10016   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10017   cp->kind = CPReal;\r
10018   cp->hProcess = piProcInfo.hProcess;\r
10019   cp->pid = piProcInfo.dwProcessId;\r
10020   cp->hFrom = hChildStdoutRdDup;\r
10021   cp->hTo = hChildStdinWrDup;\r
10022 \r
10023   *pr = (void *) cp;\r
10024 \r
10025   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10026      2000 where engines sometimes don't see the initial command(s)\r
10027      from WinBoard and hang.  I don't understand how that can happen,\r
10028      but the Sleep is harmless, so I've put it in.  Others have also\r
10029      reported what may be the same problem, so hopefully this will fix\r
10030      it for them too.  */\r
10031   Sleep(500);\r
10032 \r
10033   return NO_ERROR;\r
10034 }\r
10035 \r
10036 \r
10037 void\r
10038 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10039 {\r
10040   ChildProc *cp; int result;\r
10041 \r
10042   cp = (ChildProc *) pr;\r
10043   if (cp == NULL) return;\r
10044 \r
10045   switch (cp->kind) {\r
10046   case CPReal:\r
10047     /* TerminateProcess is considered harmful, so... */\r
10048     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10049     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10050     /* The following doesn't work because the chess program\r
10051        doesn't "have the same console" as WinBoard.  Maybe\r
10052        we could arrange for this even though neither WinBoard\r
10053        nor the chess program uses a console for stdio? */\r
10054     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10055 \r
10056     /* [AS] Special termination modes for misbehaving programs... */\r
10057     if( signal == 9 ) { \r
10058         result = TerminateProcess( cp->hProcess, 0 );\r
10059 \r
10060         if ( appData.debugMode) {\r
10061             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10062         }\r
10063     }\r
10064     else if( signal == 10 ) {\r
10065         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10066 \r
10067         if( dw != WAIT_OBJECT_0 ) {\r
10068             result = TerminateProcess( cp->hProcess, 0 );\r
10069 \r
10070             if ( appData.debugMode) {\r
10071                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10072             }\r
10073 \r
10074         }\r
10075     }\r
10076 \r
10077     CloseHandle(cp->hProcess);\r
10078     break;\r
10079 \r
10080   case CPComm:\r
10081     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10082     break;\r
10083 \r
10084   case CPSock:\r
10085     closesocket(cp->sock);\r
10086     WSACleanup();\r
10087     break;\r
10088 \r
10089   case CPRcmd:\r
10090     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10091     closesocket(cp->sock);\r
10092     closesocket(cp->sock2);\r
10093     WSACleanup();\r
10094     break;\r
10095   }\r
10096   free(cp);\r
10097 }\r
10098 \r
10099 void\r
10100 InterruptChildProcess(ProcRef pr)\r
10101 {\r
10102   ChildProc *cp;\r
10103 \r
10104   cp = (ChildProc *) pr;\r
10105   if (cp == NULL) return;\r
10106   switch (cp->kind) {\r
10107   case CPReal:\r
10108     /* The following doesn't work because the chess program\r
10109        doesn't "have the same console" as WinBoard.  Maybe\r
10110        we could arrange for this even though neither WinBoard\r
10111        nor the chess program uses a console for stdio */\r
10112     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10113     break;\r
10114 \r
10115   case CPComm:\r
10116   case CPSock:\r
10117     /* Can't interrupt */\r
10118     break;\r
10119 \r
10120   case CPRcmd:\r
10121     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10122     break;\r
10123   }\r
10124 }\r
10125 \r
10126 \r
10127 int\r
10128 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10129 {\r
10130   char cmdLine[MSG_SIZ];\r
10131 \r
10132   if (port[0] == NULLCHAR) {\r
10133     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10134   } else {\r
10135     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10136   }\r
10137   return StartChildProcess(cmdLine, "", pr);\r
10138 }\r
10139 \r
10140 \r
10141 /* Code to open TCP sockets */\r
10142 \r
10143 int\r
10144 OpenTCP(char *host, char *port, ProcRef *pr)\r
10145 {\r
10146   ChildProc *cp;\r
10147   int err;\r
10148   SOCKET s;\r
10149   struct sockaddr_in sa, mysa;\r
10150   struct hostent FAR *hp;\r
10151   unsigned short uport;\r
10152   WORD wVersionRequested;\r
10153   WSADATA wsaData;\r
10154 \r
10155   /* Initialize socket DLL */\r
10156   wVersionRequested = MAKEWORD(1, 1);\r
10157   err = WSAStartup(wVersionRequested, &wsaData);\r
10158   if (err != 0) return err;\r
10159 \r
10160   /* Make socket */\r
10161   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10162     err = WSAGetLastError();\r
10163     WSACleanup();\r
10164     return err;\r
10165   }\r
10166 \r
10167   /* Bind local address using (mostly) don't-care values.\r
10168    */\r
10169   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10170   mysa.sin_family = AF_INET;\r
10171   mysa.sin_addr.s_addr = INADDR_ANY;\r
10172   uport = (unsigned short) 0;\r
10173   mysa.sin_port = htons(uport);\r
10174   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10175       == SOCKET_ERROR) {\r
10176     err = WSAGetLastError();\r
10177     WSACleanup();\r
10178     return err;\r
10179   }\r
10180 \r
10181   /* Resolve remote host name */\r
10182   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10183   if (!(hp = gethostbyname(host))) {\r
10184     unsigned int b0, b1, b2, b3;\r
10185 \r
10186     err = WSAGetLastError();\r
10187 \r
10188     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10189       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10190       hp->h_addrtype = AF_INET;\r
10191       hp->h_length = 4;\r
10192       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10193       hp->h_addr_list[0] = (char *) malloc(4);\r
10194       hp->h_addr_list[0][0] = (char) b0;\r
10195       hp->h_addr_list[0][1] = (char) b1;\r
10196       hp->h_addr_list[0][2] = (char) b2;\r
10197       hp->h_addr_list[0][3] = (char) b3;\r
10198     } else {\r
10199       WSACleanup();\r
10200       return err;\r
10201     }\r
10202   }\r
10203   sa.sin_family = hp->h_addrtype;\r
10204   uport = (unsigned short) atoi(port);\r
10205   sa.sin_port = htons(uport);\r
10206   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10207 \r
10208   /* Make connection */\r
10209   if (connect(s, (struct sockaddr *) &sa,\r
10210               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10211     err = WSAGetLastError();\r
10212     WSACleanup();\r
10213     return err;\r
10214   }\r
10215 \r
10216   /* Prepare return value */\r
10217   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10218   cp->kind = CPSock;\r
10219   cp->sock = s;\r
10220   *pr = (ProcRef *) cp;\r
10221 \r
10222   return NO_ERROR;\r
10223 }\r
10224 \r
10225 int\r
10226 OpenCommPort(char *name, ProcRef *pr)\r
10227 {\r
10228   HANDLE h;\r
10229   COMMTIMEOUTS ct;\r
10230   ChildProc *cp;\r
10231   char fullname[MSG_SIZ];\r
10232 \r
10233   if (*name != '\\')\r
10234     sprintf(fullname, "\\\\.\\%s", name);\r
10235   else\r
10236     strcpy(fullname, name);\r
10237 \r
10238   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10239                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10240   if (h == (HANDLE) -1) {\r
10241     return GetLastError();\r
10242   }\r
10243   hCommPort = h;\r
10244 \r
10245   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10246 \r
10247   /* Accumulate characters until a 100ms pause, then parse */\r
10248   ct.ReadIntervalTimeout = 100;\r
10249   ct.ReadTotalTimeoutMultiplier = 0;\r
10250   ct.ReadTotalTimeoutConstant = 0;\r
10251   ct.WriteTotalTimeoutMultiplier = 0;\r
10252   ct.WriteTotalTimeoutConstant = 0;\r
10253   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10254 \r
10255   /* Prepare return value */\r
10256   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10257   cp->kind = CPComm;\r
10258   cp->hFrom = h;\r
10259   cp->hTo = h;\r
10260   *pr = (ProcRef *) cp;\r
10261 \r
10262   return NO_ERROR;\r
10263 }\r
10264 \r
10265 int\r
10266 OpenLoopback(ProcRef *pr)\r
10267 {\r
10268   DisplayFatalError("Not implemented", 0, 1);\r
10269   return NO_ERROR;\r
10270 }\r
10271 \r
10272 \r
10273 int\r
10274 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10275 {\r
10276   ChildProc *cp;\r
10277   int err;\r
10278   SOCKET s, s2, s3;\r
10279   struct sockaddr_in sa, mysa;\r
10280   struct hostent FAR *hp;\r
10281   unsigned short uport;\r
10282   WORD wVersionRequested;\r
10283   WSADATA wsaData;\r
10284   int fromPort;\r
10285   char stderrPortStr[MSG_SIZ];\r
10286 \r
10287   /* Initialize socket DLL */\r
10288   wVersionRequested = MAKEWORD(1, 1);\r
10289   err = WSAStartup(wVersionRequested, &wsaData);\r
10290   if (err != 0) return err;\r
10291 \r
10292   /* Resolve remote host name */\r
10293   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10294   if (!(hp = gethostbyname(host))) {\r
10295     unsigned int b0, b1, b2, b3;\r
10296 \r
10297     err = WSAGetLastError();\r
10298 \r
10299     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10300       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10301       hp->h_addrtype = AF_INET;\r
10302       hp->h_length = 4;\r
10303       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10304       hp->h_addr_list[0] = (char *) malloc(4);\r
10305       hp->h_addr_list[0][0] = (char) b0;\r
10306       hp->h_addr_list[0][1] = (char) b1;\r
10307       hp->h_addr_list[0][2] = (char) b2;\r
10308       hp->h_addr_list[0][3] = (char) b3;\r
10309     } else {\r
10310       WSACleanup();\r
10311       return err;\r
10312     }\r
10313   }\r
10314   sa.sin_family = hp->h_addrtype;\r
10315   uport = (unsigned short) 514;\r
10316   sa.sin_port = htons(uport);\r
10317   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10318 \r
10319   /* Bind local socket to unused "privileged" port address\r
10320    */\r
10321   s = INVALID_SOCKET;\r
10322   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10323   mysa.sin_family = AF_INET;\r
10324   mysa.sin_addr.s_addr = INADDR_ANY;\r
10325   for (fromPort = 1023;; fromPort--) {\r
10326     if (fromPort < 0) {\r
10327       WSACleanup();\r
10328       return WSAEADDRINUSE;\r
10329     }\r
10330     if (s == INVALID_SOCKET) {\r
10331       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10332         err = WSAGetLastError();\r
10333         WSACleanup();\r
10334         return err;\r
10335       }\r
10336     }\r
10337     uport = (unsigned short) fromPort;\r
10338     mysa.sin_port = htons(uport);\r
10339     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10340         == SOCKET_ERROR) {\r
10341       err = WSAGetLastError();\r
10342       if (err == WSAEADDRINUSE) continue;\r
10343       WSACleanup();\r
10344       return err;\r
10345     }\r
10346     if (connect(s, (struct sockaddr *) &sa,\r
10347       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10348       err = WSAGetLastError();\r
10349       if (err == WSAEADDRINUSE) {\r
10350         closesocket(s);\r
10351         s = -1;\r
10352         continue;\r
10353       }\r
10354       WSACleanup();\r
10355       return err;\r
10356     }\r
10357     break;\r
10358   }\r
10359 \r
10360   /* Bind stderr local socket to unused "privileged" port address\r
10361    */\r
10362   s2 = INVALID_SOCKET;\r
10363   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10364   mysa.sin_family = AF_INET;\r
10365   mysa.sin_addr.s_addr = INADDR_ANY;\r
10366   for (fromPort = 1023;; fromPort--) {\r
10367     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10368     if (fromPort < 0) {\r
10369       (void) closesocket(s);\r
10370       WSACleanup();\r
10371       return WSAEADDRINUSE;\r
10372     }\r
10373     if (s2 == INVALID_SOCKET) {\r
10374       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10375         err = WSAGetLastError();\r
10376         closesocket(s);\r
10377         WSACleanup();\r
10378         return err;\r
10379       }\r
10380     }\r
10381     uport = (unsigned short) fromPort;\r
10382     mysa.sin_port = htons(uport);\r
10383     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10384         == SOCKET_ERROR) {\r
10385       err = WSAGetLastError();\r
10386       if (err == WSAEADDRINUSE) continue;\r
10387       (void) closesocket(s);\r
10388       WSACleanup();\r
10389       return err;\r
10390     }\r
10391     if (listen(s2, 1) == SOCKET_ERROR) {\r
10392       err = WSAGetLastError();\r
10393       if (err == WSAEADDRINUSE) {\r
10394         closesocket(s2);\r
10395         s2 = INVALID_SOCKET;\r
10396         continue;\r
10397       }\r
10398       (void) closesocket(s);\r
10399       (void) closesocket(s2);\r
10400       WSACleanup();\r
10401       return err;\r
10402     }\r
10403     break;\r
10404   }\r
10405   prevStderrPort = fromPort; // remember port used\r
10406   sprintf(stderrPortStr, "%d", fromPort);\r
10407 \r
10408   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10409     err = WSAGetLastError();\r
10410     (void) closesocket(s);\r
10411     (void) closesocket(s2);\r
10412     WSACleanup();\r
10413     return err;\r
10414   }\r
10415 \r
10416   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10417     err = WSAGetLastError();\r
10418     (void) closesocket(s);\r
10419     (void) closesocket(s2);\r
10420     WSACleanup();\r
10421     return err;\r
10422   }\r
10423   if (*user == NULLCHAR) user = UserName();\r
10424   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10425     err = WSAGetLastError();\r
10426     (void) closesocket(s);\r
10427     (void) closesocket(s2);\r
10428     WSACleanup();\r
10429     return err;\r
10430   }\r
10431   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10432     err = WSAGetLastError();\r
10433     (void) closesocket(s);\r
10434     (void) closesocket(s2);\r
10435     WSACleanup();\r
10436     return err;\r
10437   }\r
10438 \r
10439   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10440     err = WSAGetLastError();\r
10441     (void) closesocket(s);\r
10442     (void) closesocket(s2);\r
10443     WSACleanup();\r
10444     return err;\r
10445   }\r
10446   (void) closesocket(s2);  /* Stop listening */\r
10447 \r
10448   /* Prepare return value */\r
10449   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10450   cp->kind = CPRcmd;\r
10451   cp->sock = s;\r
10452   cp->sock2 = s3;\r
10453   *pr = (ProcRef *) cp;\r
10454 \r
10455   return NO_ERROR;\r
10456 }\r
10457 \r
10458 \r
10459 InputSourceRef\r
10460 AddInputSource(ProcRef pr, int lineByLine,\r
10461                InputCallback func, VOIDSTAR closure)\r
10462 {\r
10463   InputSource *is, *is2 = NULL;\r
10464   ChildProc *cp = (ChildProc *) pr;\r
10465 \r
10466   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10467   is->lineByLine = lineByLine;\r
10468   is->func = func;\r
10469   is->closure = closure;\r
10470   is->second = NULL;\r
10471   is->next = is->buf;\r
10472   if (pr == NoProc) {\r
10473     is->kind = CPReal;\r
10474     consoleInputSource = is;\r
10475   } else {\r
10476     is->kind = cp->kind;\r
10477     /* \r
10478         [AS] Try to avoid a race condition if the thread is given control too early:\r
10479         we create all threads suspended so that the is->hThread variable can be\r
10480         safely assigned, then let the threads start with ResumeThread.\r
10481     */\r
10482     switch (cp->kind) {\r
10483     case CPReal:\r
10484       is->hFile = cp->hFrom;\r
10485       cp->hFrom = NULL; /* now owned by InputThread */\r
10486       is->hThread =\r
10487         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10488                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10489       break;\r
10490 \r
10491     case CPComm:\r
10492       is->hFile = cp->hFrom;\r
10493       cp->hFrom = NULL; /* now owned by InputThread */\r
10494       is->hThread =\r
10495         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10496                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10497       break;\r
10498 \r
10499     case CPSock:\r
10500       is->sock = cp->sock;\r
10501       is->hThread =\r
10502         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10503                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10504       break;\r
10505 \r
10506     case CPRcmd:\r
10507       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10508       *is2 = *is;\r
10509       is->sock = cp->sock;\r
10510       is->second = is2;\r
10511       is2->sock = cp->sock2;\r
10512       is2->second = is2;\r
10513       is->hThread =\r
10514         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10515                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10516       is2->hThread =\r
10517         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10518                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10519       break;\r
10520     }\r
10521 \r
10522     if( is->hThread != NULL ) {\r
10523         ResumeThread( is->hThread );\r
10524     }\r
10525 \r
10526     if( is2 != NULL && is2->hThread != NULL ) {\r
10527         ResumeThread( is2->hThread );\r
10528     }\r
10529   }\r
10530 \r
10531   return (InputSourceRef) is;\r
10532 }\r
10533 \r
10534 void\r
10535 RemoveInputSource(InputSourceRef isr)\r
10536 {\r
10537   InputSource *is;\r
10538 \r
10539   is = (InputSource *) isr;\r
10540   is->hThread = NULL;  /* tell thread to stop */\r
10541   CloseHandle(is->hThread);\r
10542   if (is->second != NULL) {\r
10543     is->second->hThread = NULL;\r
10544     CloseHandle(is->second->hThread);\r
10545   }\r
10546 }\r
10547 \r
10548 \r
10549 int\r
10550 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10551 {\r
10552   DWORD dOutCount;\r
10553   int outCount = SOCKET_ERROR;\r
10554   ChildProc *cp = (ChildProc *) pr;\r
10555   static OVERLAPPED ovl;\r
10556 \r
10557   if (pr == NoProc) {\r
10558     ConsoleOutput(message, count, FALSE);\r
10559     return count;\r
10560   } \r
10561 \r
10562   if (ovl.hEvent == NULL) {\r
10563     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10564   }\r
10565   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10566 \r
10567   switch (cp->kind) {\r
10568   case CPSock:\r
10569   case CPRcmd:\r
10570     outCount = send(cp->sock, message, count, 0);\r
10571     if (outCount == SOCKET_ERROR) {\r
10572       *outError = WSAGetLastError();\r
10573     } else {\r
10574       *outError = NO_ERROR;\r
10575     }\r
10576     break;\r
10577 \r
10578   case CPReal:\r
10579     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10580                   &dOutCount, NULL)) {\r
10581       *outError = NO_ERROR;\r
10582       outCount = (int) dOutCount;\r
10583     } else {\r
10584       *outError = GetLastError();\r
10585     }\r
10586     break;\r
10587 \r
10588   case CPComm:\r
10589     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10590                             &dOutCount, &ovl);\r
10591     if (*outError == NO_ERROR) {\r
10592       outCount = (int) dOutCount;\r
10593     }\r
10594     break;\r
10595   }\r
10596   return outCount;\r
10597 }\r
10598 \r
10599 int\r
10600 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10601                        long msdelay)\r
10602 {\r
10603   /* Ignore delay, not implemented for WinBoard */\r
10604   return OutputToProcess(pr, message, count, outError);\r
10605 }\r
10606 \r
10607 \r
10608 void\r
10609 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10610                         char *buf, int count, int error)\r
10611 {\r
10612   DisplayFatalError("Not implemented", 0, 1);\r
10613 }\r
10614 \r
10615 /* see wgamelist.c for Game List functions */\r
10616 /* see wedittags.c for Edit Tags functions */\r
10617 \r
10618 \r
10619 VOID\r
10620 ICSInitScript()\r
10621 {\r
10622   FILE *f;\r
10623   char buf[MSG_SIZ];\r
10624   char *dummy;\r
10625 \r
10626   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10627     f = fopen(buf, "r");\r
10628     if (f != NULL) {\r
10629       ProcessICSInitScript(f);\r
10630       fclose(f);\r
10631     }\r
10632   }\r
10633 }\r
10634 \r
10635 \r
10636 VOID\r
10637 StartAnalysisClock()\r
10638 {\r
10639   if (analysisTimerEvent) return;\r
10640   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10641                                         (UINT) 2000, NULL);\r
10642 }\r
10643 \r
10644 LRESULT CALLBACK\r
10645 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10646 {\r
10647   static HANDLE hwndText;\r
10648   RECT rect;\r
10649   static int sizeX, sizeY;\r
10650   int newSizeX, newSizeY, flags;\r
10651   MINMAXINFO *mmi;\r
10652 \r
10653   switch (message) {\r
10654   case WM_INITDIALOG: /* message: initialize dialog box */\r
10655     /* Initialize the dialog items */\r
10656     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10657     SetWindowText(hDlg, analysisTitle);\r
10658     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10659     /* Size and position the dialog */\r
10660     if (!analysisDialog) {\r
10661       analysisDialog = hDlg;\r
10662       flags = SWP_NOZORDER;\r
10663       GetClientRect(hDlg, &rect);\r
10664       sizeX = rect.right;\r
10665       sizeY = rect.bottom;\r
10666       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10667           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10668         WINDOWPLACEMENT wp;\r
10669         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10670         wp.length = sizeof(WINDOWPLACEMENT);\r
10671         wp.flags = 0;\r
10672         wp.showCmd = SW_SHOW;\r
10673         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10674         wp.rcNormalPosition.left = analysisX;\r
10675         wp.rcNormalPosition.right = analysisX + analysisW;\r
10676         wp.rcNormalPosition.top = analysisY;\r
10677         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10678         SetWindowPlacement(hDlg, &wp);\r
10679 \r
10680         GetClientRect(hDlg, &rect);\r
10681         newSizeX = rect.right;\r
10682         newSizeY = rect.bottom;\r
10683         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10684                               newSizeX, newSizeY);\r
10685         sizeX = newSizeX;\r
10686         sizeY = newSizeY;\r
10687       }\r
10688     }\r
10689     return FALSE;\r
10690 \r
10691   case WM_COMMAND: /* message: received a command */\r
10692     switch (LOWORD(wParam)) {\r
10693     case IDCANCEL:\r
10694       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10695           ExitAnalyzeMode();\r
10696           ModeHighlight();\r
10697           return TRUE;\r
10698       }\r
10699       EditGameEvent();\r
10700       return TRUE;\r
10701     default:\r
10702       break;\r
10703     }\r
10704     break;\r
10705 \r
10706   case WM_SIZE:\r
10707     newSizeX = LOWORD(lParam);\r
10708     newSizeY = HIWORD(lParam);\r
10709     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10710     sizeX = newSizeX;\r
10711     sizeY = newSizeY;\r
10712     break;\r
10713 \r
10714   case WM_GETMINMAXINFO:\r
10715     /* Prevent resizing window too small */\r
10716     mmi = (MINMAXINFO *) lParam;\r
10717     mmi->ptMinTrackSize.x = 100;\r
10718     mmi->ptMinTrackSize.y = 100;\r
10719     break;\r
10720   }\r
10721   return FALSE;\r
10722 }\r
10723 \r
10724 VOID\r
10725 AnalysisPopUp(char* title, char* str)\r
10726 {\r
10727   FARPROC lpProc;\r
10728   char *p, *q;\r
10729 \r
10730   /* [AS] */\r
10731   EngineOutputPopUp();\r
10732   return;\r
10733 \r
10734   if (str == NULL) str = "";\r
10735   p = (char *) malloc(2 * strlen(str) + 2);\r
10736   q = p;\r
10737   while (*str) {\r
10738     if (*str == '\n') *q++ = '\r';\r
10739     *q++ = *str++;\r
10740   }\r
10741   *q = NULLCHAR;\r
10742   if (analysisText != NULL) free(analysisText);\r
10743   analysisText = p;\r
10744 \r
10745   if (analysisDialog) {\r
10746     SetWindowText(analysisDialog, title);\r
10747     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10748     ShowWindow(analysisDialog, SW_SHOW);\r
10749   } else {\r
10750     analysisTitle = title;\r
10751     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10752     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10753                  hwndMain, (DLGPROC)lpProc);\r
10754     FreeProcInstance(lpProc);\r
10755   }\r
10756   analysisDialogUp = TRUE;  \r
10757 }\r
10758 \r
10759 VOID\r
10760 AnalysisPopDown()\r
10761 {\r
10762   if (analysisDialog) {\r
10763     ShowWindow(analysisDialog, SW_HIDE);\r
10764   }\r
10765   analysisDialogUp = FALSE;  \r
10766 }\r
10767 \r
10768 \r
10769 VOID\r
10770 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10771 {\r
10772   highlightInfo.sq[0].x = fromX;\r
10773   highlightInfo.sq[0].y = fromY;\r
10774   highlightInfo.sq[1].x = toX;\r
10775   highlightInfo.sq[1].y = toY;\r
10776 }\r
10777 \r
10778 VOID\r
10779 ClearHighlights()\r
10780 {\r
10781   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10782     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10783 }\r
10784 \r
10785 VOID\r
10786 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10787 {\r
10788   premoveHighlightInfo.sq[0].x = fromX;\r
10789   premoveHighlightInfo.sq[0].y = fromY;\r
10790   premoveHighlightInfo.sq[1].x = toX;\r
10791   premoveHighlightInfo.sq[1].y = toY;\r
10792 }\r
10793 \r
10794 VOID\r
10795 ClearPremoveHighlights()\r
10796 {\r
10797   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10798     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10799 }\r
10800 \r
10801 VOID\r
10802 ShutDownFrontEnd()\r
10803 {\r
10804   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10805   DeleteClipboardTempFiles();\r
10806 }\r
10807 \r
10808 void\r
10809 BoardToTop()\r
10810 {\r
10811     if (IsIconic(hwndMain))\r
10812       ShowWindow(hwndMain, SW_RESTORE);\r
10813 \r
10814     SetActiveWindow(hwndMain);\r
10815 }\r
10816 \r
10817 /*\r
10818  * Prototypes for animation support routines\r
10819  */\r
10820 static void ScreenSquare(int column, int row, POINT * pt);\r
10821 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10822      POINT frames[], int * nFrames);\r
10823 \r
10824 \r
10825 void\r
10826 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10827 {       // [HGM] atomic: animate blast wave\r
10828         int i;\r
10829 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10830         explodeInfo.fromX = fromX;\r
10831         explodeInfo.fromY = fromY;\r
10832         explodeInfo.toX = toX;\r
10833         explodeInfo.toY = toY;\r
10834         for(i=1; i<nFrames; i++) {\r
10835             explodeInfo.radius = (i*180)/(nFrames-1);\r
10836             DrawPosition(FALSE, NULL);\r
10837             Sleep(appData.animSpeed);\r
10838         }\r
10839         explodeInfo.radius = 0;\r
10840         DrawPosition(TRUE, NULL);\r
10841 }\r
10842 \r
10843 #define kFactor 4\r
10844 \r
10845 void\r
10846 AnimateMove(board, fromX, fromY, toX, toY)\r
10847      Board board;\r
10848      int fromX;\r
10849      int fromY;\r
10850      int toX;\r
10851      int toY;\r
10852 {\r
10853   ChessSquare piece;\r
10854   POINT start, finish, mid;\r
10855   POINT frames[kFactor * 2 + 1];\r
10856   int nFrames, n;\r
10857 \r
10858   if (!appData.animate) return;\r
10859   if (doingSizing) return;\r
10860   if (fromY < 0 || fromX < 0) return;\r
10861   piece = board[fromY][fromX];\r
10862   if (piece >= EmptySquare) return;\r
10863 \r
10864   ScreenSquare(fromX, fromY, &start);\r
10865   ScreenSquare(toX, toY, &finish);\r
10866 \r
10867   /* All pieces except knights move in straight line */\r
10868   if (piece != WhiteKnight && piece != BlackKnight) {\r
10869     mid.x = start.x + (finish.x - start.x) / 2;\r
10870     mid.y = start.y + (finish.y - start.y) / 2;\r
10871   } else {\r
10872     /* Knight: make diagonal movement then straight */\r
10873     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10874        mid.x = start.x + (finish.x - start.x) / 2;\r
10875        mid.y = finish.y;\r
10876      } else {\r
10877        mid.x = finish.x;\r
10878        mid.y = start.y + (finish.y - start.y) / 2;\r
10879      }\r
10880   }\r
10881   \r
10882   /* Don't use as many frames for very short moves */\r
10883   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10884     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10885   else\r
10886     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10887 \r
10888   animInfo.from.x = fromX;\r
10889   animInfo.from.y = fromY;\r
10890   animInfo.to.x = toX;\r
10891   animInfo.to.y = toY;\r
10892   animInfo.lastpos = start;\r
10893   animInfo.piece = piece;\r
10894   for (n = 0; n < nFrames; n++) {\r
10895     animInfo.pos = frames[n];\r
10896     DrawPosition(FALSE, NULL);\r
10897     animInfo.lastpos = animInfo.pos;\r
10898     Sleep(appData.animSpeed);\r
10899   }\r
10900   animInfo.pos = finish;\r
10901   DrawPosition(FALSE, NULL);\r
10902   animInfo.piece = EmptySquare;\r
10903   if(gameInfo.variant == VariantAtomic && \r
10904      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10905         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10906 }\r
10907 \r
10908 /*      Convert board position to corner of screen rect and color       */\r
10909 \r
10910 static void\r
10911 ScreenSquare(column, row, pt)\r
10912      int column; int row; POINT * pt;\r
10913 {\r
10914   if (flipView) {\r
10915     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10916     pt->y = lineGap + row * (squareSize + lineGap);\r
10917   } else {\r
10918     pt->x = lineGap + column * (squareSize + lineGap);\r
10919     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10920   }\r
10921 }\r
10922 \r
10923 /*      Generate a series of frame coords from start->mid->finish.\r
10924         The movement rate doubles until the half way point is\r
10925         reached, then halves back down to the final destination,\r
10926         which gives a nice slow in/out effect. The algorithmn\r
10927         may seem to generate too many intermediates for short\r
10928         moves, but remember that the purpose is to attract the\r
10929         viewers attention to the piece about to be moved and\r
10930         then to where it ends up. Too few frames would be less\r
10931         noticeable.                                             */\r
10932 \r
10933 static void\r
10934 Tween(start, mid, finish, factor, frames, nFrames)\r
10935      POINT * start; POINT * mid;\r
10936      POINT * finish; int factor;\r
10937      POINT frames[]; int * nFrames;\r
10938 {\r
10939   int n, fraction = 1, count = 0;\r
10940 \r
10941   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10942   for (n = 0; n < factor; n++)\r
10943     fraction *= 2;\r
10944   for (n = 0; n < factor; n++) {\r
10945     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10946     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10947     count ++;\r
10948     fraction = fraction / 2;\r
10949   }\r
10950   \r
10951   /* Midpoint */\r
10952   frames[count] = *mid;\r
10953   count ++;\r
10954   \r
10955   /* Slow out, stepping 1/2, then 1/4, ... */\r
10956   fraction = 2;\r
10957   for (n = 0; n < factor; n++) {\r
10958     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10959     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10960     count ++;\r
10961     fraction = fraction * 2;\r
10962   }\r
10963   *nFrames = count;\r
10964 }\r
10965 \r
10966 void\r
10967 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10968 {\r
10969 #if 0\r
10970     char buf[256];\r
10971 \r
10972     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10973         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10974 \r
10975     OutputDebugString( buf );\r
10976 #endif\r
10977 \r
10978     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10979 \r
10980     EvalGraphSet( first, last, current, pvInfoList );\r
10981 }\r
10982 \r
10983 void SetProgramStats( FrontEndProgramStats * stats )\r
10984 {\r
10985 #if 0\r
10986     char buf[1024];\r
10987 \r
10988     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10989         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10990 \r
10991     OutputDebugString( buf );\r
10992 #endif\r
10993 \r
10994     EngineOutputUpdate( stats );\r
10995 }\r