cbbb1780978495add0ebbde0bed6637e74cf90da
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  * $Id: winboard.c,v 2.3 2003/11/25 05:25:20 mann Exp $\r
4  *\r
5  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
6  * Massachusetts.  Enhancements Copyright\r
7  * 1992-2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software\r
8  * Foundation, Inc.\r
9  *\r
10  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
11  * which was written and is copyrighted by Wayne Christopher.\r
12  *\r
13  * The following terms apply to Digital Equipment Corporation's copyright\r
14  * interest in XBoard:\r
15  * ------------------------------------------------------------------------\r
16  * All Rights Reserved\r
17  *\r
18  * Permission to use, copy, modify, and distribute this software and its\r
19  * documentation for any purpose and without fee is hereby granted,\r
20  * provided that the above copyright notice appear in all copies and that\r
21  * both that copyright notice and this permission notice appear in\r
22  * supporting documentation, and that the name of Digital not be\r
23  * used in advertising or publicity pertaining to distribution of the\r
24  * software without specific, written prior permission.\r
25  *\r
26  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
27  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
28  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
29  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
30  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
31  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
32  * SOFTWARE.\r
33  * ------------------------------------------------------------------------\r
34  *\r
35  * The following terms apply to the enhanced version of XBoard\r
36  * distributed by the Free Software Foundation:\r
37  * ------------------------------------------------------------------------\r
38  *\r
39  * GNU XBoard is free software: you can redistribute it and/or modify\r
40  * it under the terms of the GNU General Public License as published by\r
41  * the Free Software Foundation, either version 3 of the License, or (at\r
42  * your option) any later version.\r
43  *\r
44  * GNU XBoard is distributed in the hope that it will be useful, but\r
45  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
47  * General Public License for more details.\r
48  *\r
49  * You should have received a copy of the GNU General Public License\r
50  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
51  *\r
52  *------------------------------------------------------------------------\r
53  ** See the file ChangeLog for a revision history.  */\r
54 \r
55 #include "config.h"\r
56 \r
57 #include <windows.h>\r
58 #include <winuser.h>\r
59 #include <winsock.h>\r
60 #include <commctrl.h>\r
61 \r
62 #include <stdio.h>\r
63 #include <stdlib.h>\r
64 #include <time.h>\r
65 #include <malloc.h>\r
66 #include <sys/stat.h>\r
67 #include <fcntl.h>\r
68 #include <math.h>\r
69 #include <commdlg.h>\r
70 #include <dlgs.h>\r
71 #include <richedit.h>\r
72 #include <mmsystem.h>\r
73 #include <ctype.h>\r
74 \r
75 #if __GNUC__\r
76 #include <errno.h>\r
77 #include <string.h>\r
78 #endif\r
79 \r
80 #include "common.h"\r
81 #include "winboard.h"\r
82 #include "frontend.h"\r
83 #include "backend.h"\r
84 #include "moves.h"\r
85 #include "wclipbrd.h"\r
86 #include "wgamelist.h"\r
87 #include "wedittags.h"\r
88 #include "woptions.h"\r
89 #include "wsockerr.h"\r
90 #include "defaults.h"\r
91 #include "help.h"\r
92 #include "wsnap.h"\r
93 \r
94 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
95 \r
96   int myrandom(void);\r
97   void mysrandom(unsigned int seed);\r
98 \r
99 extern int whiteFlag, blackFlag;\r
100 Boolean flipClock = FALSE;\r
101 \r
102 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
103 VOID NewVariantPopup(HWND hwnd);\r
104 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
105                    /*char*/int promoChar));\r
106 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
107 void DisplayMove P((int moveNumber));\r
108 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
109 typedef struct {\r
110   ChessSquare piece;  \r
111   POINT pos;      /* window coordinates of current pos */\r
112   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
113   POINT from;     /* board coordinates of the piece's orig pos */\r
114   POINT to;       /* board coordinates of the piece's new pos */\r
115 } AnimInfo;\r
116 \r
117 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
118 \r
119 typedef struct {\r
120   POINT start;    /* window coordinates of start pos */\r
121   POINT pos;      /* window coordinates of current pos */\r
122   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
123   POINT from;     /* board coordinates of the piece's orig pos */\r
124 } DragInfo;\r
125 \r
126 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
127 \r
128 typedef struct {\r
129   POINT sq[2];    /* board coordinates of from, to squares */\r
130 } HighlightInfo;\r
131 \r
132 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
133 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
134 \r
135 typedef struct { // [HGM] atomic\r
136   int fromX, fromY, toX, toY, radius;\r
137 } ExplodeInfo;\r
138 \r
139 static ExplodeInfo explodeInfo;\r
140 \r
141 /* Window class names */\r
142 char szAppName[] = "WinBoard";\r
143 char szConsoleName[] = "WBConsole";\r
144 \r
145 /* Title bar text */\r
146 char szTitle[] = "WinBoard";\r
147 char szConsoleTitle[] = "I C S Interaction";\r
148 \r
149 char *programName;\r
150 char *settingsFileName;\r
151 BOOLEAN saveSettingsOnExit;\r
152 char installDir[MSG_SIZ];\r
153 \r
154 BoardSize boardSize;\r
155 BOOLEAN chessProgram;\r
156 static int boardX, boardY;\r
157 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
158 static int squareSize, lineGap, minorSize;\r
159 static int winWidth, winHeight, winW, winH;\r
160 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
161 static int logoHeight = 0;\r
162 static char messageText[MESSAGE_TEXT_MAX];\r
163 static int clockTimerEvent = 0;\r
164 static int loadGameTimerEvent = 0;\r
165 static int analysisTimerEvent = 0;\r
166 static DelayedEventCallback delayedTimerCallback;\r
167 static int delayedTimerEvent = 0;\r
168 static int buttonCount = 2;\r
169 char *icsTextMenuString;\r
170 char *icsNames;\r
171 char *firstChessProgramNames;\r
172 char *secondChessProgramNames;\r
173 \r
174 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
175 \r
176 #define PALETTESIZE 256\r
177 \r
178 HINSTANCE hInst;          /* current instance */\r
179 HWND hwndMain = NULL;        /* root window*/\r
180 HWND hwndConsole = NULL;\r
181 BOOLEAN alwaysOnTop = FALSE;\r
182 RECT boardRect;\r
183 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
184   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
185 HPALETTE hPal;\r
186 ColorClass currentColorClass;\r
187 \r
188 HWND hCommPort = NULL;    /* currently open comm port */\r
189 static HWND hwndPause;    /* pause button */\r
190 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
191 static HBRUSH lightSquareBrush, darkSquareBrush,\r
192   blackSquareBrush, /* [HGM] for band between board and holdings */\r
193   explodeBrush,     /* [HGM] atomic */\r
194   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
195 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
196 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
197 static HPEN gridPen = NULL;\r
198 static HPEN highlightPen = NULL;\r
199 static HPEN premovePen = NULL;\r
200 static NPLOGPALETTE pLogPal;\r
201 static BOOL paletteChanged = FALSE;\r
202 static HICON iconWhite, iconBlack, iconCurrent;\r
203 static int doingSizing = FALSE;\r
204 static int lastSizing = 0;\r
205 static int prevStderrPort;\r
206 static HBITMAP userLogo;\r
207 \r
208 /* [AS] Support for background textures */\r
209 #define BACK_TEXTURE_MODE_DISABLED      0\r
210 #define BACK_TEXTURE_MODE_PLAIN         1\r
211 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
212 \r
213 static HBITMAP liteBackTexture = NULL;\r
214 static HBITMAP darkBackTexture = NULL;\r
215 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
216 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
217 static int backTextureSquareSize = 0;\r
218 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
219 \r
220 #if __GNUC__ && !defined(_winmajor)\r
221 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
222 #else\r
223 #define oldDialog (_winmajor < 4)\r
224 #endif\r
225 \r
226 char *defaultTextAttribs[] = \r
227 {\r
228   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
229   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
230   COLOR_NONE\r
231 };\r
232 \r
233 typedef struct {\r
234   char *name;\r
235   int squareSize;\r
236   int lineGap;\r
237   int smallLayout;\r
238   int tinyLayout;\r
239   int cliWidth, cliHeight;\r
240 } SizeInfo;\r
241 \r
242 SizeInfo sizeInfo[] = \r
243 {\r
244   { "tiny",     21, 0, 1, 1, 0, 0 },\r
245   { "teeny",    25, 1, 1, 1, 0, 0 },\r
246   { "dinky",    29, 1, 1, 1, 0, 0 },\r
247   { "petite",   33, 1, 1, 1, 0, 0 },\r
248   { "slim",     37, 2, 1, 0, 0, 0 },\r
249   { "small",    40, 2, 1, 0, 0, 0 },\r
250   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
251   { "middling", 49, 2, 0, 0, 0, 0 },\r
252   { "average",  54, 2, 0, 0, 0, 0 },\r
253   { "moderate", 58, 3, 0, 0, 0, 0 },\r
254   { "medium",   64, 3, 0, 0, 0, 0 },\r
255   { "bulky",    72, 3, 0, 0, 0, 0 },\r
256   { "large",    80, 3, 0, 0, 0, 0 },\r
257   { "big",      87, 3, 0, 0, 0, 0 },\r
258   { "huge",     95, 3, 0, 0, 0, 0 },\r
259   { "giant",    108, 3, 0, 0, 0, 0 },\r
260   { "colossal", 116, 4, 0, 0, 0, 0 },\r
261   { "titanic",  129, 4, 0, 0, 0, 0 },\r
262   { NULL, 0, 0, 0, 0, 0, 0 }\r
263 };\r
264 \r
265 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
266 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
267 {\r
268   { 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
269   { 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
270   { 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
271   { 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
272   { 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
273   { 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
274   { 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
275   { 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
276   { 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
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283   { 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
284   { 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
285   { 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
286 };\r
287 \r
288 MyFont *font[NUM_SIZES][NUM_FONTS];\r
289 \r
290 typedef struct {\r
291   char *label;\r
292   int id;\r
293   HWND hwnd;\r
294   WNDPROC wndproc;\r
295 } MyButtonDesc;\r
296 \r
297 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
298 #define N_BUTTONS 5\r
299 \r
300 MyButtonDesc buttonDesc[N_BUTTONS] =\r
301 {\r
302   {"<<", IDM_ToStart, NULL, NULL},\r
303   {"<", IDM_Backward, NULL, NULL},\r
304   {"P", IDM_Pause, NULL, NULL},\r
305   {">", IDM_Forward, NULL, NULL},\r
306   {">>", IDM_ToEnd, NULL, NULL},\r
307 };\r
308 \r
309 int tinyLayout = 0, smallLayout = 0;\r
310 #define MENU_BAR_ITEMS 6\r
311 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
312   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
313   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
314 };\r
315 \r
316 \r
317 MySound sounds[(int)NSoundClasses];\r
318 MyTextAttribs textAttribs[(int)NColorClasses];\r
319 \r
320 MyColorizeAttribs colorizeAttribs[] = {\r
321   { (COLORREF)0, 0, "Shout Text" },\r
322   { (COLORREF)0, 0, "SShout/CShout" },\r
323   { (COLORREF)0, 0, "Channel 1 Text" },\r
324   { (COLORREF)0, 0, "Channel Text" },\r
325   { (COLORREF)0, 0, "Kibitz Text" },\r
326   { (COLORREF)0, 0, "Tell Text" },\r
327   { (COLORREF)0, 0, "Challenge Text" },\r
328   { (COLORREF)0, 0, "Request Text" },\r
329   { (COLORREF)0, 0, "Seek Text" },\r
330   { (COLORREF)0, 0, "Normal Text" },\r
331   { (COLORREF)0, 0, "None" }\r
332 };\r
333 \r
334 \r
335 \r
336 static char *commentTitle;\r
337 static char *commentText;\r
338 static int commentIndex;\r
339 static Boolean editComment = FALSE;\r
340 HWND commentDialog = NULL;\r
341 BOOLEAN commentDialogUp = FALSE;\r
342 static int commentX, commentY, commentH, commentW;\r
343 \r
344 static char *analysisTitle;\r
345 static char *analysisText;\r
346 HWND analysisDialog = NULL;\r
347 BOOLEAN analysisDialogUp = FALSE;\r
348 static int analysisX, analysisY, analysisH, analysisW;\r
349 \r
350 char errorTitle[MSG_SIZ];\r
351 char errorMessage[2*MSG_SIZ];\r
352 HWND errorDialog = NULL;\r
353 BOOLEAN moveErrorMessageUp = FALSE;\r
354 BOOLEAN consoleEcho = TRUE;\r
355 CHARFORMAT consoleCF;\r
356 COLORREF consoleBackgroundColor;\r
357 \r
358 char *programVersion;\r
359 \r
360 #define CPReal 1\r
361 #define CPComm 2\r
362 #define CPSock 3\r
363 #define CPRcmd 4\r
364 typedef int CPKind;\r
365 \r
366 typedef struct {\r
367   CPKind kind;\r
368   HANDLE hProcess;\r
369   DWORD pid;\r
370   HANDLE hTo;\r
371   HANDLE hFrom;\r
372   SOCKET sock;\r
373   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
374 } ChildProc;\r
375 \r
376 #define INPUT_SOURCE_BUF_SIZE 4096\r
377 \r
378 typedef struct _InputSource {\r
379   CPKind kind;\r
380   HANDLE hFile;\r
381   SOCKET sock;\r
382   int lineByLine;\r
383   HANDLE hThread;\r
384   DWORD id;\r
385   char buf[INPUT_SOURCE_BUF_SIZE];\r
386   char *next;\r
387   DWORD count;\r
388   int error;\r
389   InputCallback func;\r
390   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
391   VOIDSTAR closure;\r
392 } InputSource;\r
393 \r
394 InputSource *consoleInputSource;\r
395 \r
396 DCB dcb;\r
397 \r
398 /* forward */\r
399 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
400 VOID ConsoleCreate();\r
401 LRESULT CALLBACK\r
402   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
403 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
404 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
405 VOID ParseCommSettings(char *arg, DCB *dcb);\r
406 LRESULT CALLBACK\r
407   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
408 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
409 void ParseIcsTextMenu(char *icsTextMenuString);\r
410 VOID PopUpMoveDialog(char firstchar);\r
411 VOID PopUpNameDialog(char firstchar);\r
412 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
413 \r
414 /* [AS] */\r
415 int NewGameFRC();\r
416 int GameListOptions();\r
417 \r
418 HWND moveHistoryDialog = NULL;\r
419 BOOLEAN moveHistoryDialogUp = FALSE;\r
420 \r
421 WindowPlacement wpMoveHistory;\r
422 \r
423 HWND evalGraphDialog = NULL;\r
424 BOOLEAN evalGraphDialogUp = FALSE;\r
425 \r
426 WindowPlacement wpEvalGraph;\r
427 \r
428 HWND engineOutputDialog = NULL;\r
429 BOOLEAN engineOutputDialogUp = FALSE;\r
430 \r
431 WindowPlacement wpEngineOutput;\r
432 WindowPlacement wpGameList;\r
433 WindowPlacement wpConsole;\r
434 \r
435 VOID MoveHistoryPopUp();\r
436 VOID MoveHistoryPopDown();\r
437 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
438 BOOL MoveHistoryIsUp();\r
439 \r
440 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
441 VOID EvalGraphPopUp();\r
442 VOID EvalGraphPopDown();\r
443 BOOL EvalGraphIsUp();\r
444 \r
445 VOID EngineOutputPopUp();\r
446 VOID EngineOutputPopDown();\r
447 BOOL EngineOutputIsUp();\r
448 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
449 \r
450 VOID GothicPopUp(char *title, VariantClass variant);\r
451 /*\r
452  * Setting "frozen" should disable all user input other than deleting\r
453  * the window.  We do this while engines are initializing themselves.\r
454  */\r
455 static int frozen = 0;\r
456 static int oldMenuItemState[MENU_BAR_ITEMS];\r
457 void FreezeUI()\r
458 {\r
459   HMENU hmenu;\r
460   int i;\r
461 \r
462   if (frozen) return;\r
463   frozen = 1;\r
464   hmenu = GetMenu(hwndMain);\r
465   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
466     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
467   }\r
468   DrawMenuBar(hwndMain);\r
469 }\r
470 \r
471 /* Undo a FreezeUI */\r
472 void ThawUI()\r
473 {\r
474   HMENU hmenu;\r
475   int i;\r
476 \r
477   if (!frozen) return;\r
478   frozen = 0;\r
479   hmenu = GetMenu(hwndMain);\r
480   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
481     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
482   }\r
483   DrawMenuBar(hwndMain);\r
484 }\r
485 \r
486 static int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
487 \r
488 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
489 #ifdef JAWS\r
490 #include "jaws.c"\r
491 #else\r
492 #define JAWS_INIT\r
493 #define JAWS_ALT_INTERCEPT\r
494 #define JAWS_KB_NAVIGATION\r
495 #define JAWS_MENU_ITEMS\r
496 #define JAWS_SILENCE\r
497 #define JAWS_REPLAY\r
498 #define JAWS_DELETE(X) X\r
499 #define SAYMACHINEMOVE()\r
500 #define SAY(X)\r
501 #endif\r
502 \r
503 /*---------------------------------------------------------------------------*\\r
504  *\r
505  * WinMain\r
506  *\r
507 \*---------------------------------------------------------------------------*/\r
508 \r
509 int APIENTRY\r
510 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
511         LPSTR lpCmdLine, int nCmdShow)\r
512 {\r
513   MSG msg;\r
514   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
515 //  INITCOMMONCONTROLSEX ex;\r
516 \r
517   debugFP = stderr;\r
518 \r
519   LoadLibrary("RICHED32.DLL");\r
520   consoleCF.cbSize = sizeof(CHARFORMAT);\r
521 \r
522   if (!InitApplication(hInstance)) {\r
523     return (FALSE);\r
524   }\r
525   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
526     return (FALSE);\r
527   }\r
528 \r
529   JAWS_INIT\r
530 \r
531 //  InitCommonControlsEx(&ex);\r
532   InitCommonControls();\r
533 \r
534   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
535   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
536   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
537 \r
538   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
539 \r
540   while (GetMessage(&msg, /* message structure */\r
541                     NULL, /* handle of window receiving the message */\r
542                     0,    /* lowest message to examine */\r
543                     0))   /* highest message to examine */\r
544     {\r
545 \r
546       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
547         // [HGM] navigate: switch between all windows with tab\r
548         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
549         int i, currentElement = 0;\r
550 \r
551         // first determine what element of the chain we come from (if any)\r
552         if(appData.icsActive) {\r
553             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
554             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
555         }\r
556         if(engineOutputDialog && EngineOutputIsUp()) {\r
557             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
558             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
559         }\r
560         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
561             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
562         }\r
563         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
564         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
565         if(msg.hwnd == e1)                 currentElement = 2; else\r
566         if(msg.hwnd == e2)                 currentElement = 3; else\r
567         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
568         if(msg.hwnd == mh)                currentElement = 4; else\r
569         if(msg.hwnd == evalGraphDialog)    currentElement = 7; else\r
570         if(msg.hwnd == hText)  currentElement = 5; else\r
571         if(msg.hwnd == hInput) currentElement = 6; else\r
572         for (i = 0; i < N_BUTTONS; i++) {\r
573             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
574         }\r
575 \r
576         // determine where to go to\r
577         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
578           do {\r
579             currentElement = (currentElement + direction) % 7;\r
580             switch(currentElement) {\r
581                 case 0:\r
582                   h = hwndMain; break; // passing this case always makes the loop exit\r
583                 case 1:\r
584                   h = buttonDesc[0].hwnd; break; // could be NULL\r
585                 case 2:\r
586                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
587                   h = e1; break;\r
588                 case 3:\r
589                   if(!EngineOutputIsUp()) continue;\r
590                   h = e2; break;\r
591                 case 4:\r
592                   if(!MoveHistoryIsUp()) continue;\r
593                   h = mh; break;\r
594 //              case 5: // input to eval graph does not seem to get here!\r
595 //                if(!EvalGraphIsUp()) continue;\r
596 //                h = evalGraphDialog; break;\r
597                 case 5:\r
598                   if(!appData.icsActive) continue;\r
599                   SAY("display");\r
600                   h = hText; break;\r
601                 case 6:\r
602                   if(!appData.icsActive) continue;\r
603                   SAY("input");\r
604                   h = hInput; break;\r
605             }\r
606           } while(h == 0);\r
607 \r
608           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
609           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
610           SetFocus(h);\r
611 \r
612           continue; // this message now has been processed\r
613         }\r
614       }\r
615 \r
616       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
617           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
618           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
619           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
620           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
621           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
622           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
623           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) &&\r
624           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
625           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
626         TranslateMessage(&msg); /* Translates virtual key codes */\r
627         DispatchMessage(&msg);  /* Dispatches message to window */\r
628       }\r
629     }\r
630 \r
631 \r
632   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
633 }\r
634 \r
635 /*---------------------------------------------------------------------------*\\r
636  *\r
637  * Initialization functions\r
638  *\r
639 \*---------------------------------------------------------------------------*/\r
640 \r
641 void\r
642 SetUserLogo()\r
643 {   // update user logo if necessary\r
644     static char oldUserName[MSG_SIZ], *curName;\r
645 \r
646     if(appData.autoLogo) {\r
647           curName = UserName();\r
648           if(strcmp(curName, oldUserName)) {\r
649                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
650                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
651                 strcpy(oldUserName, curName);\r
652           }\r
653     }\r
654 }\r
655 \r
656 BOOL\r
657 InitApplication(HINSTANCE hInstance)\r
658 {\r
659   WNDCLASS wc;\r
660 \r
661   /* Fill in window class structure with parameters that describe the */\r
662   /* main window. */\r
663 \r
664   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
665   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
666   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
667   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
668   wc.hInstance     = hInstance;         /* Owner of this class */\r
669   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
670   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
671   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
672   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
673   wc.lpszClassName = szAppName;                 /* Name to register as */\r
674 \r
675   /* Register the window class and return success/failure code. */\r
676   if (!RegisterClass(&wc)) return FALSE;\r
677 \r
678   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
679   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
680   wc.cbClsExtra    = 0;\r
681   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
682   wc.hInstance     = hInstance;\r
683   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
684   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
685   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
686   wc.lpszMenuName  = NULL;\r
687   wc.lpszClassName = szConsoleName;\r
688 \r
689   if (!RegisterClass(&wc)) return FALSE;\r
690   return TRUE;\r
691 }\r
692 \r
693 \r
694 /* Set by InitInstance, used by EnsureOnScreen */\r
695 int screenHeight, screenWidth;\r
696 \r
697 void\r
698 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
699 {\r
700 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
701   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
702   if (*x > screenWidth - 32) *x = 0;\r
703   if (*y > screenHeight - 32) *y = 0;\r
704   if (*x < minX) *x = minX;\r
705   if (*y < minY) *y = minY;\r
706 }\r
707 \r
708 BOOL\r
709 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
710 {\r
711   HWND hwnd; /* Main window handle. */\r
712   int ibs;\r
713   WINDOWPLACEMENT wp;\r
714   char *filepart;\r
715 \r
716   hInst = hInstance;    /* Store instance handle in our global variable */\r
717 \r
718   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
719     *filepart = NULLCHAR;\r
720   } else {\r
721     GetCurrentDirectory(MSG_SIZ, installDir);\r
722   }\r
723   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
724   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
725   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
726   if (appData.debugMode) {\r
727     debugFP = fopen(appData.nameOfDebugFile, "w");\r
728     setbuf(debugFP, NULL);\r
729   }\r
730 \r
731   InitBackEnd1();\r
732 \r
733 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
734 //  InitEngineUCI( installDir, &second );\r
735 \r
736   /* Create a main window for this application instance. */\r
737   hwnd = CreateWindow(szAppName, szTitle,\r
738                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
739                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
740                       NULL, NULL, hInstance, NULL);\r
741   hwndMain = hwnd;\r
742 \r
743   /* If window could not be created, return "failure" */\r
744   if (!hwnd) {\r
745     return (FALSE);\r
746   }\r
747 \r
748   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
749   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
750       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
751 \r
752       if (first.programLogo == NULL && appData.debugMode) {\r
753           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
754       }\r
755   } else if(appData.autoLogo) {\r
756       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
757         char buf[MSG_SIZ];\r
758         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
759         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
760       }\r
761   }\r
762 \r
763   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
764       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
765 \r
766       if (second.programLogo == NULL && appData.debugMode) {\r
767           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
768       }\r
769   } else if(appData.autoLogo) {\r
770       char buf[MSG_SIZ];\r
771       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
772         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
773         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
774       } else\r
775       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
776         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
777         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
778       }\r
779   }\r
780 \r
781   SetUserLogo();\r
782 \r
783   iconWhite = LoadIcon(hInstance, "icon_white");\r
784   iconBlack = LoadIcon(hInstance, "icon_black");\r
785   iconCurrent = iconWhite;\r
786   InitDrawingColors();\r
787   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
788   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
789   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
790     /* Compute window size for each board size, and use the largest\r
791        size that fits on this screen as the default. */\r
792     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
793     if (boardSize == (BoardSize)-1 &&\r
794         winH <= screenHeight\r
795            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
796         && winW <= screenWidth) {\r
797       boardSize = (BoardSize)ibs;\r
798     }\r
799   }\r
800 \r
801   InitDrawingSizes(boardSize, 0);\r
802   InitMenuChecks();\r
803   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
804 \r
805   /* [AS] Load textures if specified */\r
806   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
807   \r
808   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
809       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
810       liteBackTextureMode = appData.liteBackTextureMode;\r
811 \r
812       if (liteBackTexture == NULL && appData.debugMode) {\r
813           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
814       }\r
815   }\r
816   \r
817   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
818       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
819       darkBackTextureMode = appData.darkBackTextureMode;\r
820 \r
821       if (darkBackTexture == NULL && appData.debugMode) {\r
822           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
823       }\r
824   }\r
825 \r
826   mysrandom( (unsigned) time(NULL) );\r
827 \r
828   /* [AS] Restore layout */\r
829   if( wpMoveHistory.visible ) {\r
830       MoveHistoryPopUp();\r
831   }\r
832 \r
833   if( wpEvalGraph.visible ) {\r
834       EvalGraphPopUp();\r
835   }\r
836 \r
837   if( wpEngineOutput.visible ) {\r
838       EngineOutputPopUp();\r
839   }\r
840 \r
841   InitBackEnd2();\r
842 \r
843   /* Make the window visible; update its client area; and return "success" */\r
844   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
845   wp.length = sizeof(WINDOWPLACEMENT);\r
846   wp.flags = 0;\r
847   wp.showCmd = nCmdShow;\r
848   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
849   wp.rcNormalPosition.left = boardX;\r
850   wp.rcNormalPosition.right = boardX + winWidth;\r
851   wp.rcNormalPosition.top = boardY;\r
852   wp.rcNormalPosition.bottom = boardY + winHeight;\r
853   SetWindowPlacement(hwndMain, &wp);\r
854 \r
855   SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
856                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
857 \r
858   if (hwndConsole) {\r
859 #if AOT_CONSOLE\r
860     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
861                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
862 #endif\r
863     ShowWindow(hwndConsole, nCmdShow);\r
864   }\r
865   UpdateWindow(hwnd);\r
866 \r
867   return TRUE;\r
868 \r
869 }\r
870 \r
871 \r
872 typedef enum {\r
873   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
874   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
875   ArgSettingsFilename,\r
876   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
877 } ArgType;\r
878 \r
879 typedef struct {\r
880   char *argName;\r
881   ArgType argType;\r
882   /***\r
883   union {\r
884     String *pString;       // ArgString\r
885     int *pInt;             // ArgInt\r
886     float *pFloat;         // ArgFloat\r
887     Boolean *pBoolean;     // ArgBoolean\r
888     COLORREF *pColor;      // ArgColor\r
889     ColorClass cc;         // ArgAttribs\r
890     String *pFilename;     // ArgFilename\r
891     BoardSize *pBoardSize; // ArgBoardSize\r
892     int whichFont;         // ArgFont\r
893     DCB *pDCB;             // ArgCommSettings\r
894     String *pFilename;     // ArgSettingsFilename\r
895   } argLoc;\r
896   ***/\r
897   LPVOID argLoc;\r
898   BOOL save;\r
899 } ArgDescriptor;\r
900 \r
901 int junk;\r
902 ArgDescriptor argDescriptors[] = {\r
903   /* positional arguments */\r
904   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
905   { "", ArgNone, NULL },\r
906   /* keyword arguments */\r
907   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
908   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
909   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
910   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
911   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
912   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
913   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
914   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
915   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
916   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
917   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
918   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
919   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
920   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
921   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
922   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
923   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
924   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
925     FALSE },\r
926   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
927     FALSE },\r
928   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
929     FALSE },\r
930   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
931   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
932     FALSE },\r
933   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
934   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
935   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
936   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
937   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
938   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
939   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
940   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
941   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
942   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
943   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
944   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
945   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
946   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
947   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
948   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
949   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
950   /*!!bitmapDirectory?*/\r
951   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
952   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
953   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
954   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
955   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
956   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
957   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
958   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
959   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
960   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
961   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
962   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
963   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
964   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
965   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
966   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
967   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
968   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
969   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
970   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
971   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
972   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
973   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
974   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
975   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
976   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
977   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
978   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
979   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
980   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
981   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
982   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
983   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
984   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
985   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
986   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
987   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
988   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
989   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
990   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
991   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
992   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
993   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
994   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
995   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
996   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
997   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
998   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
999   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1000   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1001   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1002   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1003   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1004   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1005   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1006   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1007   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1008   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1009   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1010   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1011   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1012   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1013   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1014   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1015   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1016   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1017   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1018   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1019   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1020   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1021   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1022   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1023   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1024   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1025   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1026   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1027   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1028   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1029   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1030   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1031   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1032   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1033   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1034   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1035   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1036   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1037   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1038   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1039   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1040   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1041   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1042   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1043   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1044   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1045     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1046   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1047   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1048   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1049   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1050   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1051   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1052   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1053   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1054     TRUE }, /* must come after all fonts */\r
1055   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1056   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1057     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1058   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1059   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1060   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1061   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1062   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1063   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1064   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1065   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1066   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1067   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1068   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1069   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1070   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1071   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1072   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1073   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1074   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1075   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1076   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1077   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1078   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1079   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1080   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1081   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1082   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1083   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1084   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1085   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1086 #if 0\r
1087   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1088   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1089 #endif\r
1090   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1091   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1092   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1093   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1094   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1095   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1096   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1097   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1098   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1099   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1100   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1101   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1102   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1103   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1104   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1105   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1106   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1107   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1108   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1109   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1110   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1111   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1112   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1113   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1114   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1115   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1116   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1117   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1118   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1119   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1120   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1121   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1122   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1123   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1124   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1125   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1126   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1127   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1128   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1129   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1130   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1131   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1132   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1133   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1134   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1135   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1136   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1137   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1138   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1139   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1140   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1141   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1142   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1143   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1144   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1145   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1146   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1147   { "highlightLastMove", ArgBoolean,\r
1148     (LPVOID) &appData.highlightLastMove, TRUE },\r
1149   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1150   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1151   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1152   { "highlightDragging", ArgBoolean,\r
1153     (LPVOID) &appData.highlightDragging, TRUE },\r
1154   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1155   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1156   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1157   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1158   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1159   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1160   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1161   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1162   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1163   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1164   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1165   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1166   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1167   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1168   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1169   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1170   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1171   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1172   { "soundShout", ArgFilename,\r
1173     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1174   { "soundSShout", ArgFilename,\r
1175     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1176   { "soundChannel1", ArgFilename,\r
1177     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1178   { "soundChannel", ArgFilename,\r
1179     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1180   { "soundKibitz", ArgFilename,\r
1181     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1182   { "soundTell", ArgFilename,\r
1183     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1184   { "soundChallenge", ArgFilename,\r
1185     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1186   { "soundRequest", ArgFilename,\r
1187     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1188   { "soundSeek", ArgFilename,\r
1189     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1190   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1191   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1192   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1193   { "soundIcsLoss", ArgFilename, \r
1194     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1195   { "soundIcsDraw", ArgFilename, \r
1196     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1197   { "soundIcsUnfinished", ArgFilename, \r
1198     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1199   { "soundIcsAlarm", ArgFilename, \r
1200     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1201   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1202   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1203   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1204   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1205   { "reuseChessPrograms", ArgBoolean,\r
1206     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1207   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1208   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1209   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1210   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1211   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1212   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1213   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1214   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1215   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1216   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1217   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1218   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1219   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1220   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1221   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1222     TRUE },\r
1223   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1224     TRUE },\r
1225   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1226   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1227   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1228   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1229   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1230   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1231   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1232   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1233   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1234   /* [AS] New features */\r
1235   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1236   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1237   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1238   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1239   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1240   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1241   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1242   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1243   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1244   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1245   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1246   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1247   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1248   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1249   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1250   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1251   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1252   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1253   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1254   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1255   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1256   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1257   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1258   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1259   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1260   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1261   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1262   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1263   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1264   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1265   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1266   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1267   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1268   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1269   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1270   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1271   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1272   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1273   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1274   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1275   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1276   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1277   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1278   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1279   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1280   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1281   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1282   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1283   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1284   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1285 \r
1286   /* [HGM] board-size, adjudication and misc. options */\r
1287   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1288   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1289   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1290   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1291   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1292   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1293   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1294   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1295   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1296   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1297   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1298   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1299   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1300   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1301   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1302   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1303   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1304   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1305   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1306   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1307   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1308   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1309   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1310   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1311   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1312   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1313   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1314   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1315   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1316   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1317   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1318 \r
1319 #ifdef ZIPPY\r
1320   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1321   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1322   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1323   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1324   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1325   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1326   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1327   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1328   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1329   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1330   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1331   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1332   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1333     FALSE },\r
1334   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1335   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1336   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1337   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1338   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1339   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1340   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1341     FALSE },\r
1342   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1343   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1344   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1345   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1346   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1347   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1348   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1349   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1350   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1351   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1352   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1353   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1354   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1355   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1356   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1357   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1358   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1359   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1360   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1361 #endif\r
1362   /* [HGM] options for broadcasting and time odds */\r
1363   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1364   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1365   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1366   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1367   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1368   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1369   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1370   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1371   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1372   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1373   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1374 \r
1375   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1376   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1377   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1378   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1379   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1380   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1381   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1382   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1383   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1384   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1385   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1386   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1387   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1388   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1389   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1390   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1391   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1392   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1393   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1394   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1395   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1396   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1397   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1398   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1399   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1400   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1401   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1402   /* [AS] Layout stuff */\r
1403   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1404   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1405   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1406   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1407   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1408 \r
1409   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1410   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1411   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1412   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1413   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1414 \r
1415   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1416   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1417   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1418   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1419   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1420 \r
1421   { NULL, ArgNone, NULL, FALSE }\r
1422 };\r
1423 \r
1424 \r
1425 /* Kludge for indirection files on command line */\r
1426 char* lastIndirectionFilename;\r
1427 ArgDescriptor argDescriptorIndirection =\r
1428 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1429 \r
1430 \r
1431 VOID\r
1432 ExitArgError(char *msg, char *badArg)\r
1433 {\r
1434   char buf[MSG_SIZ];\r
1435 \r
1436   sprintf(buf, "%s %s", msg, badArg);\r
1437   DisplayFatalError(buf, 0, 2);\r
1438   exit(2);\r
1439 }\r
1440 \r
1441 /* Command line font name parser.  NULL name means do nothing.\r
1442    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1443    For backward compatibility, syntax without the colon is also\r
1444    accepted, but font names with digits in them won't work in that case.\r
1445 */\r
1446 VOID\r
1447 ParseFontName(char *name, MyFontParams *mfp)\r
1448 {\r
1449   char *p, *q;\r
1450   if (name == NULL) return;\r
1451   p = name;\r
1452   q = strchr(p, ':');\r
1453   if (q) {\r
1454     if (q - p >= sizeof(mfp->faceName))\r
1455       ExitArgError("Font name too long:", name);\r
1456     memcpy(mfp->faceName, p, q - p);\r
1457     mfp->faceName[q - p] = NULLCHAR;\r
1458     p = q + 1;\r
1459   } else {\r
1460     q = mfp->faceName;\r
1461     while (*p && !isdigit(*p)) {\r
1462       *q++ = *p++;\r
1463       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1464         ExitArgError("Font name too long:", name);\r
1465     }\r
1466     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1467     *q = NULLCHAR;\r
1468   }\r
1469   if (!*p) ExitArgError("Font point size missing:", name);\r
1470   mfp->pointSize = (float) atof(p);\r
1471   mfp->bold = (strchr(p, 'b') != NULL);\r
1472   mfp->italic = (strchr(p, 'i') != NULL);\r
1473   mfp->underline = (strchr(p, 'u') != NULL);\r
1474   mfp->strikeout = (strchr(p, 's') != NULL);\r
1475 }\r
1476 \r
1477 /* Color name parser.\r
1478    X version accepts X color names, but this one\r
1479    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1480 COLORREF\r
1481 ParseColorName(char *name)\r
1482 {\r
1483   int red, green, blue, count;\r
1484   char buf[MSG_SIZ];\r
1485 \r
1486   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1487   if (count != 3) {\r
1488     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1489       &red, &green, &blue);\r
1490   }\r
1491   if (count != 3) {\r
1492     sprintf(buf, "Can't parse color name %s", name);\r
1493     DisplayError(buf, 0);\r
1494     return RGB(0, 0, 0);\r
1495   }\r
1496   return PALETTERGB(red, green, blue);\r
1497 }\r
1498 \r
1499 \r
1500 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1501 {\r
1502   char *e = argValue;\r
1503   int eff = 0;\r
1504 \r
1505   while (*e) {\r
1506     if (*e == 'b')      eff |= CFE_BOLD;\r
1507     else if (*e == 'i') eff |= CFE_ITALIC;\r
1508     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1509     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1510     else if (*e == '#' || isdigit(*e)) break;\r
1511     e++;\r
1512   }\r
1513   *effects = eff;\r
1514   *color   = ParseColorName(e);\r
1515 }\r
1516 \r
1517 \r
1518 BoardSize\r
1519 ParseBoardSize(char *name)\r
1520 {\r
1521   BoardSize bs = SizeTiny;\r
1522   while (sizeInfo[bs].name != NULL) {\r
1523     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1524     bs++;\r
1525   }\r
1526   ExitArgError("Unrecognized board size value", name);\r
1527   return bs; /* not reached */\r
1528 }\r
1529 \r
1530 \r
1531 char\r
1532 StringGet(void *getClosure)\r
1533 {\r
1534   char **p = (char **) getClosure;\r
1535   return *((*p)++);\r
1536 }\r
1537 \r
1538 char\r
1539 FileGet(void *getClosure)\r
1540 {\r
1541   int c;\r
1542   FILE* f = (FILE*) getClosure;\r
1543 \r
1544   c = getc(f);\r
1545   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1546   if (c == EOF)\r
1547     return NULLCHAR;\r
1548   else\r
1549     return (char) c;\r
1550 }\r
1551 \r
1552 /* Parse settings file named "name". If file found, return the\r
1553    full name in fullname and return TRUE; else return FALSE */\r
1554 BOOLEAN\r
1555 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1556 {\r
1557   char *dummy;\r
1558   FILE *f;\r
1559   int ok; char buf[MSG_SIZ];\r
1560 \r
1561   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1562   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1563     sprintf(buf, "%s.ini", name);\r
1564     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1565   }\r
1566   if (ok) {\r
1567     f = fopen(fullname, "r");\r
1568     if (f != NULL) {\r
1569       ParseArgs(FileGet, f);\r
1570       fclose(f);\r
1571       return TRUE;\r
1572     }\r
1573   }\r
1574   return FALSE;\r
1575 }\r
1576 \r
1577 VOID\r
1578 ParseArgs(GetFunc get, void *cl)\r
1579 {\r
1580   char argName[ARG_MAX];\r
1581   char argValue[ARG_MAX];\r
1582   ArgDescriptor *ad;\r
1583   char start;\r
1584   char *q;\r
1585   int i, octval;\r
1586   char ch;\r
1587   int posarg = 0;\r
1588 \r
1589   ch = get(cl);\r
1590   for (;;) {\r
1591     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1592     if (ch == NULLCHAR) break;\r
1593     if (ch == ';') {\r
1594       /* Comment to end of line */\r
1595       ch = get(cl);\r
1596       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1597       continue;\r
1598     } else if (ch == '/' || ch == '-') {\r
1599       /* Switch */\r
1600       q = argName;\r
1601       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1602              ch != '\n' && ch != '\t') {\r
1603         *q++ = ch;\r
1604         ch = get(cl);\r
1605       }\r
1606       *q = NULLCHAR;\r
1607 \r
1608       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1609         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1610 \r
1611       if (ad->argName == NULL)\r
1612         ExitArgError("Unrecognized argument", argName);\r
1613 \r
1614     } else if (ch == '@') {\r
1615       /* Indirection file */\r
1616       ad = &argDescriptorIndirection;\r
1617       ch = get(cl);\r
1618     } else {\r
1619       /* Positional argument */\r
1620       ad = &argDescriptors[posarg++];\r
1621       strcpy(argName, ad->argName);\r
1622     }\r
1623 \r
1624     if (ad->argType == ArgTrue) {\r
1625       *(Boolean *) ad->argLoc = TRUE;\r
1626       continue;\r
1627     }\r
1628     if (ad->argType == ArgFalse) {\r
1629       *(Boolean *) ad->argLoc = FALSE;\r
1630       continue;\r
1631     }\r
1632 \r
1633     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1634     if (ch == NULLCHAR || ch == '\n') {\r
1635       ExitArgError("No value provided for argument", argName);\r
1636     }\r
1637     q = argValue;\r
1638     if (ch == '{') {\r
1639       // Quoting with { }.  No characters have to (or can) be escaped.\r
1640       // Thus the string cannot contain a '}' character.\r
1641       start = ch;\r
1642       ch = get(cl);\r
1643       while (start) {\r
1644         switch (ch) {\r
1645         case NULLCHAR:\r
1646           start = NULLCHAR;\r
1647           break;\r
1648           \r
1649         case '}':\r
1650           ch = get(cl);\r
1651           start = NULLCHAR;\r
1652           break;\r
1653 \r
1654         default:\r
1655           *q++ = ch;\r
1656           ch = get(cl);\r
1657           break;\r
1658         }\r
1659       }   \r
1660     } else if (ch == '\'' || ch == '"') {\r
1661       // Quoting with ' ' or " ", with \ as escape character.\r
1662       // Inconvenient for long strings that may contain Windows filenames.\r
1663       start = ch;\r
1664       ch = get(cl);\r
1665       while (start) {\r
1666         switch (ch) {\r
1667         case NULLCHAR:\r
1668           start = NULLCHAR;\r
1669           break;\r
1670 \r
1671         default:\r
1672         not_special:\r
1673           *q++ = ch;\r
1674           ch = get(cl);\r
1675           break;\r
1676 \r
1677         case '\'':\r
1678         case '\"':\r
1679           if (ch == start) {\r
1680             ch = get(cl);\r
1681             start = NULLCHAR;\r
1682             break;\r
1683           } else {\r
1684             goto not_special;\r
1685           }\r
1686 \r
1687         case '\\':\r
1688           if (ad->argType == ArgFilename\r
1689               || ad->argType == ArgSettingsFilename) {\r
1690               goto not_special;\r
1691           }\r
1692           ch = get(cl);\r
1693           switch (ch) {\r
1694           case NULLCHAR:\r
1695             ExitArgError("Incomplete \\ escape in value for", argName);\r
1696             break;\r
1697           case 'n':\r
1698             *q++ = '\n';\r
1699             ch = get(cl);\r
1700             break;\r
1701           case 'r':\r
1702             *q++ = '\r';\r
1703             ch = get(cl);\r
1704             break;\r
1705           case 't':\r
1706             *q++ = '\t';\r
1707             ch = get(cl);\r
1708             break;\r
1709           case 'b':\r
1710             *q++ = '\b';\r
1711             ch = get(cl);\r
1712             break;\r
1713           case 'f':\r
1714             *q++ = '\f';\r
1715             ch = get(cl);\r
1716             break;\r
1717           default:\r
1718             octval = 0;\r
1719             for (i = 0; i < 3; i++) {\r
1720               if (ch >= '0' && ch <= '7') {\r
1721                 octval = octval*8 + (ch - '0');\r
1722                 ch = get(cl);\r
1723               } else {\r
1724                 break;\r
1725               }\r
1726             }\r
1727             if (i > 0) {\r
1728               *q++ = (char) octval;\r
1729             } else {\r
1730               *q++ = ch;\r
1731               ch = get(cl);\r
1732             }\r
1733             break;\r
1734           }\r
1735           break;\r
1736         }\r
1737       }\r
1738     } else {\r
1739       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1740         *q++ = ch;\r
1741         ch = get(cl);\r
1742       }\r
1743     }\r
1744     *q = NULLCHAR;\r
1745 \r
1746     switch (ad->argType) {\r
1747     case ArgInt:\r
1748       *(int *) ad->argLoc = atoi(argValue);\r
1749       break;\r
1750 \r
1751     case ArgX:\r
1752       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1753       break;\r
1754 \r
1755     case ArgY:\r
1756       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1757       break;\r
1758 \r
1759     case ArgZ:\r
1760       *(int *) ad->argLoc = atoi(argValue);\r
1761       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1762       break;\r
1763 \r
1764     case ArgFloat:\r
1765       *(float *) ad->argLoc = (float) atof(argValue);\r
1766       break;\r
1767 \r
1768     case ArgString:\r
1769     case ArgFilename:\r
1770       *(char **) ad->argLoc = strdup(argValue);\r
1771       break;\r
1772 \r
1773     case ArgSettingsFilename:\r
1774       {\r
1775         char fullname[MSG_SIZ];\r
1776         if (ParseSettingsFile(argValue, fullname)) {\r
1777           if (ad->argLoc != NULL) {\r
1778             *(char **) ad->argLoc = strdup(fullname);\r
1779           }\r
1780         } else {\r
1781           if (ad->argLoc != NULL) {\r
1782           } else {\r
1783             ExitArgError("Failed to open indirection file", argValue);\r
1784           }\r
1785         }\r
1786       }\r
1787       break;\r
1788 \r
1789     case ArgBoolean:\r
1790       switch (argValue[0]) {\r
1791       case 't':\r
1792       case 'T':\r
1793         *(Boolean *) ad->argLoc = TRUE;\r
1794         break;\r
1795       case 'f':\r
1796       case 'F':\r
1797         *(Boolean *) ad->argLoc = FALSE;\r
1798         break;\r
1799       default:\r
1800         ExitArgError("Unrecognized boolean argument value", argValue);\r
1801         break;\r
1802       }\r
1803       break;\r
1804 \r
1805     case ArgColor:\r
1806       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1807       break;\r
1808 \r
1809     case ArgAttribs: {\r
1810       ColorClass cc = (ColorClass)ad->argLoc;\r
1811       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1812       }\r
1813       break;\r
1814       \r
1815     case ArgBoardSize:\r
1816       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1817       break;\r
1818 \r
1819     case ArgFont:\r
1820       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1821       break;\r
1822 \r
1823     case ArgCommSettings:\r
1824       ParseCommSettings(argValue, &dcb);\r
1825       break;\r
1826 \r
1827     case ArgNone:\r
1828       ExitArgError("Unrecognized argument", argValue);\r
1829       break;\r
1830     case ArgTrue:\r
1831     case ArgFalse: ;\r
1832     }\r
1833   }\r
1834 }\r
1835 \r
1836 VOID\r
1837 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1838 {\r
1839   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1840   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1841   DeleteDC(hdc);\r
1842   lf->lfWidth = 0;\r
1843   lf->lfEscapement = 0;\r
1844   lf->lfOrientation = 0;\r
1845   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1846   lf->lfItalic = mfp->italic;\r
1847   lf->lfUnderline = mfp->underline;\r
1848   lf->lfStrikeOut = mfp->strikeout;\r
1849   lf->lfCharSet = DEFAULT_CHARSET;\r
1850   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1851   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1852   lf->lfQuality = DEFAULT_QUALITY;\r
1853   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1854   strcpy(lf->lfFaceName, mfp->faceName);\r
1855 }\r
1856 \r
1857 VOID\r
1858 CreateFontInMF(MyFont *mf)\r
1859 {\r
1860   LFfromMFP(&mf->lf, &mf->mfp);\r
1861   if (mf->hf) DeleteObject(mf->hf);\r
1862   mf->hf = CreateFontIndirect(&mf->lf);\r
1863 }\r
1864 \r
1865 VOID\r
1866 SetDefaultTextAttribs()\r
1867 {\r
1868   ColorClass cc;\r
1869   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1870     ParseAttribs(&textAttribs[cc].color, \r
1871                  &textAttribs[cc].effects, \r
1872                  defaultTextAttribs[cc]);\r
1873   }\r
1874 }\r
1875 \r
1876 VOID\r
1877 SetDefaultSounds()\r
1878 {\r
1879   ColorClass cc;\r
1880   SoundClass sc;\r
1881   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1882     textAttribs[cc].sound.name = strdup("");\r
1883     textAttribs[cc].sound.data = NULL;\r
1884   }\r
1885   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1886     sounds[sc].name = strdup("");\r
1887     sounds[sc].data = NULL;\r
1888   }\r
1889   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1890 }\r
1891 \r
1892 VOID\r
1893 LoadAllSounds()\r
1894 {\r
1895   ColorClass cc;\r
1896   SoundClass sc;\r
1897   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1898     MyLoadSound(&textAttribs[cc].sound);\r
1899   }\r
1900   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1901     MyLoadSound(&sounds[sc]);\r
1902   }\r
1903 }\r
1904 \r
1905 VOID\r
1906 InitAppData(LPSTR lpCmdLine)\r
1907 {\r
1908   int i, j;\r
1909   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1910   char *dummy, *p;\r
1911 \r
1912   programName = szAppName;\r
1913 \r
1914   /* Initialize to defaults */\r
1915   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1916   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1917   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1918   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1919   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1920   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1921   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1922   SetDefaultTextAttribs();\r
1923   SetDefaultSounds();\r
1924   appData.movesPerSession = MOVES_PER_SESSION;\r
1925   appData.initString = INIT_STRING;\r
1926   appData.secondInitString = INIT_STRING;\r
1927   appData.firstComputerString = COMPUTER_STRING;\r
1928   appData.secondComputerString = COMPUTER_STRING;\r
1929   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1930   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1931   appData.firstPlaysBlack = FALSE;\r
1932   appData.noChessProgram = FALSE;\r
1933   chessProgram = FALSE;\r
1934   appData.firstHost = FIRST_HOST;\r
1935   appData.secondHost = SECOND_HOST;\r
1936   appData.firstDirectory = FIRST_DIRECTORY;\r
1937   appData.secondDirectory = SECOND_DIRECTORY;\r
1938   appData.bitmapDirectory = "";\r
1939   appData.remoteShell = REMOTE_SHELL;\r
1940   appData.remoteUser = "";\r
1941   appData.timeDelay = TIME_DELAY;\r
1942   appData.timeControl = TIME_CONTROL;\r
1943   appData.timeIncrement = TIME_INCREMENT;\r
1944   appData.icsActive = FALSE;\r
1945   appData.icsHost = "";\r
1946   appData.icsPort = ICS_PORT;\r
1947   appData.icsCommPort = ICS_COMM_PORT;\r
1948   appData.icsLogon = ICS_LOGON;\r
1949   appData.icsHelper = "";\r
1950   appData.useTelnet = FALSE;\r
1951   appData.telnetProgram = TELNET_PROGRAM;\r
1952   appData.gateway = "";\r
1953   appData.loadGameFile = "";\r
1954   appData.loadGameIndex = 0;\r
1955   appData.saveGameFile = "";\r
1956   appData.autoSaveGames = FALSE;\r
1957   appData.loadPositionFile = "";\r
1958   appData.loadPositionIndex = 1;\r
1959   appData.savePositionFile = "";\r
1960   appData.matchMode = FALSE;\r
1961   appData.matchGames = 0;\r
1962   appData.monoMode = FALSE;\r
1963   appData.debugMode = FALSE;\r
1964   appData.clockMode = TRUE;\r
1965   boardSize = (BoardSize) -1; /* determine by screen size */\r
1966   appData.Iconic = FALSE; /*unused*/\r
1967   appData.searchTime = "";\r
1968   appData.searchDepth = 0;\r
1969   appData.showCoords = FALSE;\r
1970   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1971   appData.autoCallFlag = FALSE;\r
1972   appData.flipView = FALSE;\r
1973   appData.autoFlipView = TRUE;\r
1974   appData.cmailGameName = "";\r
1975   appData.alwaysPromoteToQueen = FALSE;\r
1976   appData.oldSaveStyle = FALSE;\r
1977   appData.quietPlay = FALSE;\r
1978   appData.showThinking = FALSE;\r
1979   appData.ponderNextMove = TRUE;\r
1980   appData.periodicUpdates = TRUE;\r
1981   appData.popupExitMessage = TRUE;\r
1982   appData.popupMoveErrors = FALSE;\r
1983   appData.autoObserve = FALSE;\r
1984   appData.autoComment = FALSE;\r
1985   appData.animate = TRUE;\r
1986   appData.animSpeed = 10;\r
1987   appData.animateDragging = TRUE;\r
1988   appData.highlightLastMove = TRUE;\r
1989   appData.getMoveList = TRUE;\r
1990   appData.testLegality = TRUE;\r
1991   appData.premove = TRUE;\r
1992   appData.premoveWhite = FALSE;\r
1993   appData.premoveWhiteText = "";\r
1994   appData.premoveBlack = FALSE;\r
1995   appData.premoveBlackText = "";\r
1996   appData.icsAlarm = TRUE;\r
1997   appData.icsAlarmTime = 5000;\r
1998   appData.autoRaiseBoard = TRUE;\r
1999   appData.localLineEditing = TRUE;\r
2000   appData.colorize = TRUE;\r
2001   appData.reuseFirst = TRUE;\r
2002   appData.reuseSecond = TRUE;\r
2003   appData.blindfold = FALSE;\r
2004   appData.icsEngineAnalyze = FALSE;\r
2005   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2006   dcb.DCBlength = sizeof(DCB);\r
2007   dcb.BaudRate = 9600;\r
2008   dcb.fBinary = TRUE;\r
2009   dcb.fParity = FALSE;\r
2010   dcb.fOutxCtsFlow = FALSE;\r
2011   dcb.fOutxDsrFlow = FALSE;\r
2012   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2013   dcb.fDsrSensitivity = FALSE;\r
2014   dcb.fTXContinueOnXoff = TRUE;\r
2015   dcb.fOutX = FALSE;\r
2016   dcb.fInX = FALSE;\r
2017   dcb.fNull = FALSE;\r
2018   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2019   dcb.fAbortOnError = FALSE;\r
2020   dcb.ByteSize = 7;\r
2021   dcb.Parity = SPACEPARITY;\r
2022   dcb.StopBits = ONESTOPBIT;\r
2023   settingsFileName = SETTINGS_FILE;\r
2024   saveSettingsOnExit = TRUE;\r
2025   boardX = CW_USEDEFAULT;\r
2026   boardY = CW_USEDEFAULT;\r
2027   analysisX = CW_USEDEFAULT; \r
2028   analysisY = CW_USEDEFAULT; \r
2029   analysisW = CW_USEDEFAULT;\r
2030   analysisH = CW_USEDEFAULT;\r
2031   commentX = CW_USEDEFAULT; \r
2032   commentY = CW_USEDEFAULT; \r
2033   commentW = CW_USEDEFAULT;\r
2034   commentH = CW_USEDEFAULT;\r
2035   editTagsX = CW_USEDEFAULT; \r
2036   editTagsY = CW_USEDEFAULT; \r
2037   editTagsW = CW_USEDEFAULT;\r
2038   editTagsH = CW_USEDEFAULT;\r
2039   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2040   icsNames = ICS_NAMES;\r
2041   firstChessProgramNames = FCP_NAMES;\r
2042   secondChessProgramNames = SCP_NAMES;\r
2043   appData.initialMode = "";\r
2044   appData.variant = "normal";\r
2045   appData.firstProtocolVersion = PROTOVER;\r
2046   appData.secondProtocolVersion = PROTOVER;\r
2047   appData.showButtonBar = TRUE;\r
2048 \r
2049    /* [AS] New properties (see comments in header file) */\r
2050   appData.firstScoreIsAbsolute = FALSE;\r
2051   appData.secondScoreIsAbsolute = FALSE;\r
2052   appData.saveExtendedInfoInPGN = FALSE;\r
2053   appData.hideThinkingFromHuman = FALSE;\r
2054   appData.liteBackTextureFile = "";\r
2055   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2056   appData.darkBackTextureFile = "";\r
2057   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2058   appData.renderPiecesWithFont = "";\r
2059   appData.fontToPieceTable = "";\r
2060   appData.fontBackColorWhite = 0;\r
2061   appData.fontForeColorWhite = 0;\r
2062   appData.fontBackColorBlack = 0;\r
2063   appData.fontForeColorBlack = 0;\r
2064   appData.fontPieceSize = 80;\r
2065   appData.overrideLineGap = 1;\r
2066   appData.adjudicateLossThreshold = 0;\r
2067   appData.delayBeforeQuit = 0;\r
2068   appData.delayAfterQuit = 0;\r
2069   appData.nameOfDebugFile = "winboard.debug";\r
2070   appData.pgnEventHeader = "Computer Chess Game";\r
2071   appData.defaultFrcPosition = -1;\r
2072   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2073   appData.saveOutOfBookInfo = TRUE;\r
2074   appData.showEvalInMoveHistory = TRUE;\r
2075   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2076   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2077   appData.highlightMoveWithArrow = FALSE;\r
2078   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2079   appData.useStickyWindows = TRUE;\r
2080   appData.adjudicateDrawMoves = 0;\r
2081   appData.autoDisplayComment = TRUE;\r
2082   appData.autoDisplayTags = TRUE;\r
2083   appData.firstIsUCI = FALSE;\r
2084   appData.secondIsUCI = FALSE;\r
2085   appData.firstHasOwnBookUCI = TRUE;\r
2086   appData.secondHasOwnBookUCI = TRUE;\r
2087   appData.polyglotDir = "";\r
2088   appData.usePolyglotBook = FALSE;\r
2089   appData.polyglotBook = "";\r
2090   appData.defaultHashSize = 64;\r
2091   appData.defaultCacheSizeEGTB = 4;\r
2092   appData.defaultPathEGTB = "c:\\egtb";\r
2093   appData.firstOptions = "";\r
2094   appData.secondOptions = "";\r
2095 \r
2096   InitWindowPlacement( &wpGameList );\r
2097   InitWindowPlacement( &wpMoveHistory );\r
2098   InitWindowPlacement( &wpEvalGraph );\r
2099   InitWindowPlacement( &wpEngineOutput );\r
2100   InitWindowPlacement( &wpConsole );\r
2101 \r
2102   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2103   appData.NrFiles      = -1;\r
2104   appData.NrRanks      = -1;\r
2105   appData.holdingsSize = -1;\r
2106   appData.testClaims   = FALSE;\r
2107   appData.checkMates   = FALSE;\r
2108   appData.materialDraws= FALSE;\r
2109   appData.trivialDraws = FALSE;\r
2110   appData.ruleMoves    = 51;\r
2111   appData.drawRepeats  = 6;\r
2112   appData.matchPause   = 10000;\r
2113   appData.alphaRank    = FALSE;\r
2114   appData.allWhite     = FALSE;\r
2115   appData.upsideDown   = FALSE;\r
2116   appData.serverPause  = 15;\r
2117   appData.serverMovesName   = NULL;\r
2118   appData.suppressLoadMoves = FALSE;\r
2119   appData.firstTimeOdds  = 1;\r
2120   appData.secondTimeOdds = 1;\r
2121   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2122   appData.secondAccumulateTC = 1;\r
2123   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2124   appData.secondNPS = -1;\r
2125   appData.engineComments = 1;\r
2126   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2127   appData.egtFormats = "";\r
2128 \r
2129 #ifdef ZIPPY\r
2130   appData.zippyTalk = ZIPPY_TALK;\r
2131   appData.zippyPlay = ZIPPY_PLAY;\r
2132   appData.zippyLines = ZIPPY_LINES;\r
2133   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2134   appData.zippyPassword = ZIPPY_PASSWORD;\r
2135   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2136   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2137   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2138   appData.zippyUseI = ZIPPY_USE_I;\r
2139   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2140   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2141   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2142   appData.zippyGameStart = ZIPPY_GAME_START;\r
2143   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2144   appData.zippyAbort = ZIPPY_ABORT;\r
2145   appData.zippyVariants = ZIPPY_VARIANTS;\r
2146   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2147   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2148 #endif\r
2149 \r
2150   /* Point font array elements to structures and\r
2151      parse default font names */\r
2152   for (i=0; i<NUM_FONTS; i++) {\r
2153     for (j=0; j<NUM_SIZES; j++) {\r
2154       font[j][i] = &fontRec[j][i];\r
2155       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2156     }\r
2157   }\r
2158   \r
2159   /* Parse default settings file if any */\r
2160   if (ParseSettingsFile(settingsFileName, buf)) {\r
2161     settingsFileName = strdup(buf);\r
2162   }\r
2163 \r
2164   /* Parse command line */\r
2165   ParseArgs(StringGet, &lpCmdLine);\r
2166 \r
2167   /* [HGM] make sure board size is acceptable */\r
2168   if(appData.NrFiles > BOARD_SIZE ||\r
2169      appData.NrRanks > BOARD_SIZE   )\r
2170       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2171 \r
2172   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2173    * with options from the command line, we now make an even higher priority\r
2174    * overrule by WB options attached to the engine command line. This so that\r
2175    * tournament managers can use WB options (such as /timeOdds) that follow\r
2176    * the engines.\r
2177    */\r
2178   if(appData.firstChessProgram != NULL) {\r
2179       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2180       static char *f = "first";\r
2181       char buf[MSG_SIZ], *q = buf;\r
2182       if(p != NULL) { // engine command line contains WinBoard options\r
2183           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2184           ParseArgs(StringGet, &q);\r
2185           p[-1] = 0; // cut them offengine command line\r
2186       }\r
2187   }\r
2188   // now do same for second chess program\r
2189   if(appData.secondChessProgram != NULL) {\r
2190       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2191       static char *s = "second";\r
2192       char buf[MSG_SIZ], *q = buf;\r
2193       if(p != NULL) { // engine command line contains WinBoard options\r
2194           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2195           ParseArgs(StringGet, &q);\r
2196           p[-1] = 0; // cut them offengine command line\r
2197       }\r
2198   }\r
2199 \r
2200 \r
2201   /* Propagate options that affect others */\r
2202   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2203   if (appData.icsActive || appData.noChessProgram) {\r
2204      chessProgram = FALSE;  /* not local chess program mode */\r
2205   }\r
2206 \r
2207   /* Open startup dialog if needed */\r
2208   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2209       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2210       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2211                         *appData.secondChessProgram == NULLCHAR))) {\r
2212     FARPROC lpProc;\r
2213     \r
2214     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2215     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2216     FreeProcInstance(lpProc);\r
2217   }\r
2218 \r
2219   /* Make sure save files land in the right (?) directory */\r
2220   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2221     appData.saveGameFile = strdup(buf);\r
2222   }\r
2223   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2224     appData.savePositionFile = strdup(buf);\r
2225   }\r
2226 \r
2227   /* Finish initialization for fonts and sounds */\r
2228   for (i=0; i<NUM_FONTS; i++) {\r
2229     for (j=0; j<NUM_SIZES; j++) {\r
2230       CreateFontInMF(font[j][i]);\r
2231     }\r
2232   }\r
2233   /* xboard, and older WinBoards, controlled the move sound with the\r
2234      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2235      always turn the option on (so that the backend will call us),\r
2236      then let the user turn the sound off by setting it to silence if\r
2237      desired.  To accommodate old winboard.ini files saved by old\r
2238      versions of WinBoard, we also turn off the sound if the option\r
2239      was initially set to false. */\r
2240   if (!appData.ringBellAfterMoves) {\r
2241     sounds[(int)SoundMove].name = strdup("");\r
2242     appData.ringBellAfterMoves = TRUE;\r
2243   }\r
2244   GetCurrentDirectory(MSG_SIZ, currDir);\r
2245   SetCurrentDirectory(installDir);\r
2246   LoadAllSounds();\r
2247   SetCurrentDirectory(currDir);\r
2248 \r
2249   p = icsTextMenuString;\r
2250   if (p[0] == '@') {\r
2251     FILE* f = fopen(p + 1, "r");\r
2252     if (f == NULL) {\r
2253       DisplayFatalError(p + 1, errno, 2);\r
2254       return;\r
2255     }\r
2256     i = fread(buf, 1, sizeof(buf)-1, f);\r
2257     fclose(f);\r
2258     buf[i] = NULLCHAR;\r
2259     p = buf;\r
2260   }\r
2261   ParseIcsTextMenu(strdup(p));\r
2262 }\r
2263 \r
2264 \r
2265 VOID\r
2266 InitMenuChecks()\r
2267 {\r
2268   HMENU hmenu = GetMenu(hwndMain);\r
2269 \r
2270   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2271                         MF_BYCOMMAND|((appData.icsActive &&\r
2272                                        *appData.icsCommPort != NULLCHAR) ?\r
2273                                       MF_ENABLED : MF_GRAYED));\r
2274   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2275                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2276                                      MF_CHECKED : MF_UNCHECKED));\r
2277 }\r
2278 \r
2279 \r
2280 VOID\r
2281 SaveSettings(char* name)\r
2282 {\r
2283   FILE *f;\r
2284   ArgDescriptor *ad;\r
2285   WINDOWPLACEMENT wp;\r
2286   char dir[MSG_SIZ];\r
2287 \r
2288   if (!hwndMain) return;\r
2289 \r
2290   GetCurrentDirectory(MSG_SIZ, dir);\r
2291   SetCurrentDirectory(installDir);\r
2292   f = fopen(name, "w");\r
2293   SetCurrentDirectory(dir);\r
2294   if (f == NULL) {\r
2295     DisplayError(name, errno);\r
2296     return;\r
2297   }\r
2298   fprintf(f, ";\n");\r
2299   fprintf(f, "; %s %s.%s Save Settings file\n", PRODUCT, VERSION, PATCHLEVEL);\r
2300   fprintf(f, ";\n");\r
2301   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2302   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2303   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2304   fprintf(f, ";\n");\r
2305 \r
2306   wp.length = sizeof(WINDOWPLACEMENT);\r
2307   GetWindowPlacement(hwndMain, &wp);\r
2308   boardX = wp.rcNormalPosition.left;\r
2309   boardY = wp.rcNormalPosition.top;\r
2310 \r
2311   if (hwndConsole) {\r
2312     GetWindowPlacement(hwndConsole, &wp);\r
2313     wpConsole.x = wp.rcNormalPosition.left;\r
2314     wpConsole.y = wp.rcNormalPosition.top;\r
2315     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2316     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2317   }\r
2318 \r
2319   if (analysisDialog) {\r
2320     GetWindowPlacement(analysisDialog, &wp);\r
2321     analysisX = wp.rcNormalPosition.left;\r
2322     analysisY = wp.rcNormalPosition.top;\r
2323     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2324     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2325   }\r
2326 \r
2327   if (commentDialog) {\r
2328     GetWindowPlacement(commentDialog, &wp);\r
2329     commentX = wp.rcNormalPosition.left;\r
2330     commentY = wp.rcNormalPosition.top;\r
2331     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2332     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2333   }\r
2334 \r
2335   if (editTagsDialog) {\r
2336     GetWindowPlacement(editTagsDialog, &wp);\r
2337     editTagsX = wp.rcNormalPosition.left;\r
2338     editTagsY = wp.rcNormalPosition.top;\r
2339     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2340     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2341   }\r
2342 \r
2343   if (gameListDialog) {\r
2344     GetWindowPlacement(gameListDialog, &wp);\r
2345     wpGameList.x = wp.rcNormalPosition.left;\r
2346     wpGameList.y = wp.rcNormalPosition.top;\r
2347     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2348     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2349   }\r
2350 \r
2351   /* [AS] Move history */\r
2352   wpMoveHistory.visible = MoveHistoryIsUp();\r
2353   \r
2354   if( moveHistoryDialog ) {\r
2355     GetWindowPlacement(moveHistoryDialog, &wp);\r
2356     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2357     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2358     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2359     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2360   }\r
2361 \r
2362   /* [AS] Eval graph */\r
2363   wpEvalGraph.visible = EvalGraphIsUp();\r
2364 \r
2365   if( evalGraphDialog ) {\r
2366     GetWindowPlacement(evalGraphDialog, &wp);\r
2367     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2368     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2369     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2370     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2371   }\r
2372 \r
2373   /* [AS] Engine output */\r
2374   wpEngineOutput.visible = EngineOutputIsUp();\r
2375 \r
2376   if( engineOutputDialog ) {\r
2377     GetWindowPlacement(engineOutputDialog, &wp);\r
2378     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2379     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2380     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2381     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2382   }\r
2383 \r
2384   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2385     if (!ad->save) continue;\r
2386     switch (ad->argType) {\r
2387     case ArgString:\r
2388       {\r
2389         char *p = *(char **)ad->argLoc;\r
2390         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2391           /* Quote multiline values or \-containing values\r
2392              with { } if possible */\r
2393           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2394         } else {\r
2395           /* Else quote with " " */\r
2396           fprintf(f, "/%s=\"", ad->argName);\r
2397           while (*p) {\r
2398             if (*p == '\n') fprintf(f, "\n");\r
2399             else if (*p == '\r') fprintf(f, "\\r");\r
2400             else if (*p == '\t') fprintf(f, "\\t");\r
2401             else if (*p == '\b') fprintf(f, "\\b");\r
2402             else if (*p == '\f') fprintf(f, "\\f");\r
2403             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2404             else if (*p == '\"') fprintf(f, "\\\"");\r
2405             else if (*p == '\\') fprintf(f, "\\\\");\r
2406             else putc(*p, f);\r
2407             p++;\r
2408           }\r
2409           fprintf(f, "\"\n");\r
2410         }\r
2411       }\r
2412       break;\r
2413     case ArgInt:\r
2414     case ArgZ:\r
2415       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2416       break;\r
2417     case ArgX:\r
2418       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2419       break;\r
2420     case ArgY:\r
2421       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2422       break;\r
2423     case ArgFloat:\r
2424       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2425       break;\r
2426     case ArgBoolean:\r
2427       fprintf(f, "/%s=%s\n", ad->argName, \r
2428         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2429       break;\r
2430     case ArgTrue:\r
2431       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2432       break;\r
2433     case ArgFalse:\r
2434       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2435       break;\r
2436     case ArgColor:\r
2437       {\r
2438         COLORREF color = *(COLORREF *)ad->argLoc;\r
2439         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2440           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2441       }\r
2442       break;\r
2443     case ArgAttribs:\r
2444       {\r
2445         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2446         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2447           (ta->effects & CFE_BOLD) ? "b" : "",\r
2448           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2449           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2450           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2451           (ta->effects) ? " " : "",\r
2452           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2453       }\r
2454       break;\r
2455     case ArgFilename:\r
2456       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2457         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2458       } else {\r
2459         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2460       }\r
2461       break;\r
2462     case ArgBoardSize:\r
2463       fprintf(f, "/%s=%s\n", ad->argName,\r
2464               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2465       break;\r
2466     case ArgFont:\r
2467       {\r
2468         int bs;\r
2469         for (bs=0; bs<NUM_SIZES; bs++) {\r
2470           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2471           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2472           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2473             ad->argName, mfp->faceName, mfp->pointSize,\r
2474             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2475             mfp->bold ? "b" : "",\r
2476             mfp->italic ? "i" : "",\r
2477             mfp->underline ? "u" : "",\r
2478             mfp->strikeout ? "s" : "");\r
2479         }\r
2480       }\r
2481       break;\r
2482     case ArgCommSettings:\r
2483       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2484     case ArgNone:\r
2485     case ArgSettingsFilename: ;\r
2486     }\r
2487   }\r
2488   fclose(f);\r
2489 }\r
2490 \r
2491 \r
2492 \r
2493 /*---------------------------------------------------------------------------*\\r
2494  *\r
2495  * GDI board drawing routines\r
2496  *\r
2497 \*---------------------------------------------------------------------------*/\r
2498 \r
2499 /* [AS] Draw square using background texture */\r
2500 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2501 {\r
2502     XFORM   x;\r
2503 \r
2504     if( mode == 0 ) {\r
2505         return; /* Should never happen! */\r
2506     }\r
2507 \r
2508     SetGraphicsMode( dst, GM_ADVANCED );\r
2509 \r
2510     switch( mode ) {\r
2511     case 1:\r
2512         /* Identity */\r
2513         break;\r
2514     case 2:\r
2515         /* X reflection */\r
2516         x.eM11 = -1.0;\r
2517         x.eM12 = 0;\r
2518         x.eM21 = 0;\r
2519         x.eM22 = 1.0;\r
2520         x.eDx = (FLOAT) dw + dx - 1;\r
2521         x.eDy = 0;\r
2522         dx = 0;\r
2523         SetWorldTransform( dst, &x );\r
2524         break;\r
2525     case 3:\r
2526         /* Y reflection */\r
2527         x.eM11 = 1.0;\r
2528         x.eM12 = 0;\r
2529         x.eM21 = 0;\r
2530         x.eM22 = -1.0;\r
2531         x.eDx = 0;\r
2532         x.eDy = (FLOAT) dh + dy - 1;\r
2533         dy = 0;\r
2534         SetWorldTransform( dst, &x );\r
2535         break;\r
2536     case 4:\r
2537         /* X/Y flip */\r
2538         x.eM11 = 0;\r
2539         x.eM12 = 1.0;\r
2540         x.eM21 = 1.0;\r
2541         x.eM22 = 0;\r
2542         x.eDx = (FLOAT) dx;\r
2543         x.eDy = (FLOAT) dy;\r
2544         dx = 0;\r
2545         dy = 0;\r
2546         SetWorldTransform( dst, &x );\r
2547         break;\r
2548     }\r
2549 \r
2550     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2551 \r
2552     x.eM11 = 1.0;\r
2553     x.eM12 = 0;\r
2554     x.eM21 = 0;\r
2555     x.eM22 = 1.0;\r
2556     x.eDx = 0;\r
2557     x.eDy = 0;\r
2558     SetWorldTransform( dst, &x );\r
2559 \r
2560     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2561 }\r
2562 \r
2563 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2564 enum {\r
2565     PM_WP = (int) WhitePawn, \r
2566     PM_WN = (int) WhiteKnight, \r
2567     PM_WB = (int) WhiteBishop, \r
2568     PM_WR = (int) WhiteRook, \r
2569     PM_WQ = (int) WhiteQueen, \r
2570     PM_WF = (int) WhiteFerz, \r
2571     PM_WW = (int) WhiteWazir, \r
2572     PM_WE = (int) WhiteAlfil, \r
2573     PM_WM = (int) WhiteMan, \r
2574     PM_WO = (int) WhiteCannon, \r
2575     PM_WU = (int) WhiteUnicorn, \r
2576     PM_WH = (int) WhiteNightrider, \r
2577     PM_WA = (int) WhiteAngel, \r
2578     PM_WC = (int) WhiteMarshall, \r
2579     PM_WAB = (int) WhiteCardinal, \r
2580     PM_WD = (int) WhiteDragon, \r
2581     PM_WL = (int) WhiteLance, \r
2582     PM_WS = (int) WhiteCobra, \r
2583     PM_WV = (int) WhiteFalcon, \r
2584     PM_WSG = (int) WhiteSilver, \r
2585     PM_WG = (int) WhiteGrasshopper, \r
2586     PM_WK = (int) WhiteKing,\r
2587     PM_BP = (int) BlackPawn, \r
2588     PM_BN = (int) BlackKnight, \r
2589     PM_BB = (int) BlackBishop, \r
2590     PM_BR = (int) BlackRook, \r
2591     PM_BQ = (int) BlackQueen, \r
2592     PM_BF = (int) BlackFerz, \r
2593     PM_BW = (int) BlackWazir, \r
2594     PM_BE = (int) BlackAlfil, \r
2595     PM_BM = (int) BlackMan,\r
2596     PM_BO = (int) BlackCannon, \r
2597     PM_BU = (int) BlackUnicorn, \r
2598     PM_BH = (int) BlackNightrider, \r
2599     PM_BA = (int) BlackAngel, \r
2600     PM_BC = (int) BlackMarshall, \r
2601     PM_BG = (int) BlackGrasshopper, \r
2602     PM_BAB = (int) BlackCardinal,\r
2603     PM_BD = (int) BlackDragon,\r
2604     PM_BL = (int) BlackLance,\r
2605     PM_BS = (int) BlackCobra,\r
2606     PM_BV = (int) BlackFalcon,\r
2607     PM_BSG = (int) BlackSilver,\r
2608     PM_BK = (int) BlackKing\r
2609 };\r
2610 \r
2611 static HFONT hPieceFont = NULL;\r
2612 static HBITMAP hPieceMask[(int) EmptySquare];\r
2613 static HBITMAP hPieceFace[(int) EmptySquare];\r
2614 static int fontBitmapSquareSize = 0;\r
2615 static char pieceToFontChar[(int) EmptySquare] =\r
2616                               { 'p', 'n', 'b', 'r', 'q', \r
2617                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2618                       'k', 'o', 'm', 'v', 't', 'w', \r
2619                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2620                                                               'l' };\r
2621 \r
2622 extern BOOL SetCharTable( char *table, const char * map );\r
2623 /* [HGM] moved to backend.c */\r
2624 \r
2625 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2626 {\r
2627     HBRUSH hbrush;\r
2628     BYTE r1 = GetRValue( color );\r
2629     BYTE g1 = GetGValue( color );\r
2630     BYTE b1 = GetBValue( color );\r
2631     BYTE r2 = r1 / 2;\r
2632     BYTE g2 = g1 / 2;\r
2633     BYTE b2 = b1 / 2;\r
2634     RECT rc;\r
2635 \r
2636     /* Create a uniform background first */\r
2637     hbrush = CreateSolidBrush( color );\r
2638     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2639     FillRect( hdc, &rc, hbrush );\r
2640     DeleteObject( hbrush );\r
2641     \r
2642     if( mode == 1 ) {\r
2643         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2644         int steps = squareSize / 2;\r
2645         int i;\r
2646 \r
2647         for( i=0; i<steps; i++ ) {\r
2648             BYTE r = r1 - (r1-r2) * i / steps;\r
2649             BYTE g = g1 - (g1-g2) * i / steps;\r
2650             BYTE b = b1 - (b1-b2) * i / steps;\r
2651 \r
2652             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2653             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2654             FillRect( hdc, &rc, hbrush );\r
2655             DeleteObject(hbrush);\r
2656         }\r
2657     }\r
2658     else if( mode == 2 ) {\r
2659         /* Diagonal gradient, good more or less for every piece */\r
2660         POINT triangle[3];\r
2661         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2662         HBRUSH hbrush_old;\r
2663         int steps = squareSize;\r
2664         int i;\r
2665 \r
2666         triangle[0].x = squareSize - steps;\r
2667         triangle[0].y = squareSize;\r
2668         triangle[1].x = squareSize;\r
2669         triangle[1].y = squareSize;\r
2670         triangle[2].x = squareSize;\r
2671         triangle[2].y = squareSize - steps;\r
2672 \r
2673         for( i=0; i<steps; i++ ) {\r
2674             BYTE r = r1 - (r1-r2) * i / steps;\r
2675             BYTE g = g1 - (g1-g2) * i / steps;\r
2676             BYTE b = b1 - (b1-b2) * i / steps;\r
2677 \r
2678             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2679             hbrush_old = SelectObject( hdc, hbrush );\r
2680             Polygon( hdc, triangle, 3 );\r
2681             SelectObject( hdc, hbrush_old );\r
2682             DeleteObject(hbrush);\r
2683             triangle[0].x++;\r
2684             triangle[2].y++;\r
2685         }\r
2686 \r
2687         SelectObject( hdc, hpen );\r
2688     }\r
2689 }\r
2690 \r
2691 /*\r
2692     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2693     seems to work ok. The main problem here is to find the "inside" of a chess\r
2694     piece: follow the steps as explained below.\r
2695 */\r
2696 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2697 {\r
2698     HBITMAP hbm;\r
2699     HBITMAP hbm_old;\r
2700     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2701     RECT rc;\r
2702     SIZE sz;\r
2703     POINT pt;\r
2704     int backColor = whitePieceColor; \r
2705     int foreColor = blackPieceColor;\r
2706     \r
2707     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2708         backColor = appData.fontBackColorWhite;\r
2709         foreColor = appData.fontForeColorWhite;\r
2710     }\r
2711     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2712         backColor = appData.fontBackColorBlack;\r
2713         foreColor = appData.fontForeColorBlack;\r
2714     }\r
2715 \r
2716     /* Mask */\r
2717     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2718 \r
2719     hbm_old = SelectObject( hdc, hbm );\r
2720 \r
2721     rc.left = 0;\r
2722     rc.top = 0;\r
2723     rc.right = squareSize;\r
2724     rc.bottom = squareSize;\r
2725 \r
2726     /* Step 1: background is now black */\r
2727     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2728 \r
2729     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2730 \r
2731     pt.x = (squareSize - sz.cx) / 2;\r
2732     pt.y = (squareSize - sz.cy) / 2;\r
2733 \r
2734     SetBkMode( hdc, TRANSPARENT );\r
2735     SetTextColor( hdc, chroma );\r
2736     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2737     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2738 \r
2739     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2740     /* Step 3: the area outside the piece is filled with white */\r
2741 //    FloodFill( hdc, 0, 0, chroma );\r
2742     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2743     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2744     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2745     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2746     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2747     /* \r
2748         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2749         but if the start point is not inside the piece we're lost!\r
2750         There should be a better way to do this... if we could create a region or path\r
2751         from the fill operation we would be fine for example.\r
2752     */\r
2753 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2754     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2755 \r
2756     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2757         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2758         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2759 \r
2760         SelectObject( dc2, bm2 );\r
2761         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2762         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2763         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2764         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2765         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2766 \r
2767         DeleteDC( dc2 );\r
2768         DeleteObject( bm2 );\r
2769     }\r
2770 \r
2771     SetTextColor( hdc, 0 );\r
2772     /* \r
2773         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2774         draw the piece again in black for safety.\r
2775     */\r
2776     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2777 \r
2778     SelectObject( hdc, hbm_old );\r
2779 \r
2780     if( hPieceMask[index] != NULL ) {\r
2781         DeleteObject( hPieceMask[index] );\r
2782     }\r
2783 \r
2784     hPieceMask[index] = hbm;\r
2785 \r
2786     /* Face */\r
2787     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2788 \r
2789     SelectObject( hdc, hbm );\r
2790 \r
2791     {\r
2792         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2793         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2794         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2795 \r
2796         SelectObject( dc1, hPieceMask[index] );\r
2797         SelectObject( dc2, bm2 );\r
2798         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2799         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2800         \r
2801         /* \r
2802             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2803             the piece background and deletes (makes transparent) the rest.\r
2804             Thanks to that mask, we are free to paint the background with the greates\r
2805             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2806             We use this, to make gradients and give the pieces a "roundish" look.\r
2807         */\r
2808         SetPieceBackground( hdc, backColor, 2 );\r
2809         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2810 \r
2811         DeleteDC( dc2 );\r
2812         DeleteDC( dc1 );\r
2813         DeleteObject( bm2 );\r
2814     }\r
2815 \r
2816     SetTextColor( hdc, foreColor );\r
2817     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2818 \r
2819     SelectObject( hdc, hbm_old );\r
2820 \r
2821     if( hPieceFace[index] != NULL ) {\r
2822         DeleteObject( hPieceFace[index] );\r
2823     }\r
2824 \r
2825     hPieceFace[index] = hbm;\r
2826 }\r
2827 \r
2828 static int TranslatePieceToFontPiece( int piece )\r
2829 {\r
2830     switch( piece ) {\r
2831     case BlackPawn:\r
2832         return PM_BP;\r
2833     case BlackKnight:\r
2834         return PM_BN;\r
2835     case BlackBishop:\r
2836         return PM_BB;\r
2837     case BlackRook:\r
2838         return PM_BR;\r
2839     case BlackQueen:\r
2840         return PM_BQ;\r
2841     case BlackKing:\r
2842         return PM_BK;\r
2843     case WhitePawn:\r
2844         return PM_WP;\r
2845     case WhiteKnight:\r
2846         return PM_WN;\r
2847     case WhiteBishop:\r
2848         return PM_WB;\r
2849     case WhiteRook:\r
2850         return PM_WR;\r
2851     case WhiteQueen:\r
2852         return PM_WQ;\r
2853     case WhiteKing:\r
2854         return PM_WK;\r
2855 \r
2856     case BlackAngel:\r
2857         return PM_BA;\r
2858     case BlackMarshall:\r
2859         return PM_BC;\r
2860     case BlackFerz:\r
2861         return PM_BF;\r
2862     case BlackNightrider:\r
2863         return PM_BH;\r
2864     case BlackAlfil:\r
2865         return PM_BE;\r
2866     case BlackWazir:\r
2867         return PM_BW;\r
2868     case BlackUnicorn:\r
2869         return PM_BU;\r
2870     case BlackCannon:\r
2871         return PM_BO;\r
2872     case BlackGrasshopper:\r
2873         return PM_BG;\r
2874     case BlackMan:\r
2875         return PM_BM;\r
2876     case BlackSilver:\r
2877         return PM_BSG;\r
2878     case BlackLance:\r
2879         return PM_BL;\r
2880     case BlackFalcon:\r
2881         return PM_BV;\r
2882     case BlackCobra:\r
2883         return PM_BS;\r
2884     case BlackCardinal:\r
2885         return PM_BAB;\r
2886     case BlackDragon:\r
2887         return PM_BD;\r
2888 \r
2889     case WhiteAngel:\r
2890         return PM_WA;\r
2891     case WhiteMarshall:\r
2892         return PM_WC;\r
2893     case WhiteFerz:\r
2894         return PM_WF;\r
2895     case WhiteNightrider:\r
2896         return PM_WH;\r
2897     case WhiteAlfil:\r
2898         return PM_WE;\r
2899     case WhiteWazir:\r
2900         return PM_WW;\r
2901     case WhiteUnicorn:\r
2902         return PM_WU;\r
2903     case WhiteCannon:\r
2904         return PM_WO;\r
2905     case WhiteGrasshopper:\r
2906         return PM_WG;\r
2907     case WhiteMan:\r
2908         return PM_WM;\r
2909     case WhiteSilver:\r
2910         return PM_WSG;\r
2911     case WhiteLance:\r
2912         return PM_WL;\r
2913     case WhiteFalcon:\r
2914         return PM_WV;\r
2915     case WhiteCobra:\r
2916         return PM_WS;\r
2917     case WhiteCardinal:\r
2918         return PM_WAB;\r
2919     case WhiteDragon:\r
2920         return PM_WD;\r
2921     }\r
2922 \r
2923     return 0;\r
2924 }\r
2925 \r
2926 void CreatePiecesFromFont()\r
2927 {\r
2928     LOGFONT lf;\r
2929     HDC hdc_window = NULL;\r
2930     HDC hdc = NULL;\r
2931     HFONT hfont_old;\r
2932     int fontHeight;\r
2933     int i;\r
2934 \r
2935     if( fontBitmapSquareSize < 0 ) {\r
2936         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2937         return;\r
2938     }\r
2939 \r
2940     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2941         fontBitmapSquareSize = -1;\r
2942         return;\r
2943     }\r
2944 \r
2945     if( fontBitmapSquareSize != squareSize ) {\r
2946         hdc_window = GetDC( hwndMain );\r
2947         hdc = CreateCompatibleDC( hdc_window );\r
2948 \r
2949         if( hPieceFont != NULL ) {\r
2950             DeleteObject( hPieceFont );\r
2951         }\r
2952         else {\r
2953             for( i=0; i<=(int)BlackKing; i++ ) {\r
2954                 hPieceMask[i] = NULL;\r
2955                 hPieceFace[i] = NULL;\r
2956             }\r
2957         }\r
2958 \r
2959         fontHeight = 75;\r
2960 \r
2961         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2962             fontHeight = appData.fontPieceSize;\r
2963         }\r
2964 \r
2965         fontHeight = (fontHeight * squareSize) / 100;\r
2966 \r
2967         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2968         lf.lfWidth = 0;\r
2969         lf.lfEscapement = 0;\r
2970         lf.lfOrientation = 0;\r
2971         lf.lfWeight = FW_NORMAL;\r
2972         lf.lfItalic = 0;\r
2973         lf.lfUnderline = 0;\r
2974         lf.lfStrikeOut = 0;\r
2975         lf.lfCharSet = DEFAULT_CHARSET;\r
2976         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2977         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2978         lf.lfQuality = PROOF_QUALITY;\r
2979         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2980         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2981         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2982 \r
2983         hPieceFont = CreateFontIndirect( &lf );\r
2984 \r
2985         if( hPieceFont == NULL ) {\r
2986             fontBitmapSquareSize = -2;\r
2987         }\r
2988         else {\r
2989             /* Setup font-to-piece character table */\r
2990             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2991                 /* No (or wrong) global settings, try to detect the font */\r
2992                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2993                     /* Alpha */\r
2994                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
2995                 }\r
2996                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
2997                     /* DiagramTT* family */\r
2998                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
2999                 }\r
3000                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3001                     /* Fairy symbols */\r
3002                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3003                 }\r
3004                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3005                     /* Good Companion (Some characters get warped as literal :-( */\r
3006                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3007                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3008                     SetCharTable(pieceToFontChar, s);\r
3009                 }\r
3010                 else {\r
3011                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3012                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3013                 }\r
3014             }\r
3015 \r
3016             /* Create bitmaps */\r
3017             hfont_old = SelectObject( hdc, hPieceFont );\r
3018 #if 0\r
3019             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
3020             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
3021             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
3022             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
3023             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
3024             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
3025             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
3026             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
3027             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
3028             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
3029             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
3030             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
3031 \r
3032             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
3033             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
3034             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
3035             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
3036             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
3037             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
3038             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
3039             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
3040             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
3041             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
3042             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
3043             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
3044             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
3045             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
3046             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
3047             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
3048             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
3049             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
3050             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
3051             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
3052             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
3053             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
3054             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
3055             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
3056             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
3057             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
3058             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
3059             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
3060             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
3061             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
3062             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
3063             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
3064 #else\r
3065             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3066                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3067                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3068 #endif\r
3069             SelectObject( hdc, hfont_old );\r
3070 \r
3071             fontBitmapSquareSize = squareSize;\r
3072         }\r
3073     }\r
3074 \r
3075     if( hdc != NULL ) {\r
3076         DeleteDC( hdc );\r
3077     }\r
3078 \r
3079     if( hdc_window != NULL ) {\r
3080         ReleaseDC( hwndMain, hdc_window );\r
3081     }\r
3082 }\r
3083 \r
3084 HBITMAP\r
3085 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3086 {\r
3087   char name[128];\r
3088 \r
3089   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3090   if (gameInfo.event &&\r
3091       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3092       strcmp(name, "k80s") == 0) {\r
3093     strcpy(name, "tim");\r
3094   }\r
3095   return LoadBitmap(hinst, name);\r
3096 }\r
3097 \r
3098 \r
3099 /* Insert a color into the program's logical palette\r
3100    structure.  This code assumes the given color is\r
3101    the result of the RGB or PALETTERGB macro, and it\r
3102    knows how those macros work (which is documented).\r
3103 */\r
3104 VOID\r
3105 InsertInPalette(COLORREF color)\r
3106 {\r
3107   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3108 \r
3109   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3110     DisplayFatalError("Too many colors", 0, 1);\r
3111     pLogPal->palNumEntries--;\r
3112     return;\r
3113   }\r
3114 \r
3115   pe->peFlags = (char) 0;\r
3116   pe->peRed = (char) (0xFF & color);\r
3117   pe->peGreen = (char) (0xFF & (color >> 8));\r
3118   pe->peBlue = (char) (0xFF & (color >> 16));\r
3119   return;\r
3120 }\r
3121 \r
3122 \r
3123 VOID\r
3124 InitDrawingColors()\r
3125 {\r
3126   if (pLogPal == NULL) {\r
3127     /* Allocate enough memory for a logical palette with\r
3128      * PALETTESIZE entries and set the size and version fields\r
3129      * of the logical palette structure.\r
3130      */\r
3131     pLogPal = (NPLOGPALETTE)\r
3132       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3133                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3134     pLogPal->palVersion    = 0x300;\r
3135   }\r
3136   pLogPal->palNumEntries = 0;\r
3137 \r
3138   InsertInPalette(lightSquareColor);\r
3139   InsertInPalette(darkSquareColor);\r
3140   InsertInPalette(whitePieceColor);\r
3141   InsertInPalette(blackPieceColor);\r
3142   InsertInPalette(highlightSquareColor);\r
3143   InsertInPalette(premoveHighlightColor);\r
3144 \r
3145   /*  create a logical color palette according the information\r
3146    *  in the LOGPALETTE structure.\r
3147    */\r
3148   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3149 \r
3150   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3151   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3152   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3153   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3154   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3155   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3156   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3157   /* [AS] Force rendering of the font-based pieces */\r
3158   if( fontBitmapSquareSize > 0 ) {\r
3159     fontBitmapSquareSize = 0;\r
3160   }\r
3161 }\r
3162 \r
3163 \r
3164 int\r
3165 BoardWidth(int boardSize, int n)\r
3166 { /* [HGM] argument n added to allow different width and height */\r
3167   int lineGap = sizeInfo[boardSize].lineGap;\r
3168 \r
3169   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3170       lineGap = appData.overrideLineGap;\r
3171   }\r
3172 \r
3173   return (n + 1) * lineGap +\r
3174           n * sizeInfo[boardSize].squareSize;\r
3175 }\r
3176 \r
3177 /* Respond to board resize by dragging edge */\r
3178 VOID\r
3179 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3180 {\r
3181   BoardSize newSize = NUM_SIZES - 1;\r
3182   static int recurse = 0;\r
3183   if (IsIconic(hwndMain)) return;\r
3184   if (recurse > 0) return;\r
3185   recurse++;\r
3186   while (newSize > 0) {\r
3187         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3188         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3189            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3190     newSize--;\r
3191   } \r
3192   boardSize = newSize;\r
3193   InitDrawingSizes(boardSize, flags);\r
3194   recurse--;\r
3195 }\r
3196 \r
3197 \r
3198 \r
3199 VOID\r
3200 InitDrawingSizes(BoardSize boardSize, int flags)\r
3201 {\r
3202   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3203   ChessSquare piece;\r
3204   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3205   HDC hdc;\r
3206   SIZE clockSize, messageSize;\r
3207   HFONT oldFont;\r
3208   char buf[MSG_SIZ];\r
3209   char *str;\r
3210   HMENU hmenu = GetMenu(hwndMain);\r
3211   RECT crect, wrect, oldRect;\r
3212   int offby;\r
3213   LOGBRUSH logbrush;\r
3214 \r
3215   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3216   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3217 \r
3218   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3219   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3220 \r
3221   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3222   oldRect.top = boardY;\r
3223   oldRect.right = boardX + winWidth;\r
3224   oldRect.bottom = boardY + winHeight;\r
3225 \r
3226   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3227   smallLayout = sizeInfo[boardSize].smallLayout;\r
3228   squareSize = sizeInfo[boardSize].squareSize;\r
3229   lineGap = sizeInfo[boardSize].lineGap;\r
3230   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3231 \r
3232   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3233       lineGap = appData.overrideLineGap;\r
3234   }\r
3235 \r
3236   if (tinyLayout != oldTinyLayout) {\r
3237     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3238     if (tinyLayout) {\r
3239       style &= ~WS_SYSMENU;\r
3240       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3241                  "&Minimize\tCtrl+F4");\r
3242     } else {\r
3243       style |= WS_SYSMENU;\r
3244       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3245     }\r
3246     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3247 \r
3248     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3249       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3250         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3251     }\r
3252     DrawMenuBar(hwndMain);\r
3253   }\r
3254 \r
3255   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3256   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3257 \r
3258   /* Get text area sizes */\r
3259   hdc = GetDC(hwndMain);\r
3260   if (appData.clockMode) {\r
3261     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3262   } else {\r
3263     sprintf(buf, "White");\r
3264   }\r
3265   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3266   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3267   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3268   str = "We only care about the height here";\r
3269   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3270   SelectObject(hdc, oldFont);\r
3271   ReleaseDC(hwndMain, hdc);\r
3272 \r
3273   /* Compute where everything goes */\r
3274   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3275         /* [HGM] logo: if either logo is on, reserve space for it */\r
3276         logoHeight =  2*clockSize.cy;\r
3277         leftLogoRect.left   = OUTER_MARGIN;\r
3278         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3279         leftLogoRect.top    = OUTER_MARGIN;\r
3280         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3281 \r
3282         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3283         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3284         rightLogoRect.top    = OUTER_MARGIN;\r
3285         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3286 \r
3287 \r
3288     whiteRect.left = leftLogoRect.right;\r
3289     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3290     whiteRect.top = OUTER_MARGIN;\r
3291     whiteRect.bottom = whiteRect.top + logoHeight;\r
3292 \r
3293     blackRect.right = rightLogoRect.left;\r
3294     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3295     blackRect.top = whiteRect.top;\r
3296     blackRect.bottom = whiteRect.bottom;\r
3297   } else {\r
3298     whiteRect.left = OUTER_MARGIN;\r
3299     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3300     whiteRect.top = OUTER_MARGIN;\r
3301     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3302 \r
3303     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3304     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3305     blackRect.top = whiteRect.top;\r
3306     blackRect.bottom = whiteRect.bottom;\r
3307   }\r
3308 \r
3309   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3310   if (appData.showButtonBar) {\r
3311     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3312       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3313   } else {\r
3314     messageRect.right = OUTER_MARGIN + boardWidth;\r
3315   }\r
3316   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3317   messageRect.bottom = messageRect.top + messageSize.cy;\r
3318 \r
3319   boardRect.left = OUTER_MARGIN;\r
3320   boardRect.right = boardRect.left + boardWidth;\r
3321   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3322   boardRect.bottom = boardRect.top + boardHeight;\r
3323 \r
3324   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3325   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3326   oldBoardSize = boardSize;\r
3327   oldTinyLayout = tinyLayout;\r
3328   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3329   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3330     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3331   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3332   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3333   winHeight = winH; //       without disturbing window attachments\r
3334   GetWindowRect(hwndMain, &wrect);\r
3335   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3336                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3337 \r
3338   // [HGM] placement: let attached windows follow size change.\r
3339   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3340   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3341   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3342   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3343   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3344 \r
3345   /* compensate if menu bar wrapped */\r
3346   GetClientRect(hwndMain, &crect);\r
3347   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3348   winHeight += offby;\r
3349   switch (flags) {\r
3350   case WMSZ_TOPLEFT:\r
3351     SetWindowPos(hwndMain, NULL, \r
3352                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3353                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3354     break;\r
3355 \r
3356   case WMSZ_TOPRIGHT:\r
3357   case WMSZ_TOP:\r
3358     SetWindowPos(hwndMain, NULL, \r
3359                  wrect.left, wrect.bottom - winHeight, \r
3360                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3361     break;\r
3362 \r
3363   case WMSZ_BOTTOMLEFT:\r
3364   case WMSZ_LEFT:\r
3365     SetWindowPos(hwndMain, NULL, \r
3366                  wrect.right - winWidth, wrect.top, \r
3367                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3368     break;\r
3369 \r
3370   case WMSZ_BOTTOMRIGHT:\r
3371   case WMSZ_BOTTOM:\r
3372   case WMSZ_RIGHT:\r
3373   default:\r
3374     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3375                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3376     break;\r
3377   }\r
3378 \r
3379   hwndPause = NULL;\r
3380   for (i = 0; i < N_BUTTONS; i++) {\r
3381     if (buttonDesc[i].hwnd != NULL) {\r
3382       DestroyWindow(buttonDesc[i].hwnd);\r
3383       buttonDesc[i].hwnd = NULL;\r
3384     }\r
3385     if (appData.showButtonBar) {\r
3386       buttonDesc[i].hwnd =\r
3387         CreateWindow("BUTTON", buttonDesc[i].label,\r
3388                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3389                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3390                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3391                      (HMENU) buttonDesc[i].id,\r
3392                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3393       if (tinyLayout) {\r
3394         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3395                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3396                     MAKELPARAM(FALSE, 0));\r
3397       }\r
3398       if (buttonDesc[i].id == IDM_Pause)\r
3399         hwndPause = buttonDesc[i].hwnd;\r
3400       buttonDesc[i].wndproc = (WNDPROC)\r
3401         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3402     }\r
3403   }\r
3404   if (gridPen != NULL) DeleteObject(gridPen);\r
3405   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3406   if (premovePen != NULL) DeleteObject(premovePen);\r
3407   if (lineGap != 0) {\r
3408     logbrush.lbStyle = BS_SOLID;\r
3409     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3410     gridPen =\r
3411       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3412                    lineGap, &logbrush, 0, NULL);\r
3413     logbrush.lbColor = highlightSquareColor;\r
3414     highlightPen =\r
3415       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3416                    lineGap, &logbrush, 0, NULL);\r
3417 \r
3418     logbrush.lbColor = premoveHighlightColor; \r
3419     premovePen =\r
3420       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3421                    lineGap, &logbrush, 0, NULL);\r
3422 \r
3423     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3424     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3425       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3426       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3427         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3428       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3429         BOARD_WIDTH * (squareSize + lineGap);\r
3430       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3431     }\r
3432     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3433       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3434       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3435         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3436         lineGap / 2 + (i * (squareSize + lineGap));\r
3437       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3438         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3439       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3440     }\r
3441   }\r
3442 \r
3443   /* [HGM] Licensing requirement */\r
3444 #ifdef GOTHIC\r
3445   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3446 #endif\r
3447 #ifdef FALCON\r
3448   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3449 #endif\r
3450   GothicPopUp( "", VariantNormal);\r
3451 \r
3452 \r
3453 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3454 \r
3455   /* Load piece bitmaps for this board size */\r
3456   for (i=0; i<=2; i++) {\r
3457     for (piece = WhitePawn;\r
3458          (int) piece < (int) BlackPawn;\r
3459          piece = (ChessSquare) ((int) piece + 1)) {\r
3460       if (pieceBitmap[i][piece] != NULL)\r
3461         DeleteObject(pieceBitmap[i][piece]);\r
3462     }\r
3463   }\r
3464 \r
3465   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3466   // Orthodox Chess pieces\r
3467   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3468   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3469   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3470   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3471   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3472   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3473   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3474   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3475   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3476   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3477   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3478   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3479   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3480   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3481   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3482   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3483     // in Shogi, Hijack the unused Queen for Lance\r
3484     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3485     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3486     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3487   } else {\r
3488     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3489     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3490     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3491   }\r
3492 \r
3493   if(squareSize <= 72 && squareSize >= 33) { \r
3494     /* A & C are available in most sizes now */\r
3495     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3496       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3497       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3498       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3499       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3500       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3501       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3502       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3503       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3504       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3505       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3506       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3507       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3508     } else { // Smirf-like\r
3509       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3510       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3511       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3512     }\r
3513     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3514       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3515       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3516       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3517     } else { // WinBoard standard\r
3518       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3519       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3520       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3521     }\r
3522   }\r
3523 \r
3524 \r
3525   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3526     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3527     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3528     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3529     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3530     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3531     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3532     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3533     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3534     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3535     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3536     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3537     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3538     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3539     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3540     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3541     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3542     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3543     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3544     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3545     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3546     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3547     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3548     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3549     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3550     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3551     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3552     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3553     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3554     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3555     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3556 \r
3557     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3558       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3559       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3560       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3561       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3562       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3563       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3564       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3565       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3566       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3567       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3568       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3569       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3570     } else {\r
3571       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3572       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3573       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3574       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3575       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3576       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3577       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3578       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3579       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3580       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3581       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3582       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3583     }\r
3584 \r
3585   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3586     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3587     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3588     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3589     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3590     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3591     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3592     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3593     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3594     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3595     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3596     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3597     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3598     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3599     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3600   }\r
3601 \r
3602 \r
3603   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3604   /* special Shogi support in this size */\r
3605   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3606       for (piece = WhitePawn;\r
3607            (int) piece < (int) BlackPawn;\r
3608            piece = (ChessSquare) ((int) piece + 1)) {\r
3609         if (pieceBitmap[i][piece] != NULL)\r
3610           DeleteObject(pieceBitmap[i][piece]);\r
3611       }\r
3612     }\r
3613   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3614   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3615   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3616   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3617   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3618   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3619   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3620   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3621   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3622   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3623   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3624   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3625   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3626   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3627   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3628   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3629   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3630   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3631   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3632   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3633   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3634   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3635   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3636   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3637   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3638   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3639   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3640   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3641   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3642   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3643   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3644   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3645   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3646   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3647   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3648   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3649   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3650   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3651   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3652   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3653   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3654   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3655   minorSize = 0;\r
3656   }\r
3657 }\r
3658 \r
3659 HBITMAP\r
3660 PieceBitmap(ChessSquare p, int kind)\r
3661 {\r
3662   if ((int) p >= (int) BlackPawn)\r
3663     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3664 \r
3665   return pieceBitmap[kind][(int) p];\r
3666 }\r
3667 \r
3668 /***************************************************************/\r
3669 \r
3670 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3671 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3672 /*\r
3673 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3674 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3675 */\r
3676 \r
3677 VOID\r
3678 SquareToPos(int row, int column, int * x, int * y)\r
3679 {\r
3680   if (flipView) {\r
3681     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3682     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3683   } else {\r
3684     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3685     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3686   }\r
3687 }\r
3688 \r
3689 VOID\r
3690 DrawCoordsOnDC(HDC hdc)\r
3691 {\r
3692   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
3693   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
3694   char str[2] = { NULLCHAR, NULLCHAR };\r
3695   int oldMode, oldAlign, x, y, start, i;\r
3696   HFONT oldFont;\r
3697   HBRUSH oldBrush;\r
3698 \r
3699   if (!appData.showCoords)\r
3700     return;\r
3701 \r
3702   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3703 \r
3704   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3705   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3706   oldAlign = GetTextAlign(hdc);\r
3707   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3708 \r
3709   y = boardRect.top + lineGap;\r
3710   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3711 \r
3712   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3713   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3714     str[0] = files[start + i];\r
3715     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3716     y += squareSize + lineGap;\r
3717   }\r
3718 \r
3719   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3720 \r
3721   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3722   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3723     str[0] = ranks[start + i];\r
3724     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3725     x += squareSize + lineGap;\r
3726   }    \r
3727 \r
3728   SelectObject(hdc, oldBrush);\r
3729   SetBkMode(hdc, oldMode);\r
3730   SetTextAlign(hdc, oldAlign);\r
3731   SelectObject(hdc, oldFont);\r
3732 }\r
3733 \r
3734 VOID\r
3735 DrawGridOnDC(HDC hdc)\r
3736 {\r
3737   HPEN oldPen;\r
3738  \r
3739   if (lineGap != 0) {\r
3740     oldPen = SelectObject(hdc, gridPen);\r
3741     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3742     SelectObject(hdc, oldPen);\r
3743   }\r
3744 }\r
3745 \r
3746 #define HIGHLIGHT_PEN 0\r
3747 #define PREMOVE_PEN   1\r
3748 \r
3749 VOID\r
3750 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3751 {\r
3752   int x1, y1;\r
3753   HPEN oldPen, hPen;\r
3754   if (lineGap == 0) return;\r
3755   if (flipView) {\r
3756     x1 = boardRect.left +\r
3757       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3758     y1 = boardRect.top +\r
3759       lineGap/2 + y * (squareSize + lineGap);\r
3760   } else {\r
3761     x1 = boardRect.left +\r
3762       lineGap/2 + x * (squareSize + lineGap);\r
3763     y1 = boardRect.top +\r
3764       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3765   }\r
3766   hPen = pen ? premovePen : highlightPen;\r
3767   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3768   MoveToEx(hdc, x1, y1, NULL);\r
3769   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3770   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3771   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3772   LineTo(hdc, x1, y1);\r
3773   SelectObject(hdc, oldPen);\r
3774 }\r
3775 \r
3776 VOID\r
3777 DrawHighlightsOnDC(HDC hdc)\r
3778 {\r
3779   int i;\r
3780   for (i=0; i<2; i++) {\r
3781     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3782       DrawHighlightOnDC(hdc, TRUE,\r
3783                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3784                         HIGHLIGHT_PEN);\r
3785   }\r
3786   for (i=0; i<2; i++) {\r
3787     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3788         premoveHighlightInfo.sq[i].y >= 0) {\r
3789         DrawHighlightOnDC(hdc, TRUE,\r
3790                           premoveHighlightInfo.sq[i].x, \r
3791                           premoveHighlightInfo.sq[i].y,\r
3792                           PREMOVE_PEN);\r
3793     }\r
3794   }\r
3795 }\r
3796 \r
3797 /* Note: sqcolor is used only in monoMode */\r
3798 /* Note that this code is largely duplicated in woptions.c,\r
3799    function DrawSampleSquare, so that needs to be updated too */\r
3800 VOID\r
3801 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3802 {\r
3803   HBITMAP oldBitmap;\r
3804   HBRUSH oldBrush;\r
3805   int tmpSize;\r
3806 \r
3807   if (appData.blindfold) return;\r
3808 \r
3809   /* [AS] Use font-based pieces if needed */\r
3810   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3811     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3812     CreatePiecesFromFont();\r
3813 \r
3814     if( fontBitmapSquareSize == squareSize ) {\r
3815         int index = TranslatePieceToFontPiece(piece);\r
3816 \r
3817         SelectObject( tmphdc, hPieceMask[ index ] );\r
3818 \r
3819         BitBlt( hdc,\r
3820             x, y,\r
3821             squareSize, squareSize,\r
3822             tmphdc,\r
3823             0, 0,\r
3824             SRCAND );\r
3825 \r
3826         SelectObject( tmphdc, hPieceFace[ index ] );\r
3827 \r
3828         BitBlt( hdc,\r
3829             x, y,\r
3830             squareSize, squareSize,\r
3831             tmphdc,\r
3832             0, 0,\r
3833             SRCPAINT );\r
3834 \r
3835         return;\r
3836     }\r
3837   }\r
3838 \r
3839   if (appData.monoMode) {\r
3840     SelectObject(tmphdc, PieceBitmap(piece, \r
3841       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3842     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3843            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3844   } else {\r
3845     tmpSize = squareSize;\r
3846     if(minorSize &&\r
3847         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3848          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3849       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3850       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3851       x += (squareSize - minorSize)>>1;\r
3852       y += squareSize - minorSize - 2;\r
3853       tmpSize = minorSize;\r
3854     }\r
3855     if (color || appData.allWhite ) {\r
3856       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3857       if( color )\r
3858               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3859       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3860       if(appData.upsideDown && color==flipView)\r
3861         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3862       else\r
3863         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3864 #if 0\r
3865       /* Use black piece color for outline of white pieces */\r
3866       /* Not sure this looks really good (though xboard does it).\r
3867          Maybe better to have another selectable color, default black */\r
3868       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3869       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3870       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3871 #else\r
3872       /* Use black for outline of white pieces */\r
3873       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3874       if(appData.upsideDown && color==flipView)\r
3875         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3876       else\r
3877         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3878 #endif\r
3879     } else {\r
3880 #if 0\r
3881       /* Use white piece color for details of black pieces */\r
3882       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3883          WHITE_PIECE ones aren't always the right shape. */\r
3884       /* Not sure this looks really good (though xboard does it).\r
3885          Maybe better to have another selectable color, default medium gray? */\r
3886       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3887       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3888       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3889       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3890       SelectObject(hdc, blackPieceBrush);\r
3891       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3892 #else\r
3893       /* Use square color for details of black pieces */\r
3894       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3895       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3896       if(appData.upsideDown && !flipView)\r
3897         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3898       else\r
3899         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3900 #endif\r
3901     }\r
3902     SelectObject(hdc, oldBrush);\r
3903     SelectObject(tmphdc, oldBitmap);\r
3904   }\r
3905 }\r
3906 \r
3907 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3908 int GetBackTextureMode( int algo )\r
3909 {\r
3910     int result = BACK_TEXTURE_MODE_DISABLED;\r
3911 \r
3912     switch( algo ) \r
3913     {\r
3914         case BACK_TEXTURE_MODE_PLAIN:\r
3915             result = 1; /* Always use identity map */\r
3916             break;\r
3917         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3918             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3919             break;\r
3920     }\r
3921 \r
3922     return result;\r
3923 }\r
3924 \r
3925 /* \r
3926     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3927     to handle redraws cleanly (as random numbers would always be different).\r
3928 */\r
3929 VOID RebuildTextureSquareInfo()\r
3930 {\r
3931     BITMAP bi;\r
3932     int lite_w = 0;\r
3933     int lite_h = 0;\r
3934     int dark_w = 0;\r
3935     int dark_h = 0;\r
3936     int row;\r
3937     int col;\r
3938 \r
3939     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3940 \r
3941     if( liteBackTexture != NULL ) {\r
3942         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3943             lite_w = bi.bmWidth;\r
3944             lite_h = bi.bmHeight;\r
3945         }\r
3946     }\r
3947 \r
3948     if( darkBackTexture != NULL ) {\r
3949         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3950             dark_w = bi.bmWidth;\r
3951             dark_h = bi.bmHeight;\r
3952         }\r
3953     }\r
3954 \r
3955     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3956         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3957             if( (col + row) & 1 ) {\r
3958                 /* Lite square */\r
3959                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3960                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3961                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3962                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3963                 }\r
3964             }\r
3965             else {\r
3966                 /* Dark square */\r
3967                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3968                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3969                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3970                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3971                 }\r
3972             }\r
3973         }\r
3974     }\r
3975 }\r
3976 \r
3977 /* [AS] Arrow highlighting support */\r
3978 \r
3979 static int A_WIDTH = 5; /* Width of arrow body */\r
3980 \r
3981 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3982 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3983 \r
3984 static double Sqr( double x )\r
3985 {\r
3986     return x*x;\r
3987 }\r
3988 \r
3989 static int Round( double x )\r
3990 {\r
3991     return (int) (x + 0.5);\r
3992 }\r
3993 \r
3994 /* Draw an arrow between two points using current settings */\r
3995 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3996 {\r
3997     POINT arrow[7];\r
3998     double dx, dy, j, k, x, y;\r
3999 \r
4000     if( d_x == s_x ) {\r
4001         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4002 \r
4003         arrow[0].x = s_x + A_WIDTH;\r
4004         arrow[0].y = s_y;\r
4005 \r
4006         arrow[1].x = s_x + A_WIDTH;\r
4007         arrow[1].y = d_y - h;\r
4008 \r
4009         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
4010         arrow[2].y = d_y - h;\r
4011 \r
4012         arrow[3].x = d_x;\r
4013         arrow[3].y = d_y;\r
4014 \r
4015         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
4016         arrow[4].y = d_y - h;\r
4017 \r
4018         arrow[5].x = s_x - A_WIDTH;\r
4019         arrow[5].y = d_y - h;\r
4020 \r
4021         arrow[6].x = s_x - A_WIDTH;\r
4022         arrow[6].y = s_y;\r
4023     }\r
4024     else if( d_y == s_y ) {\r
4025         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4026 \r
4027         arrow[0].x = s_x;\r
4028         arrow[0].y = s_y + A_WIDTH;\r
4029 \r
4030         arrow[1].x = d_x - w;\r
4031         arrow[1].y = s_y + A_WIDTH;\r
4032 \r
4033         arrow[2].x = d_x - w;\r
4034         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
4035 \r
4036         arrow[3].x = d_x;\r
4037         arrow[3].y = d_y;\r
4038 \r
4039         arrow[4].x = d_x - w;\r
4040         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
4041 \r
4042         arrow[5].x = d_x - w;\r
4043         arrow[5].y = s_y - A_WIDTH;\r
4044 \r
4045         arrow[6].x = s_x;\r
4046         arrow[6].y = s_y - A_WIDTH;\r
4047     }\r
4048     else {\r
4049         /* [AS] Needed a lot of paper for this! :-) */\r
4050         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4051         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4052   \r
4053         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4054 \r
4055         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4056 \r
4057         x = s_x;\r
4058         y = s_y;\r
4059 \r
4060         arrow[0].x = Round(x - j);\r
4061         arrow[0].y = Round(y + j*dx);\r
4062 \r
4063         arrow[1].x = Round(x + j);\r
4064         arrow[1].y = Round(y - j*dx);\r
4065 \r
4066         if( d_x > s_x ) {\r
4067             x = (double) d_x - k;\r
4068             y = (double) d_y - k*dy;\r
4069         }\r
4070         else {\r
4071             x = (double) d_x + k;\r
4072             y = (double) d_y + k*dy;\r
4073         }\r
4074 \r
4075         arrow[2].x = Round(x + j);\r
4076         arrow[2].y = Round(y - j*dx);\r
4077 \r
4078         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4079         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4080 \r
4081         arrow[4].x = d_x;\r
4082         arrow[4].y = d_y;\r
4083 \r
4084         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4085         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4086 \r
4087         arrow[6].x = Round(x - j);\r
4088         arrow[6].y = Round(y + j*dx);\r
4089     }\r
4090 \r
4091     Polygon( hdc, arrow, 7 );\r
4092 }\r
4093 \r
4094 /* [AS] Draw an arrow between two squares */\r
4095 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4096 {\r
4097     int s_x, s_y, d_x, d_y;\r
4098     HPEN hpen;\r
4099     HPEN holdpen;\r
4100     HBRUSH hbrush;\r
4101     HBRUSH holdbrush;\r
4102     LOGBRUSH stLB;\r
4103 \r
4104     if( s_col == d_col && s_row == d_row ) {\r
4105         return;\r
4106     }\r
4107 \r
4108     /* Get source and destination points */\r
4109     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4110     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4111 \r
4112     if( d_y > s_y ) {\r
4113         d_y += squareSize / 4;\r
4114     }\r
4115     else if( d_y < s_y ) {\r
4116         d_y += 3 * squareSize / 4;\r
4117     }\r
4118     else {\r
4119         d_y += squareSize / 2;\r
4120     }\r
4121 \r
4122     if( d_x > s_x ) {\r
4123         d_x += squareSize / 4;\r
4124     }\r
4125     else if( d_x < s_x ) {\r
4126         d_x += 3 * squareSize / 4;\r
4127     }\r
4128     else {\r
4129         d_x += squareSize / 2;\r
4130     }\r
4131 \r
4132     s_x += squareSize / 2;\r
4133     s_y += squareSize / 2;\r
4134 \r
4135     /* Adjust width */\r
4136     A_WIDTH = squareSize / 14;\r
4137 \r
4138     /* Draw */\r
4139     stLB.lbStyle = BS_SOLID;\r
4140     stLB.lbColor = appData.highlightArrowColor;\r
4141     stLB.lbHatch = 0;\r
4142 \r
4143     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4144     holdpen = SelectObject( hdc, hpen );\r
4145     hbrush = CreateBrushIndirect( &stLB );\r
4146     holdbrush = SelectObject( hdc, hbrush );\r
4147 \r
4148     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4149 \r
4150     SelectObject( hdc, holdpen );\r
4151     SelectObject( hdc, holdbrush );\r
4152     DeleteObject( hpen );\r
4153     DeleteObject( hbrush );\r
4154 }\r
4155 \r
4156 BOOL HasHighlightInfo()\r
4157 {\r
4158     BOOL result = FALSE;\r
4159 \r
4160     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4161         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4162     {\r
4163         result = TRUE;\r
4164     }\r
4165 \r
4166     return result;\r
4167 }\r
4168 \r
4169 BOOL IsDrawArrowEnabled()\r
4170 {\r
4171     BOOL result = FALSE;\r
4172 \r
4173     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4174         result = TRUE;\r
4175     }\r
4176 \r
4177     return result;\r
4178 }\r
4179 \r
4180 VOID DrawArrowHighlight( HDC hdc )\r
4181 {\r
4182     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4183         DrawArrowBetweenSquares( hdc,\r
4184             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4185             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4186     }\r
4187 }\r
4188 \r
4189 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4190 {\r
4191     HRGN result = NULL;\r
4192 \r
4193     if( HasHighlightInfo() ) {\r
4194         int x1, y1, x2, y2;\r
4195         int sx, sy, dx, dy;\r
4196 \r
4197         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4198         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4199 \r
4200         sx = MIN( x1, x2 );\r
4201         sy = MIN( y1, y2 );\r
4202         dx = MAX( x1, x2 ) + squareSize;\r
4203         dy = MAX( y1, y2 ) + squareSize;\r
4204 \r
4205         result = CreateRectRgn( sx, sy, dx, dy );\r
4206     }\r
4207 \r
4208     return result;\r
4209 }\r
4210 \r
4211 /*\r
4212     Warning: this function modifies the behavior of several other functions. \r
4213     \r
4214     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4215     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4216     repaint is scattered all over the place, which is not good for features such as\r
4217     "arrow highlighting" that require a full repaint of the board.\r
4218 \r
4219     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4220     user interaction, when speed is not so important) but especially to avoid errors\r
4221     in the displayed graphics.\r
4222 \r
4223     In such patched places, I always try refer to this function so there is a single\r
4224     place to maintain knowledge.\r
4225     \r
4226     To restore the original behavior, just return FALSE unconditionally.\r
4227 */\r
4228 BOOL IsFullRepaintPreferrable()\r
4229 {\r
4230     BOOL result = FALSE;\r
4231 \r
4232     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4233         /* Arrow may appear on the board */\r
4234         result = TRUE;\r
4235     }\r
4236 \r
4237     return result;\r
4238 }\r
4239 \r
4240 /* \r
4241     This function is called by DrawPosition to know whether a full repaint must\r
4242     be forced or not.\r
4243 \r
4244     Only DrawPosition may directly call this function, which makes use of \r
4245     some state information. Other function should call DrawPosition specifying \r
4246     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4247 */\r
4248 BOOL DrawPositionNeedsFullRepaint()\r
4249 {\r
4250     BOOL result = FALSE;\r
4251 \r
4252     /* \r
4253         Probably a slightly better policy would be to trigger a full repaint\r
4254         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4255         but animation is fast enough that it's difficult to notice.\r
4256     */\r
4257     if( animInfo.piece == EmptySquare ) {\r
4258         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4259             result = TRUE;\r
4260         }\r
4261     }\r
4262 \r
4263     return result;\r
4264 }\r
4265 \r
4266 VOID\r
4267 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4268 {\r
4269   int row, column, x, y, square_color, piece_color;\r
4270   ChessSquare piece;\r
4271   HBRUSH oldBrush;\r
4272   HDC texture_hdc = NULL;\r
4273 \r
4274   /* [AS] Initialize background textures if needed */\r
4275   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4276       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4277       if( backTextureSquareSize != squareSize \r
4278        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4279           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4280           backTextureSquareSize = squareSize;\r
4281           RebuildTextureSquareInfo();\r
4282       }\r
4283 \r
4284       texture_hdc = CreateCompatibleDC( hdc );\r
4285   }\r
4286 \r
4287   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4288     for (column = 0; column < BOARD_WIDTH; column++) {\r
4289   \r
4290       SquareToPos(row, column, &x, &y);\r
4291 \r
4292       piece = board[row][column];\r
4293 \r
4294       square_color = ((column + row) % 2) == 1;\r
4295       if( gameInfo.variant == VariantXiangqi ) {\r
4296           square_color = !InPalace(row, column);\r
4297           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4298           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4299       }\r
4300       piece_color = (int) piece < (int) BlackPawn;\r
4301 \r
4302 \r
4303       /* [HGM] holdings file: light square or black */\r
4304       if(column == BOARD_LEFT-2) {\r
4305             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4306                 square_color = 1;\r
4307             else {\r
4308                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4309                 continue;\r
4310             }\r
4311       } else\r
4312       if(column == BOARD_RGHT + 1 ) {\r
4313             if( row < gameInfo.holdingsSize )\r
4314                 square_color = 1;\r
4315             else {\r
4316                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4317                 continue;\r
4318             }\r
4319       }\r
4320       if(column == BOARD_LEFT-1 ) /* left align */\r
4321             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4322       else if( column == BOARD_RGHT) /* right align */\r
4323             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4324       else\r
4325       if (appData.monoMode) {\r
4326         if (piece == EmptySquare) {\r
4327           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4328                  square_color ? WHITENESS : BLACKNESS);\r
4329         } else {\r
4330           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4331         }\r
4332       } \r
4333       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4334           /* [AS] Draw the square using a texture bitmap */\r
4335           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4336           int r = row, c = column; // [HGM] do not flip board in flipView\r
4337           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4338 \r
4339           DrawTile( x, y, \r
4340               squareSize, squareSize, \r
4341               hdc, \r
4342               texture_hdc,\r
4343               backTextureSquareInfo[r][c].mode,\r
4344               backTextureSquareInfo[r][c].x,\r
4345               backTextureSquareInfo[r][c].y );\r
4346 \r
4347           SelectObject( texture_hdc, hbm );\r
4348 \r
4349           if (piece != EmptySquare) {\r
4350               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4351           }\r
4352       }\r
4353       else {\r
4354         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4355 \r
4356         oldBrush = SelectObject(hdc, brush );\r
4357         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4358         SelectObject(hdc, oldBrush);\r
4359         if (piece != EmptySquare)\r
4360           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4361       }\r
4362     }\r
4363   }\r
4364 \r
4365   if( texture_hdc != NULL ) {\r
4366     DeleteDC( texture_hdc );\r
4367   }\r
4368 }\r
4369 \r
4370 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4371 void fputDW(FILE *f, int x)\r
4372 {\r
4373         fputc(x     & 255, f);\r
4374         fputc(x>>8  & 255, f);\r
4375         fputc(x>>16 & 255, f);\r
4376         fputc(x>>24 & 255, f);\r
4377 }\r
4378 \r
4379 #define MAX_CLIPS 200   /* more than enough */\r
4380 \r
4381 VOID\r
4382 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4383 {\r
4384 //  HBITMAP bufferBitmap;\r
4385   BITMAP bi;\r
4386 //  RECT Rect;\r
4387   HDC tmphdc;\r
4388   HBITMAP hbm;\r
4389   int w = 100, h = 50;\r
4390 \r
4391   if(logo == NULL) return;\r
4392 //  GetClientRect(hwndMain, &Rect);\r
4393 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4394 //                                      Rect.bottom-Rect.top+1);\r
4395   tmphdc = CreateCompatibleDC(hdc);\r
4396   hbm = SelectObject(tmphdc, logo);\r
4397   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4398             w = bi.bmWidth;\r
4399             h = bi.bmHeight;\r
4400   }\r
4401   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4402                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4403   SelectObject(tmphdc, hbm);\r
4404   DeleteDC(tmphdc);\r
4405 }\r
4406 \r
4407 VOID\r
4408 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4409 {\r
4410   static Board lastReq, lastDrawn;\r
4411   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4412   static int lastDrawnFlipView = 0;\r
4413   static int lastReqValid = 0, lastDrawnValid = 0;\r
4414   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4415   HDC tmphdc;\r
4416   HDC hdcmem;\r
4417   HBITMAP bufferBitmap;\r
4418   HBITMAP oldBitmap;\r
4419   RECT Rect;\r
4420   HRGN clips[MAX_CLIPS];\r
4421   ChessSquare dragged_piece = EmptySquare;\r
4422 \r
4423   /* I'm undecided on this - this function figures out whether a full\r
4424    * repaint is necessary on its own, so there's no real reason to have the\r
4425    * caller tell it that.  I think this can safely be set to FALSE - but\r
4426    * if we trust the callers not to request full repaints unnessesarily, then\r
4427    * we could skip some clipping work.  In other words, only request a full\r
4428    * redraw when the majority of pieces have changed positions (ie. flip, \r
4429    * gamestart and similar)  --Hawk\r
4430    */\r
4431   Boolean fullrepaint = repaint;\r
4432 \r
4433   if( DrawPositionNeedsFullRepaint() ) {\r
4434       fullrepaint = TRUE;\r
4435   }\r
4436 \r
4437 #if 0\r
4438   if( fullrepaint ) {\r
4439       static int repaint_count = 0;\r
4440       char buf[128];\r
4441 \r
4442       repaint_count++;\r
4443       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4444       OutputDebugString( buf );\r
4445   }\r
4446 #endif\r
4447 \r
4448   if (board == NULL) {\r
4449     if (!lastReqValid) {\r
4450       return;\r
4451     }\r
4452     board = lastReq;\r
4453   } else {\r
4454     CopyBoard(lastReq, board);\r
4455     lastReqValid = 1;\r
4456   }\r
4457 \r
4458   if (doingSizing) {\r
4459     return;\r
4460   }\r
4461 \r
4462   if (IsIconic(hwndMain)) {\r
4463     return;\r
4464   }\r
4465 \r
4466   if (hdc == NULL) {\r
4467     hdc = GetDC(hwndMain);\r
4468     if (!appData.monoMode) {\r
4469       SelectPalette(hdc, hPal, FALSE);\r
4470       RealizePalette(hdc);\r
4471     }\r
4472     releaseDC = TRUE;\r
4473   } else {\r
4474     releaseDC = FALSE;\r
4475   }\r
4476 \r
4477 #if 0\r
4478   fprintf(debugFP, "*******************************\n"\r
4479                    "repaint = %s\n"\r
4480                    "dragInfo.from (%d,%d)\n"\r
4481                    "dragInfo.start (%d,%d)\n"\r
4482                    "dragInfo.pos (%d,%d)\n"\r
4483                    "dragInfo.lastpos (%d,%d)\n", \r
4484                     repaint ? "TRUE" : "FALSE",\r
4485                     dragInfo.from.x, dragInfo.from.y, \r
4486                     dragInfo.start.x, dragInfo.start.y,\r
4487                     dragInfo.pos.x, dragInfo.pos.y,\r
4488                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4489   fprintf(debugFP, "prev:  ");\r
4490   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4491     for (column = 0; column < BOARD_WIDTH; column++) {\r
4492       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4493     }\r
4494   }\r
4495   fprintf(debugFP, "\n");\r
4496   fprintf(debugFP, "board: ");\r
4497   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4498     for (column = 0; column < BOARD_WIDTH; column++) {\r
4499       fprintf(debugFP, "%d ", board[row][column]);\r
4500     }\r
4501   }\r
4502   fprintf(debugFP, "\n");\r
4503   fflush(debugFP);\r
4504 #endif\r
4505 \r
4506   /* Create some work-DCs */\r
4507   hdcmem = CreateCompatibleDC(hdc);\r
4508   tmphdc = CreateCompatibleDC(hdc);\r
4509 \r
4510   /* If dragging is in progress, we temporarely remove the piece */\r
4511   /* [HGM] or temporarily decrease count if stacked              */\r
4512   /*       !! Moved to before board compare !!                   */\r
4513   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4514     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4515     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4516             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4517         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4518     } else \r
4519     if(dragInfo.from.x == BOARD_RGHT+1) {\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         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4524   }\r
4525 \r
4526   /* Figure out which squares need updating by comparing the \r
4527    * newest board with the last drawn board and checking if\r
4528    * flipping has changed.\r
4529    */\r
4530   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4531     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4532       for (column = 0; column < BOARD_WIDTH; column++) {\r
4533         if (lastDrawn[row][column] != board[row][column]) {\r
4534           SquareToPos(row, column, &x, &y);\r
4535           clips[num_clips++] =\r
4536             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4537         }\r
4538       }\r
4539     }\r
4540     for (i=0; i<2; i++) {\r
4541       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4542           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4543         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4544             lastDrawnHighlight.sq[i].y >= 0) {\r
4545           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4546                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4547           clips[num_clips++] =\r
4548             CreateRectRgn(x - lineGap, y - lineGap, \r
4549                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4550         }\r
4551         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4552           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4553           clips[num_clips++] =\r
4554             CreateRectRgn(x - lineGap, y - lineGap, \r
4555                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4556         }\r
4557       }\r
4558     }\r
4559     for (i=0; i<2; i++) {\r
4560       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4561           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4562         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4563             lastDrawnPremove.sq[i].y >= 0) {\r
4564           SquareToPos(lastDrawnPremove.sq[i].y,\r
4565                       lastDrawnPremove.sq[i].x, &x, &y);\r
4566           clips[num_clips++] =\r
4567             CreateRectRgn(x - lineGap, y - lineGap, \r
4568                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4569         }\r
4570         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4571             premoveHighlightInfo.sq[i].y >= 0) {\r
4572           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4573                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4574           clips[num_clips++] =\r
4575             CreateRectRgn(x - lineGap, y - lineGap, \r
4576                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4577         }\r
4578       }\r
4579     }\r
4580   } else {\r
4581     fullrepaint = TRUE;\r
4582   }\r
4583 \r
4584   /* Create a buffer bitmap - this is the actual bitmap\r
4585    * being written to.  When all the work is done, we can\r
4586    * copy it to the real DC (the screen).  This avoids\r
4587    * the problems with flickering.\r
4588    */\r
4589   GetClientRect(hwndMain, &Rect);\r
4590   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4591                                         Rect.bottom-Rect.top+1);\r
4592   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4593   if (!appData.monoMode) {\r
4594     SelectPalette(hdcmem, hPal, FALSE);\r
4595   }\r
4596 \r
4597   /* Create clips for dragging */\r
4598   if (!fullrepaint) {\r
4599     if (dragInfo.from.x >= 0) {\r
4600       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4601       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4602     }\r
4603     if (dragInfo.start.x >= 0) {\r
4604       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4605       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4606     }\r
4607     if (dragInfo.pos.x >= 0) {\r
4608       x = dragInfo.pos.x - squareSize / 2;\r
4609       y = dragInfo.pos.y - squareSize / 2;\r
4610       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4611     }\r
4612     if (dragInfo.lastpos.x >= 0) {\r
4613       x = dragInfo.lastpos.x - squareSize / 2;\r
4614       y = dragInfo.lastpos.y - squareSize / 2;\r
4615       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4616     }\r
4617   }\r
4618 \r
4619   /* Are we animating a move?  \r
4620    * If so, \r
4621    *   - remove the piece from the board (temporarely)\r
4622    *   - calculate the clipping region\r
4623    */\r
4624   if (!fullrepaint) {\r
4625     if (animInfo.piece != EmptySquare) {\r
4626       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4627       x = boardRect.left + animInfo.lastpos.x;\r
4628       y = boardRect.top + animInfo.lastpos.y;\r
4629       x2 = boardRect.left + animInfo.pos.x;\r
4630       y2 = boardRect.top + animInfo.pos.y;\r
4631       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4632       /* Slight kludge.  The real problem is that after AnimateMove is\r
4633          done, the position on the screen does not match lastDrawn.\r
4634          This currently causes trouble only on e.p. captures in\r
4635          atomic, where the piece moves to an empty square and then\r
4636          explodes.  The old and new positions both had an empty square\r
4637          at the destination, but animation has drawn a piece there and\r
4638          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4639       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4640     }\r
4641   }\r
4642 \r
4643   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4644   if (num_clips == 0)\r
4645     fullrepaint = TRUE;\r
4646 \r
4647   /* Set clipping on the memory DC */\r
4648   if (!fullrepaint) {\r
4649     SelectClipRgn(hdcmem, clips[0]);\r
4650     for (x = 1; x < num_clips; x++) {\r
4651       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4652         abort();  // this should never ever happen!\r
4653     }\r
4654   }\r
4655 \r
4656   /* Do all the drawing to the memory DC */\r
4657   if(explodeInfo.radius) { // [HGM] atomic\r
4658         HBRUSH oldBrush;\r
4659         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4660         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4661         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4662         x += squareSize/2;\r
4663         y += squareSize/2;\r
4664         if(!fullrepaint) {\r
4665           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4666           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4667         }\r
4668         DrawGridOnDC(hdcmem);\r
4669         DrawHighlightsOnDC(hdcmem);\r
4670         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4671         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4672         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4673         SelectObject(hdcmem, oldBrush);\r
4674   } else {\r
4675     DrawGridOnDC(hdcmem);\r
4676     DrawHighlightsOnDC(hdcmem);\r
4677     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4678   }\r
4679   if(logoHeight) {\r
4680         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4681         if(appData.autoLogo) {\r
4682           \r
4683           switch(gameMode) { // pick logos based on game mode\r
4684             case IcsObserving:\r
4685                 whiteLogo = second.programLogo; // ICS logo\r
4686                 blackLogo = second.programLogo;\r
4687             default:\r
4688                 break;\r
4689             case IcsPlayingWhite:\r
4690                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4691                 blackLogo = second.programLogo; // ICS logo\r
4692                 break;\r
4693             case IcsPlayingBlack:\r
4694                 whiteLogo = second.programLogo; // ICS logo\r
4695                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4696                 break;\r
4697             case TwoMachinesPlay:\r
4698                 if(first.twoMachinesColor[0] == 'b') {\r
4699                     whiteLogo = second.programLogo;\r
4700                     blackLogo = first.programLogo;\r
4701                 }\r
4702                 break;\r
4703             case MachinePlaysWhite:\r
4704                 blackLogo = userLogo;\r
4705                 break;\r
4706             case MachinePlaysBlack:\r
4707                 whiteLogo = userLogo;\r
4708                 blackLogo = first.programLogo;\r
4709           }\r
4710         }\r
4711         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4712         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4713   }\r
4714 \r
4715   if( appData.highlightMoveWithArrow ) {\r
4716     DrawArrowHighlight(hdcmem);\r
4717   }\r
4718 \r
4719   DrawCoordsOnDC(hdcmem);\r
4720 \r
4721   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4722                  /* to make sure lastDrawn contains what is actually drawn */\r
4723 \r
4724   /* Put the dragged piece back into place and draw it (out of place!) */\r
4725     if (dragged_piece != EmptySquare) {\r
4726     /* [HGM] or restack */\r
4727     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4728                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4729     else\r
4730     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4731                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4732     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4733     x = dragInfo.pos.x - squareSize / 2;\r
4734     y = dragInfo.pos.y - squareSize / 2;\r
4735     DrawPieceOnDC(hdcmem, dragged_piece,\r
4736                   ((int) dragged_piece < (int) BlackPawn), \r
4737                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4738   }   \r
4739   \r
4740   /* Put the animated piece back into place and draw it */\r
4741   if (animInfo.piece != EmptySquare) {\r
4742     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4743     x = boardRect.left + animInfo.pos.x;\r
4744     y = boardRect.top + animInfo.pos.y;\r
4745     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4746                   ((int) animInfo.piece < (int) BlackPawn),\r
4747                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4748   }\r
4749 \r
4750   /* Release the bufferBitmap by selecting in the old bitmap \r
4751    * and delete the memory DC\r
4752    */\r
4753   SelectObject(hdcmem, oldBitmap);\r
4754   DeleteDC(hdcmem);\r
4755 \r
4756   /* Set clipping on the target DC */\r
4757   if (!fullrepaint) {\r
4758     SelectClipRgn(hdc, clips[0]);\r
4759     for (x = 1; x < num_clips; x++) {\r
4760       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4761         abort();   // this should never ever happen!\r
4762     } \r
4763   }\r
4764 \r
4765   /* Copy the new bitmap onto the screen in one go.\r
4766    * This way we avoid any flickering\r
4767    */\r
4768   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4769   BitBlt(hdc, boardRect.left, boardRect.top,\r
4770          boardRect.right - boardRect.left,\r
4771          boardRect.bottom - boardRect.top,\r
4772          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4773   if(saveDiagFlag) { \r
4774     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4775     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4776 \r
4777     GetObject(bufferBitmap, sizeof(b), &b);\r
4778     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4779         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4780         bih.biWidth = b.bmWidth;\r
4781         bih.biHeight = b.bmHeight;\r
4782         bih.biPlanes = 1;\r
4783         bih.biBitCount = b.bmBitsPixel;\r
4784         bih.biCompression = 0;\r
4785         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4786         bih.biXPelsPerMeter = 0;\r
4787         bih.biYPelsPerMeter = 0;\r
4788         bih.biClrUsed = 0;\r
4789         bih.biClrImportant = 0;\r
4790 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4791 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4792         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4793 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4794 \r
4795 #if 1\r
4796         wb = b.bmWidthBytes;\r
4797         // count colors\r
4798         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4799                 int k = ((int*) pData)[i];\r
4800                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4801                 if(j >= 16) break;\r
4802                 color[j] = k;\r
4803                 if(j >= nrColors) nrColors = j+1;\r
4804         }\r
4805         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4806                 INT p = 0;\r
4807                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4808                     for(w=0; w<(wb>>2); w+=2) {\r
4809                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4810                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4811                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4812                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4813                         pData[p++] = m | j<<4;\r
4814                     }\r
4815                     while(p&3) pData[p++] = 0;\r
4816                 }\r
4817                 fac = 3;\r
4818                 wb = ((wb+31)>>5)<<2;\r
4819         }\r
4820         // write BITMAPFILEHEADER\r
4821         fprintf(diagFile, "BM");\r
4822         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4823         fputDW(diagFile, 0);\r
4824         fputDW(diagFile, 0x36 + (fac?64:0));\r
4825         // write BITMAPINFOHEADER\r
4826         fputDW(diagFile, 40);\r
4827         fputDW(diagFile, b.bmWidth);\r
4828         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4829         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4830         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4831         fputDW(diagFile, 0);\r
4832         fputDW(diagFile, 0);\r
4833         fputDW(diagFile, 0);\r
4834         fputDW(diagFile, 0);\r
4835         fputDW(diagFile, 0);\r
4836         fputDW(diagFile, 0);\r
4837         // write color table\r
4838         if(fac)\r
4839         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4840         // write bitmap data\r
4841         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4842                 fputc(pData[i], diagFile);\r
4843 #endif\r
4844      }\r
4845   }\r
4846 \r
4847   SelectObject(tmphdc, oldBitmap);\r
4848 \r
4849   /* Massive cleanup */\r
4850   for (x = 0; x < num_clips; x++)\r
4851     DeleteObject(clips[x]);\r
4852 \r
4853   DeleteDC(tmphdc);\r
4854   DeleteObject(bufferBitmap);\r
4855 \r
4856   if (releaseDC) \r
4857     ReleaseDC(hwndMain, hdc);\r
4858   \r
4859   if (lastDrawnFlipView != flipView) {\r
4860     if (flipView)\r
4861       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4862     else\r
4863       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4864   }\r
4865 \r
4866 /*  CopyBoard(lastDrawn, board);*/\r
4867   lastDrawnHighlight = highlightInfo;\r
4868   lastDrawnPremove   = premoveHighlightInfo;\r
4869   lastDrawnFlipView = flipView;\r
4870   lastDrawnValid = 1;\r
4871 }\r
4872 \r
4873 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4874 int\r
4875 SaveDiagram(f)\r
4876      FILE *f;\r
4877 {\r
4878     saveDiagFlag = 1; diagFile = f;\r
4879     HDCDrawPosition(NULL, TRUE, NULL);\r
4880 \r
4881     saveDiagFlag = 0;\r
4882 \r
4883 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4884     \r
4885     fclose(f);\r
4886     return TRUE;\r
4887 }\r
4888 \r
4889 \r
4890 /*---------------------------------------------------------------------------*\\r
4891 | CLIENT PAINT PROCEDURE\r
4892 |   This is the main event-handler for the WM_PAINT message.\r
4893 |\r
4894 \*---------------------------------------------------------------------------*/\r
4895 VOID\r
4896 PaintProc(HWND hwnd)\r
4897 {\r
4898   HDC         hdc;\r
4899   PAINTSTRUCT ps;\r
4900   HFONT       oldFont;\r
4901 \r
4902   if((hdc = BeginPaint(hwnd, &ps))) {\r
4903     if (IsIconic(hwnd)) {\r
4904       DrawIcon(hdc, 2, 2, iconCurrent);\r
4905     } else {\r
4906       if (!appData.monoMode) {\r
4907         SelectPalette(hdc, hPal, FALSE);\r
4908         RealizePalette(hdc);\r
4909       }\r
4910       HDCDrawPosition(hdc, 1, NULL);\r
4911       oldFont =\r
4912         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4913       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4914                  ETO_CLIPPED|ETO_OPAQUE,\r
4915                  &messageRect, messageText, strlen(messageText), NULL);\r
4916       SelectObject(hdc, oldFont);\r
4917       DisplayBothClocks();\r
4918     }\r
4919     EndPaint(hwnd,&ps);\r
4920   }\r
4921 \r
4922   return;\r
4923 }\r
4924 \r
4925 \r
4926 /*\r
4927  * If the user selects on a border boundary, return -1; if off the board,\r
4928  *   return -2.  Otherwise map the event coordinate to the square.\r
4929  * The offset boardRect.left or boardRect.top must already have been\r
4930  *   subtracted from x.\r
4931  */\r
4932 int\r
4933 EventToSquare(int x)\r
4934 {\r
4935   if (x <= 0)\r
4936     return -2;\r
4937   if (x < lineGap)\r
4938     return -1;\r
4939   x -= lineGap;\r
4940   if ((x % (squareSize + lineGap)) >= squareSize)\r
4941     return -1;\r
4942   x /= (squareSize + lineGap);\r
4943   if (x >= BOARD_SIZE)\r
4944     return -2;\r
4945   return x;\r
4946 }\r
4947 \r
4948 typedef struct {\r
4949   char piece;\r
4950   int command;\r
4951   char* name;\r
4952 } DropEnable;\r
4953 \r
4954 DropEnable dropEnables[] = {\r
4955   { 'P', DP_Pawn, "Pawn" },\r
4956   { 'N', DP_Knight, "Knight" },\r
4957   { 'B', DP_Bishop, "Bishop" },\r
4958   { 'R', DP_Rook, "Rook" },\r
4959   { 'Q', DP_Queen, "Queen" },\r
4960 };\r
4961 \r
4962 VOID\r
4963 SetupDropMenu(HMENU hmenu)\r
4964 {\r
4965   int i, count, enable;\r
4966   char *p;\r
4967   extern char white_holding[], black_holding[];\r
4968   char item[MSG_SIZ];\r
4969 \r
4970   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4971     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4972                dropEnables[i].piece);\r
4973     count = 0;\r
4974     while (p && *p++ == dropEnables[i].piece) count++;\r
4975     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4976     enable = count > 0 || !appData.testLegality\r
4977       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4978                       && !appData.icsActive);\r
4979     ModifyMenu(hmenu, dropEnables[i].command,\r
4980                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4981                dropEnables[i].command, item);\r
4982   }\r
4983 }\r
4984 \r
4985 /* Event handler for mouse messages */\r
4986 VOID\r
4987 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4988 {\r
4989   int x, y;\r
4990   POINT pt;\r
4991   static int recursive = 0;\r
4992   HMENU hmenu;\r
4993 //  BOOLEAN needsRedraw = FALSE;\r
4994   BOOLEAN saveAnimate;\r
4995   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4996   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4997   ChessMove moveType;\r
4998 \r
4999   if (recursive) {\r
5000     if (message == WM_MBUTTONUP) {\r
5001       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
5002          to the middle button: we simulate pressing the left button too!\r
5003          */\r
5004       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
5005       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
5006     }\r
5007     return;\r
5008   }\r
5009   recursive++;\r
5010   \r
5011   pt.x = LOWORD(lParam);\r
5012   pt.y = HIWORD(lParam);\r
5013   x = EventToSquare(pt.x - boardRect.left);\r
5014   y = EventToSquare(pt.y - boardRect.top);\r
5015   if (!flipView && y >= 0) {\r
5016     y = BOARD_HEIGHT - 1 - y;\r
5017   }\r
5018   if (flipView && x >= 0) {\r
5019     x = BOARD_WIDTH - 1 - x;\r
5020   }\r
5021 \r
5022   switch (message) {\r
5023   case WM_LBUTTONDOWN:\r
5024     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
5025         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
5026         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
5027         if(gameInfo.holdingsWidth && \r
5028                 (WhiteOnMove(currentMove) \r
5029                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
5030                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
5031             // click in right holdings, for determining promotion piece\r
5032             ChessSquare p = boards[currentMove][y][x];\r
5033             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
5034             if(p != EmptySquare) {\r
5035                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
5036                 fromX = fromY = -1;\r
5037                 break;\r
5038             }\r
5039         }\r
5040         DrawPosition(FALSE, boards[currentMove]);\r
5041         break;\r
5042     }\r
5043     ErrorPopDown();\r
5044     sameAgain = FALSE;\r
5045     if (y == -2) {\r
5046       /* Downclick vertically off board; check if on clock */\r
5047       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5048         if (gameMode == EditPosition) {\r
5049           SetWhiteToPlayEvent();\r
5050         } else if (gameMode == IcsPlayingBlack ||\r
5051                    gameMode == MachinePlaysWhite) {\r
5052           CallFlagEvent();\r
5053         } else if (gameMode == EditGame) {\r
5054           AdjustClock(flipClock, -1);\r
5055         }\r
5056       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5057         if (gameMode == EditPosition) {\r
5058           SetBlackToPlayEvent();\r
5059         } else if (gameMode == IcsPlayingWhite ||\r
5060                    gameMode == MachinePlaysBlack) {\r
5061           CallFlagEvent();\r
5062         } else if (gameMode == EditGame) {\r
5063           AdjustClock(!flipClock, -1);\r
5064         }\r
5065       }\r
5066       if (!appData.highlightLastMove) {\r
5067         ClearHighlights();\r
5068         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
5069       }\r
5070       fromX = fromY = -1;\r
5071       dragInfo.start.x = dragInfo.start.y = -1;\r
5072       dragInfo.from = dragInfo.start;\r
5073       break;\r
5074     } else if (x < 0 || y < 0\r
5075       /* [HGM] block clicks between board and holdings */\r
5076               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
5077               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
5078               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
5079         /* EditPosition, empty square, or different color piece;\r
5080            click-click move is possible */\r
5081                                ) {\r
5082       break;\r
5083     } else if (fromX == x && fromY == y) {\r
5084       /* Downclick on same square again */\r
5085       ClearHighlights();\r
5086       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5087       sameAgain = TRUE;  \r
5088     } else if (fromX != -1 &&\r
5089                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5090                                                                         ) {\r
5091       /* Downclick on different square. */\r
5092       /* [HGM] if on holdings file, should count as new first click ! */\r
5093       { /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5094         toX = x;\r
5095         toY = y;\r
5096         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5097            to make sure move is legal before showing promotion popup */\r
5098         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5099         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5100                 fromX = fromY = -1; \r
5101                 ClearHighlights();\r
5102                 DrawPosition(FALSE, boards[currentMove]);\r
5103                 break; \r
5104         } else \r
5105         if(moveType != ImpossibleMove) {\r
5106           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5107           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5108             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5109               appData.alwaysPromoteToQueen)) {\r
5110                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5111                   if (!appData.highlightLastMove) {\r
5112                       ClearHighlights();\r
5113                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5114                   }\r
5115           } else\r
5116           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5117                   SetHighlights(fromX, fromY, toX, toY);\r
5118                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5119                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5120                      If promotion to Q is legal, all are legal! */\r
5121                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5122                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5123                     // kludge to temporarily execute move on display, wthout promotng yet\r
5124                     promotionChoice = TRUE;\r
5125                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5126                     boards[currentMove][toY][toX] = p;\r
5127                     DrawPosition(FALSE, boards[currentMove]);\r
5128                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5129                     boards[currentMove][toY][toX] = q;\r
5130                   } else\r
5131                   PromotionPopup(hwnd);\r
5132           } else {       /* not a promotion */\r
5133              if (appData.animate || appData.highlightLastMove) {\r
5134                  SetHighlights(fromX, fromY, toX, toY);\r
5135              } else {\r
5136                  ClearHighlights();\r
5137              }\r
5138              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5139              fromX = fromY = -1;\r
5140              if (appData.animate && !appData.highlightLastMove) {\r
5141                   ClearHighlights();\r
5142                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5143              }\r
5144           }\r
5145           break;\r
5146         }\r
5147         if (gotPremove) {\r
5148             /* [HGM] it seemed that braces were missing here */\r
5149             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5150             fromX = fromY = -1;\r
5151             break;\r
5152         }\r
5153       }\r
5154       ClearHighlights();\r
5155       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5156     }\r
5157     /* First downclick, or restart on a square with same color piece */\r
5158     if (!frozen && OKToStartUserMove(x, y)) {\r
5159       fromX = x;\r
5160       fromY = y;\r
5161       dragInfo.lastpos = pt;\r
5162       dragInfo.from.x = fromX;\r
5163       dragInfo.from.y = fromY;\r
5164       dragInfo.start = dragInfo.from;\r
5165       SetCapture(hwndMain);\r
5166     } else {\r
5167       fromX = fromY = -1;\r
5168       dragInfo.start.x = dragInfo.start.y = -1;\r
5169       dragInfo.from = dragInfo.start;\r
5170       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5171     }\r
5172     break;\r
5173 \r
5174   case WM_LBUTTONUP:\r
5175     ReleaseCapture();\r
5176     if (fromX == -1) break;\r
5177     if (x == fromX && y == fromY) {\r
5178       dragInfo.from.x = dragInfo.from.y = -1;\r
5179       /* Upclick on same square */\r
5180       if (sameAgain) {\r
5181         /* Clicked same square twice: abort click-click move */\r
5182         fromX = fromY = -1;\r
5183         gotPremove = 0;\r
5184         ClearPremoveHighlights();\r
5185       } else {\r
5186         /* First square clicked: start click-click move */\r
5187         SetHighlights(fromX, fromY, -1, -1);\r
5188       }\r
5189       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5190     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5191       /* Errant click; ignore */\r
5192       break;\r
5193     } else {\r
5194       /* Finish drag move. */\r
5195     if (appData.debugMode) {\r
5196         fprintf(debugFP, "release\n");\r
5197     }\r
5198       dragInfo.from.x = dragInfo.from.y = -1;\r
5199       toX = x;\r
5200       toY = y;\r
5201       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5202       appData.animate = appData.animate && !appData.animateDragging;\r
5203       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5204       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5205                 fromX = fromY = -1; \r
5206                 ClearHighlights();\r
5207                 DrawPosition(FALSE, boards[currentMove]);\r
5208                 appData.animate = saveAnimate;\r
5209                 break; \r
5210       } else \r
5211       if(moveType != ImpossibleMove) {\r
5212           /* [HGM] use move type to determine if move is promotion.\r
5213              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5214           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5215             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5216               appData.alwaysPromoteToQueen)) \r
5217                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5218           else \r
5219           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5220                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5221                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5222                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5223                     // kludge to temporarily execute move on display, wthout promotng yet\r
5224                     promotionChoice = TRUE;\r
5225                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5226                     boards[currentMove][toY][toX] = p;\r
5227                     DrawPosition(FALSE, boards[currentMove]);\r
5228                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5229                     boards[currentMove][toY][toX] = q;\r
5230                     appData.animate = saveAnimate;\r
5231                     break;\r
5232                   } else\r
5233                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5234           } else {\r
5235             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5236                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5237                                         moveType == WhiteCapturesEnPassant || \r
5238                                         moveType == BlackCapturesEnPassant   ) )\r
5239                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5240             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5241           }\r
5242       }\r
5243       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5244       appData.animate = saveAnimate;\r
5245       fromX = fromY = -1;\r
5246       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5247         ClearHighlights();\r
5248       }\r
5249       if (appData.animate || appData.animateDragging ||\r
5250           appData.highlightDragging || gotPremove) {\r
5251         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5252       }\r
5253     }\r
5254     dragInfo.start.x = dragInfo.start.y = -1; \r
5255     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5256     break;\r
5257 \r
5258   case WM_MOUSEMOVE:\r
5259     if ((appData.animateDragging || appData.highlightDragging)\r
5260         && (wParam & MK_LBUTTON)\r
5261         && dragInfo.from.x >= 0) \r
5262     {\r
5263       BOOL full_repaint = FALSE;\r
5264 \r
5265       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5266       if (appData.animateDragging) {\r
5267         dragInfo.pos = pt;\r
5268       }\r
5269       if (appData.highlightDragging) {\r
5270         SetHighlights(fromX, fromY, x, y);\r
5271         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5272             full_repaint = TRUE;\r
5273         }\r
5274       }\r
5275       \r
5276       DrawPosition( full_repaint, NULL);\r
5277       \r
5278       dragInfo.lastpos = dragInfo.pos;\r
5279     }\r
5280     break;\r
5281 \r
5282   case WM_MOUSEWHEEL: // [DM]\r
5283     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5284        /* Mouse Wheel is being rolled forward\r
5285         * Play moves forward\r
5286         */\r
5287        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5288                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5289        /* Mouse Wheel is being rolled backward\r
5290         * Play moves backward\r
5291         */\r
5292        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5293                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5294     }\r
5295     break;\r
5296 \r
5297   case WM_MBUTTONDOWN:\r
5298   case WM_RBUTTONDOWN:\r
5299     ErrorPopDown();\r
5300     ReleaseCapture();\r
5301     fromX = fromY = -1;\r
5302     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5303     dragInfo.start.x = dragInfo.start.y = -1;\r
5304     dragInfo.from = dragInfo.start;\r
5305     dragInfo.lastpos = dragInfo.pos;\r
5306     if (appData.highlightDragging) {\r
5307       ClearHighlights();\r
5308     }\r
5309     if(y == -2) {\r
5310       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5311       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5312           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5313       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5314           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5315       }\r
5316     }\r
5317     DrawPosition(TRUE, NULL);\r
5318 \r
5319     switch (gameMode) {\r
5320     case EditPosition:\r
5321     case IcsExamining:\r
5322       if (x < 0 || y < 0) break;\r
5323       fromX = x;\r
5324       fromY = y;\r
5325       if (message == WM_MBUTTONDOWN) {\r
5326         buttonCount = 3;  /* even if system didn't think so */\r
5327         if (wParam & MK_SHIFT) \r
5328           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5329         else\r
5330           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5331       } else { /* message == WM_RBUTTONDOWN */\r
5332 #if 0\r
5333         if (buttonCount == 3) {\r
5334           if (wParam & MK_SHIFT) \r
5335             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5336           else\r
5337             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5338         } else {\r
5339           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5340         }\r
5341 #else\r
5342         /* Just have one menu, on the right button.  Windows users don't\r
5343            think to try the middle one, and sometimes other software steals\r
5344            it, or it doesn't really exist. */\r
5345         if(gameInfo.variant != VariantShogi)\r
5346             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5347         else\r
5348             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5349 #endif\r
5350       }\r
5351       break;\r
5352     case IcsPlayingWhite:\r
5353     case IcsPlayingBlack:\r
5354     case EditGame:\r
5355     case MachinePlaysWhite:\r
5356     case MachinePlaysBlack:\r
5357       if (appData.testLegality &&\r
5358           gameInfo.variant != VariantBughouse &&\r
5359           gameInfo.variant != VariantCrazyhouse) break;\r
5360       if (x < 0 || y < 0) break;\r
5361       fromX = x;\r
5362       fromY = y;\r
5363       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5364       SetupDropMenu(hmenu);\r
5365       MenuPopup(hwnd, pt, hmenu, -1);\r
5366       break;\r
5367     default:\r
5368       break;\r
5369     }\r
5370     break;\r
5371   }\r
5372 \r
5373   recursive--;\r
5374 }\r
5375 \r
5376 /* Preprocess messages for buttons in main window */\r
5377 LRESULT CALLBACK\r
5378 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5379 {\r
5380   int id = GetWindowLong(hwnd, GWL_ID);\r
5381   int i, dir;\r
5382 \r
5383   for (i=0; i<N_BUTTONS; i++) {\r
5384     if (buttonDesc[i].id == id) break;\r
5385   }\r
5386   if (i == N_BUTTONS) return 0;\r
5387   switch (message) {\r
5388   case WM_KEYDOWN:\r
5389     switch (wParam) {\r
5390     case VK_LEFT:\r
5391     case VK_RIGHT:\r
5392       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5393       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5394       return TRUE;\r
5395     }\r
5396     break;\r
5397   case WM_CHAR:\r
5398     switch (wParam) {\r
5399     case '\r':\r
5400       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5401       return TRUE;\r
5402     default:\r
5403       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5404         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5405         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5406         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5407         SetFocus(h);\r
5408         SendMessage(h, WM_CHAR, wParam, lParam);\r
5409         return TRUE;\r
5410       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5411         PopUpMoveDialog((char)wParam);\r
5412       }\r
5413       break;\r
5414     }\r
5415     break;\r
5416   }\r
5417   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5418 }\r
5419 \r
5420 /* Process messages for Promotion dialog box */\r
5421 LRESULT CALLBACK\r
5422 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5423 {\r
5424   char promoChar;\r
5425 \r
5426   switch (message) {\r
5427   case WM_INITDIALOG: /* message: initialize dialog box */\r
5428     /* Center the dialog over the application window */\r
5429     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5430     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5431       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5432        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5433                SW_SHOW : SW_HIDE);\r
5434     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5435     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5436        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5437          PieceToChar(WhiteAngel) != '~') ||\r
5438         (PieceToChar(BlackAngel) >= 'A' &&\r
5439          PieceToChar(BlackAngel) != '~')   ) ?\r
5440                SW_SHOW : SW_HIDE);\r
5441     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5442        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5443          PieceToChar(WhiteMarshall) != '~') ||\r
5444         (PieceToChar(BlackMarshall) >= 'A' &&\r
5445          PieceToChar(BlackMarshall) != '~')   ) ?\r
5446                SW_SHOW : SW_HIDE);\r
5447     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5448     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5449        gameInfo.variant != VariantShogi ?\r
5450                SW_SHOW : SW_HIDE);\r
5451     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5452        gameInfo.variant != VariantShogi ?\r
5453                SW_SHOW : SW_HIDE);\r
5454     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5455        gameInfo.variant == VariantShogi ?\r
5456                SW_SHOW : SW_HIDE);\r
5457     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5458        gameInfo.variant == VariantShogi ?\r
5459                SW_SHOW : SW_HIDE);\r
5460     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5461        gameInfo.variant == VariantSuper ?\r
5462                SW_SHOW : SW_HIDE);\r
5463     return TRUE;\r
5464 \r
5465   case WM_COMMAND: /* message: received a command */\r
5466     switch (LOWORD(wParam)) {\r
5467     case IDCANCEL:\r
5468       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5469       ClearHighlights();\r
5470       DrawPosition(FALSE, NULL);\r
5471       return TRUE;\r
5472     case PB_King:\r
5473       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5474       break;\r
5475     case PB_Queen:\r
5476       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5477       break;\r
5478     case PB_Rook:\r
5479       promoChar = PieceToChar(BlackRook);\r
5480       break;\r
5481     case PB_Bishop:\r
5482       promoChar = PieceToChar(BlackBishop);\r
5483       break;\r
5484     case PB_Chancellor:\r
5485       promoChar = PieceToChar(BlackMarshall);\r
5486       break;\r
5487     case PB_Archbishop:\r
5488       promoChar = PieceToChar(BlackAngel);\r
5489       break;\r
5490     case PB_Knight:\r
5491       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5492       break;\r
5493     default:\r
5494       return FALSE;\r
5495     }\r
5496     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5497     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5498        only show the popup when we are already sure the move is valid or\r
5499        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5500        will figure out it is a promotion from the promoChar. */\r
5501     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5502     if (!appData.highlightLastMove) {\r
5503       ClearHighlights();\r
5504       DrawPosition(FALSE, NULL);\r
5505     }\r
5506     return TRUE;\r
5507   }\r
5508   return FALSE;\r
5509 }\r
5510 \r
5511 /* Pop up promotion dialog */\r
5512 VOID\r
5513 PromotionPopup(HWND hwnd)\r
5514 {\r
5515   FARPROC lpProc;\r
5516 \r
5517   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5518   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5519     hwnd, (DLGPROC)lpProc);\r
5520   FreeProcInstance(lpProc);\r
5521 }\r
5522 \r
5523 /* Toggle ShowThinking */\r
5524 VOID\r
5525 ToggleShowThinking()\r
5526 {\r
5527   appData.showThinking = !appData.showThinking;\r
5528   ShowThinkingEvent();\r
5529 }\r
5530 \r
5531 VOID\r
5532 LoadGameDialog(HWND hwnd, char* title)\r
5533 {\r
5534   UINT number = 0;\r
5535   FILE *f;\r
5536   char fileTitle[MSG_SIZ];\r
5537   f = OpenFileDialog(hwnd, "rb", "",\r
5538                      appData.oldSaveStyle ? "gam" : "pgn",\r
5539                      GAME_FILT,\r
5540                      title, &number, fileTitle, NULL);\r
5541   if (f != NULL) {\r
5542     cmailMsgLoaded = FALSE;\r
5543     if (number == 0) {\r
5544       int error = GameListBuild(f);\r
5545       if (error) {\r
5546         DisplayError("Cannot build game list", error);\r
5547       } else if (!ListEmpty(&gameList) &&\r
5548                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5549         GameListPopUp(f, fileTitle);\r
5550         return;\r
5551       }\r
5552       GameListDestroy();\r
5553       number = 1;\r
5554     }\r
5555     LoadGame(f, number, fileTitle, FALSE);\r
5556   }\r
5557 }\r
5558 \r
5559 VOID\r
5560 ChangedConsoleFont()\r
5561 {\r
5562   CHARFORMAT cfmt;\r
5563   CHARRANGE tmpsel, sel;\r
5564   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5565   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5566   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5567   PARAFORMAT paraf;\r
5568 \r
5569   cfmt.cbSize = sizeof(CHARFORMAT);\r
5570   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5571   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5572   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5573    * size.  This was undocumented in the version of MSVC++ that I had\r
5574    * when I wrote the code, but is apparently documented now.\r
5575    */\r
5576   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5577   cfmt.bCharSet = f->lf.lfCharSet;\r
5578   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5579   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5580   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5581   /* Why are the following seemingly needed too? */\r
5582   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5583   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5584   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5585   tmpsel.cpMin = 0;\r
5586   tmpsel.cpMax = -1; /*999999?*/\r
5587   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5588   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5589   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5590    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5591    */\r
5592   paraf.cbSize = sizeof(paraf);\r
5593   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5594   paraf.dxStartIndent = 0;\r
5595   paraf.dxOffset = WRAP_INDENT;\r
5596   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5597   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5598 }\r
5599 \r
5600 /*---------------------------------------------------------------------------*\\r
5601  *\r
5602  * Window Proc for main window\r
5603  *\r
5604 \*---------------------------------------------------------------------------*/\r
5605 \r
5606 /* Process messages for main window, etc. */\r
5607 LRESULT CALLBACK\r
5608 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5609 {\r
5610   FARPROC lpProc;\r
5611   int wmId, wmEvent;\r
5612   char *defName;\r
5613   FILE *f;\r
5614   UINT number;\r
5615   char fileTitle[MSG_SIZ];\r
5616   char buf[MSG_SIZ];\r
5617   static SnapData sd;\r
5618 \r
5619   switch (message) {\r
5620 \r
5621   case WM_PAINT: /* message: repaint portion of window */\r
5622     PaintProc(hwnd);\r
5623     break;\r
5624 \r
5625   case WM_ERASEBKGND:\r
5626     if (IsIconic(hwnd)) {\r
5627       /* Cheat; change the message */\r
5628       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5629     } else {\r
5630       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5631     }\r
5632     break;\r
5633 \r
5634   case WM_LBUTTONDOWN:\r
5635   case WM_MBUTTONDOWN:\r
5636   case WM_RBUTTONDOWN:\r
5637   case WM_LBUTTONUP:\r
5638   case WM_MBUTTONUP:\r
5639   case WM_RBUTTONUP:\r
5640   case WM_MOUSEMOVE:\r
5641   case WM_MOUSEWHEEL:\r
5642     MouseEvent(hwnd, message, wParam, lParam);\r
5643     break;\r
5644 \r
5645   JAWS_KB_NAVIGATION\r
5646 \r
5647   case WM_CHAR:\r
5648     \r
5649     JAWS_ALT_INTERCEPT\r
5650 \r
5651     if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) { \r
5652         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5653         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5654         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5655         SetFocus(h);\r
5656         SendMessage(h, message, wParam, lParam);\r
5657     } else if(lParam != KF_REPEAT) {\r
5658         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5659                 PopUpMoveDialog((char)wParam);\r
5660         } else if((char)wParam == 003) CopyGameToClipboard();\r
5661          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5662     }\r
5663 \r
5664     break;\r
5665 \r
5666   case WM_PALETTECHANGED:\r
5667     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5668       int nnew;\r
5669       HDC hdc = GetDC(hwndMain);\r
5670       SelectPalette(hdc, hPal, TRUE);\r
5671       nnew = RealizePalette(hdc);\r
5672       if (nnew > 0) {\r
5673         paletteChanged = TRUE;\r
5674 #if 0\r
5675         UpdateColors(hdc);\r
5676 #else\r
5677         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5678 #endif\r
5679       }\r
5680       ReleaseDC(hwnd, hdc);\r
5681     }\r
5682     break;\r
5683 \r
5684   case WM_QUERYNEWPALETTE:\r
5685     if (!appData.monoMode /*&& paletteChanged*/) {\r
5686       int nnew;\r
5687       HDC hdc = GetDC(hwndMain);\r
5688       paletteChanged = FALSE;\r
5689       SelectPalette(hdc, hPal, FALSE);\r
5690       nnew = RealizePalette(hdc);\r
5691       if (nnew > 0) {\r
5692         InvalidateRect(hwnd, &boardRect, FALSE);\r
5693       }\r
5694       ReleaseDC(hwnd, hdc);\r
5695       return TRUE;\r
5696     }\r
5697     return FALSE;\r
5698 \r
5699   case WM_COMMAND: /* message: command from application menu */\r
5700     wmId    = LOWORD(wParam);\r
5701     wmEvent = HIWORD(wParam);\r
5702 \r
5703     switch (wmId) {\r
5704     case IDM_NewGame:\r
5705       ResetGameEvent();\r
5706       AnalysisPopDown();\r
5707       SAY("new game enter a move to play against the computer with white");\r
5708       break;\r
5709 \r
5710     case IDM_NewGameFRC:\r
5711       if( NewGameFRC() == 0 ) {\r
5712         ResetGameEvent();\r
5713         AnalysisPopDown();\r
5714       }\r
5715       break;\r
5716 \r
5717     case IDM_NewVariant:\r
5718       NewVariantPopup(hwnd);\r
5719       break;\r
5720 \r
5721     case IDM_LoadGame:\r
5722       LoadGameDialog(hwnd, "Load Game from File");\r
5723       break;\r
5724 \r
5725     case IDM_LoadNextGame:\r
5726       ReloadGame(1);\r
5727       break;\r
5728 \r
5729     case IDM_LoadPrevGame:\r
5730       ReloadGame(-1);\r
5731       break;\r
5732 \r
5733     case IDM_ReloadGame:\r
5734       ReloadGame(0);\r
5735       break;\r
5736 \r
5737     case IDM_LoadPosition:\r
5738       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5739         Reset(FALSE, TRUE);\r
5740       }\r
5741       number = 1;\r
5742       f = OpenFileDialog(hwnd, "rb", "",\r
5743                          appData.oldSaveStyle ? "pos" : "fen",\r
5744                          POSITION_FILT,\r
5745                          "Load Position from File", &number, fileTitle, NULL);\r
5746       if (f != NULL) {\r
5747         LoadPosition(f, number, fileTitle);\r
5748       }\r
5749       break;\r
5750 \r
5751     case IDM_LoadNextPosition:\r
5752       ReloadPosition(1);\r
5753       break;\r
5754 \r
5755     case IDM_LoadPrevPosition:\r
5756       ReloadPosition(-1);\r
5757       break;\r
5758 \r
5759     case IDM_ReloadPosition:\r
5760       ReloadPosition(0);\r
5761       break;\r
5762 \r
5763     case IDM_SaveGame:\r
5764       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5765       f = OpenFileDialog(hwnd, "a", defName,\r
5766                          appData.oldSaveStyle ? "gam" : "pgn",\r
5767                          GAME_FILT,\r
5768                          "Save Game to File", NULL, fileTitle, NULL);\r
5769       if (f != NULL) {\r
5770         SaveGame(f, 0, "");\r
5771       }\r
5772       break;\r
5773 \r
5774     case IDM_SavePosition:\r
5775       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5776       f = OpenFileDialog(hwnd, "a", defName,\r
5777                          appData.oldSaveStyle ? "pos" : "fen",\r
5778                          POSITION_FILT,\r
5779                          "Save Position to File", NULL, fileTitle, NULL);\r
5780       if (f != NULL) {\r
5781         SavePosition(f, 0, "");\r
5782       }\r
5783       break;\r
5784 \r
5785     case IDM_SaveDiagram:\r
5786       defName = "diagram";\r
5787       f = OpenFileDialog(hwnd, "wb", defName,\r
5788                          "bmp",\r
5789                          DIAGRAM_FILT,\r
5790                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5791       if (f != NULL) {\r
5792         SaveDiagram(f);\r
5793       }\r
5794       break;\r
5795 \r
5796     case IDM_CopyGame:\r
5797       CopyGameToClipboard();\r
5798       break;\r
5799 \r
5800     case IDM_PasteGame:\r
5801       PasteGameFromClipboard();\r
5802       break;\r
5803 \r
5804     case IDM_CopyGameListToClipboard:\r
5805       CopyGameListToClipboard();\r
5806       break;\r
5807 \r
5808     /* [AS] Autodetect FEN or PGN data */\r
5809     case IDM_PasteAny:\r
5810       PasteGameOrFENFromClipboard();\r
5811       break;\r
5812 \r
5813     /* [AS] Move history */\r
5814     case IDM_ShowMoveHistory:\r
5815         if( MoveHistoryIsUp() ) {\r
5816             MoveHistoryPopDown();\r
5817         }\r
5818         else {\r
5819             MoveHistoryPopUp();\r
5820         }\r
5821         break;\r
5822 \r
5823     /* [AS] Eval graph */\r
5824     case IDM_ShowEvalGraph:\r
5825         if( EvalGraphIsUp() ) {\r
5826             EvalGraphPopDown();\r
5827         }\r
5828         else {\r
5829             EvalGraphPopUp();\r
5830             SetFocus(hwndMain);\r
5831         }\r
5832         break;\r
5833 \r
5834     /* [AS] Engine output */\r
5835     case IDM_ShowEngineOutput:\r
5836         if( EngineOutputIsUp() ) {\r
5837             EngineOutputPopDown();\r
5838         }\r
5839         else {\r
5840             EngineOutputPopUp();\r
5841         }\r
5842         break;\r
5843 \r
5844     /* [AS] User adjudication */\r
5845     case IDM_UserAdjudication_White:\r
5846         UserAdjudicationEvent( +1 );\r
5847         break;\r
5848 \r
5849     case IDM_UserAdjudication_Black:\r
5850         UserAdjudicationEvent( -1 );\r
5851         break;\r
5852 \r
5853     case IDM_UserAdjudication_Draw:\r
5854         UserAdjudicationEvent( 0 );\r
5855         break;\r
5856 \r
5857     /* [AS] Game list options dialog */\r
5858     case IDM_GameListOptions:\r
5859       GameListOptions();\r
5860       break;\r
5861 \r
5862     case IDM_CopyPosition:\r
5863       CopyFENToClipboard();\r
5864       break;\r
5865 \r
5866     case IDM_PastePosition:\r
5867       PasteFENFromClipboard();\r
5868       break;\r
5869 \r
5870     case IDM_MailMove:\r
5871       MailMoveEvent();\r
5872       break;\r
5873 \r
5874     case IDM_ReloadCMailMsg:\r
5875       Reset(TRUE, TRUE);\r
5876       ReloadCmailMsgEvent(FALSE);\r
5877       break;\r
5878 \r
5879     case IDM_Minimize:\r
5880       ShowWindow(hwnd, SW_MINIMIZE);\r
5881       break;\r
5882 \r
5883     case IDM_Exit:\r
5884       ExitEvent(0);\r
5885       break;\r
5886 \r
5887     case IDM_MachineWhite:\r
5888       MachineWhiteEvent();\r
5889       /*\r
5890        * refresh the tags dialog only if it's visible\r
5891        */\r
5892       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5893           char *tags;\r
5894           tags = PGNTags(&gameInfo);\r
5895           TagsPopUp(tags, CmailMsg());\r
5896           free(tags);\r
5897       }\r
5898       SAY("computer starts playing white");\r
5899       break;\r
5900 \r
5901     case IDM_MachineBlack:\r
5902       MachineBlackEvent();\r
5903       /*\r
5904        * refresh the tags dialog only if it's visible\r
5905        */\r
5906       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5907           char *tags;\r
5908           tags = PGNTags(&gameInfo);\r
5909           TagsPopUp(tags, CmailMsg());\r
5910           free(tags);\r
5911       }\r
5912       SAY("computer starts playing black");\r
5913       break;\r
5914 \r
5915     case IDM_TwoMachines:\r
5916       TwoMachinesEvent();\r
5917       /*\r
5918        * refresh the tags dialog only if it's visible\r
5919        */\r
5920       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5921           char *tags;\r
5922           tags = PGNTags(&gameInfo);\r
5923           TagsPopUp(tags, CmailMsg());\r
5924           free(tags);\r
5925       }\r
5926       SAY("programs start playing each other");\r
5927       break;\r
5928 \r
5929     case IDM_AnalysisMode:\r
5930       if (!first.analysisSupport) {\r
5931         sprintf(buf, "%s does not support analysis", first.tidy);\r
5932         DisplayError(buf, 0);\r
5933       } else {\r
5934         SAY("analyzing current position");\r
5935         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5936         if (appData.icsActive) {\r
5937                if (gameMode != IcsObserving) {\r
5938                        sprintf(buf, "You are not observing a game");\r
5939                        DisplayError(buf, 0);\r
5940                        /* secure check */\r
5941                        if (appData.icsEngineAnalyze) {\r
5942                                if (appData.debugMode) \r
5943                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5944                                ExitAnalyzeMode();\r
5945                                ModeHighlight();\r
5946                                break;\r
5947                        }\r
5948                        break;\r
5949                } else {\r
5950                        /* if enable, user want disable icsEngineAnalyze */\r
5951                        if (appData.icsEngineAnalyze) {\r
5952                                ExitAnalyzeMode();\r
5953                                ModeHighlight();\r
5954                                break;\r
5955                        }\r
5956                        appData.icsEngineAnalyze = TRUE;\r
5957                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5958                }\r
5959         } \r
5960         if (!appData.showThinking) ToggleShowThinking();\r
5961         AnalyzeModeEvent();\r
5962       }\r
5963       break;\r
5964 \r
5965     case IDM_AnalyzeFile:\r
5966       if (!first.analysisSupport) {\r
5967         char buf[MSG_SIZ];\r
5968         sprintf(buf, "%s does not support analysis", first.tidy);\r
5969         DisplayError(buf, 0);\r
5970       } else {\r
5971         if (!appData.showThinking) ToggleShowThinking();\r
5972         AnalyzeFileEvent();\r
5973         LoadGameDialog(hwnd, "Analyze Game from File");\r
5974         AnalysisPeriodicEvent(1);\r
5975       }\r
5976       break;\r
5977 \r
5978     case IDM_IcsClient:\r
5979       IcsClientEvent();\r
5980       break;\r
5981 \r
5982     case IDM_EditGame:\r
5983       EditGameEvent();\r
5984       SAY("edit game");\r
5985       break;\r
5986 \r
5987     case IDM_EditPosition:\r
5988       EditPositionEvent();\r
5989       SAY("to set up a position type a FEN");\r
5990       break;\r
5991 \r
5992     case IDM_Training:\r
5993       TrainingEvent();\r
5994       break;\r
5995 \r
5996     case IDM_ShowGameList:\r
5997       ShowGameListProc();\r
5998       break;\r
5999 \r
6000     case IDM_EditTags:\r
6001       EditTagsProc();\r
6002       break;\r
6003 \r
6004     case IDM_EditComment:\r
6005       if (commentDialogUp && editComment) {\r
6006         CommentPopDown();\r
6007       } else {\r
6008         EditCommentEvent();\r
6009       }\r
6010       break;\r
6011 \r
6012     case IDM_Pause:\r
6013       PauseEvent();\r
6014       break;\r
6015 \r
6016     case IDM_Accept:\r
6017       AcceptEvent();\r
6018       break;\r
6019 \r
6020     case IDM_Decline:\r
6021       DeclineEvent();\r
6022       break;\r
6023 \r
6024     case IDM_Rematch:\r
6025       RematchEvent();\r
6026       break;\r
6027 \r
6028     case IDM_CallFlag:\r
6029       CallFlagEvent();\r
6030       break;\r
6031 \r
6032     case IDM_Draw:\r
6033       DrawEvent();\r
6034       break;\r
6035 \r
6036     case IDM_Adjourn:\r
6037       AdjournEvent();\r
6038       break;\r
6039 \r
6040     case IDM_Abort:\r
6041       AbortEvent();\r
6042       break;\r
6043 \r
6044     case IDM_Resign:\r
6045       ResignEvent();\r
6046       break;\r
6047 \r
6048     case IDM_StopObserving:\r
6049       StopObservingEvent();\r
6050       break;\r
6051 \r
6052     case IDM_StopExamining:\r
6053       StopExaminingEvent();\r
6054       break;\r
6055 \r
6056     case IDM_TypeInMove:\r
6057       PopUpMoveDialog('\000');\r
6058       break;\r
6059 \r
6060     case IDM_TypeInName:\r
6061       PopUpNameDialog('\000');\r
6062       break;\r
6063 \r
6064     case IDM_Backward:\r
6065       BackwardEvent();\r
6066       SetFocus(hwndMain);\r
6067       break;\r
6068 \r
6069     JAWS_MENU_ITEMS\r
6070 \r
6071     case IDM_Forward:\r
6072       ForwardEvent();\r
6073       SetFocus(hwndMain);\r
6074       break;\r
6075 \r
6076     case IDM_ToStart:\r
6077       ToStartEvent();\r
6078       SetFocus(hwndMain);\r
6079       break;\r
6080 \r
6081     case IDM_ToEnd:\r
6082       ToEndEvent();\r
6083       SetFocus(hwndMain);\r
6084       break;\r
6085 \r
6086     case IDM_Revert:\r
6087       RevertEvent();\r
6088       break;\r
6089 \r
6090     case IDM_TruncateGame:\r
6091       TruncateGameEvent();\r
6092       break;\r
6093 \r
6094     case IDM_MoveNow:\r
6095       MoveNowEvent();\r
6096       break;\r
6097 \r
6098     case IDM_RetractMove:\r
6099       RetractMoveEvent();\r
6100       break;\r
6101 \r
6102     case IDM_FlipView:\r
6103       flipView = !flipView;\r
6104       DrawPosition(FALSE, NULL);\r
6105       break;\r
6106 \r
6107     case IDM_FlipClock:\r
6108       flipClock = !flipClock;\r
6109       DisplayBothClocks();\r
6110       DrawPosition(FALSE, NULL);\r
6111       break;\r
6112 \r
6113     case IDM_GeneralOptions:\r
6114       GeneralOptionsPopup(hwnd);\r
6115       DrawPosition(TRUE, NULL);\r
6116       break;\r
6117 \r
6118     case IDM_BoardOptions:\r
6119       BoardOptionsPopup(hwnd);\r
6120       break;\r
6121 \r
6122     case IDM_EnginePlayOptions:\r
6123       EnginePlayOptionsPopup(hwnd);\r
6124       break;\r
6125 \r
6126     case IDM_OptionsUCI:\r
6127       UciOptionsPopup(hwnd);\r
6128       break;\r
6129 \r
6130     case IDM_IcsOptions:\r
6131       IcsOptionsPopup(hwnd);\r
6132       break;\r
6133 \r
6134     case IDM_Fonts:\r
6135       FontsOptionsPopup(hwnd);\r
6136       break;\r
6137 \r
6138     case IDM_Sounds:\r
6139       SoundOptionsPopup(hwnd);\r
6140       break;\r
6141 \r
6142     case IDM_CommPort:\r
6143       CommPortOptionsPopup(hwnd);\r
6144       break;\r
6145 \r
6146     case IDM_LoadOptions:\r
6147       LoadOptionsPopup(hwnd);\r
6148       break;\r
6149 \r
6150     case IDM_SaveOptions:\r
6151       SaveOptionsPopup(hwnd);\r
6152       break;\r
6153 \r
6154     case IDM_TimeControl:\r
6155       TimeControlOptionsPopup(hwnd);\r
6156       break;\r
6157 \r
6158     case IDM_SaveSettings:\r
6159       SaveSettings(settingsFileName);\r
6160       break;\r
6161 \r
6162     case IDM_SaveSettingsOnExit:\r
6163       saveSettingsOnExit = !saveSettingsOnExit;\r
6164       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6165                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6166                                          MF_CHECKED : MF_UNCHECKED));\r
6167       break;\r
6168 \r
6169     case IDM_Hint:\r
6170       HintEvent();\r
6171       break;\r
6172 \r
6173     case IDM_Book:\r
6174       BookEvent();\r
6175       break;\r
6176 \r
6177     case IDM_AboutGame:\r
6178       AboutGameEvent();\r
6179       break;\r
6180 \r
6181     case IDM_Debug:\r
6182       appData.debugMode = !appData.debugMode;\r
6183       if (appData.debugMode) {\r
6184         char dir[MSG_SIZ];\r
6185         GetCurrentDirectory(MSG_SIZ, dir);\r
6186         SetCurrentDirectory(installDir);\r
6187         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6188         SetCurrentDirectory(dir);\r
6189         setbuf(debugFP, NULL);\r
6190       } else {\r
6191         fclose(debugFP);\r
6192         debugFP = NULL;\r
6193       }\r
6194       break;\r
6195 \r
6196     case IDM_HELPCONTENTS:\r
6197       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6198           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6199           MessageBox (GetFocus(),\r
6200                     "Unable to activate help",\r
6201                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6202       }\r
6203       break;\r
6204 \r
6205     case IDM_HELPSEARCH:\r
6206         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6207             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6208         MessageBox (GetFocus(),\r
6209                     "Unable to activate help",\r
6210                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6211       }\r
6212       break;\r
6213 \r
6214     case IDM_HELPHELP:\r
6215       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6216         MessageBox (GetFocus(),\r
6217                     "Unable to activate help",\r
6218                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6219       }\r
6220       break;\r
6221 \r
6222     case IDM_ABOUT:\r
6223       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6224       DialogBox(hInst, \r
6225         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6226         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6227       FreeProcInstance(lpProc);\r
6228       break;\r
6229 \r
6230     case IDM_DirectCommand1:\r
6231       AskQuestionEvent("Direct Command",\r
6232                        "Send to chess program:", "", "1");\r
6233       break;\r
6234     case IDM_DirectCommand2:\r
6235       AskQuestionEvent("Direct Command",\r
6236                        "Send to second chess program:", "", "2");\r
6237       break;\r
6238 \r
6239     case EP_WhitePawn:\r
6240       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6241       fromX = fromY = -1;\r
6242       break;\r
6243 \r
6244     case EP_WhiteKnight:\r
6245       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6246       fromX = fromY = -1;\r
6247       break;\r
6248 \r
6249     case EP_WhiteBishop:\r
6250       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6251       fromX = fromY = -1;\r
6252       break;\r
6253 \r
6254     case EP_WhiteRook:\r
6255       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6256       fromX = fromY = -1;\r
6257       break;\r
6258 \r
6259     case EP_WhiteQueen:\r
6260       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6261       fromX = fromY = -1;\r
6262       break;\r
6263 \r
6264     case EP_WhiteFerz:\r
6265       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6266       fromX = fromY = -1;\r
6267       break;\r
6268 \r
6269     case EP_WhiteWazir:\r
6270       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6271       fromX = fromY = -1;\r
6272       break;\r
6273 \r
6274     case EP_WhiteAlfil:\r
6275       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6276       fromX = fromY = -1;\r
6277       break;\r
6278 \r
6279     case EP_WhiteCannon:\r
6280       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6281       fromX = fromY = -1;\r
6282       break;\r
6283 \r
6284     case EP_WhiteCardinal:\r
6285       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6286       fromX = fromY = -1;\r
6287       break;\r
6288 \r
6289     case EP_WhiteMarshall:\r
6290       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6291       fromX = fromY = -1;\r
6292       break;\r
6293 \r
6294     case EP_WhiteKing:\r
6295       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6296       fromX = fromY = -1;\r
6297       break;\r
6298 \r
6299     case EP_BlackPawn:\r
6300       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6301       fromX = fromY = -1;\r
6302       break;\r
6303 \r
6304     case EP_BlackKnight:\r
6305       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6306       fromX = fromY = -1;\r
6307       break;\r
6308 \r
6309     case EP_BlackBishop:\r
6310       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6311       fromX = fromY = -1;\r
6312       break;\r
6313 \r
6314     case EP_BlackRook:\r
6315       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6316       fromX = fromY = -1;\r
6317       break;\r
6318 \r
6319     case EP_BlackQueen:\r
6320       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6321       fromX = fromY = -1;\r
6322       break;\r
6323 \r
6324     case EP_BlackFerz:\r
6325       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6326       fromX = fromY = -1;\r
6327       break;\r
6328 \r
6329     case EP_BlackWazir:\r
6330       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6331       fromX = fromY = -1;\r
6332       break;\r
6333 \r
6334     case EP_BlackAlfil:\r
6335       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6336       fromX = fromY = -1;\r
6337       break;\r
6338 \r
6339     case EP_BlackCannon:\r
6340       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6341       fromX = fromY = -1;\r
6342       break;\r
6343 \r
6344     case EP_BlackCardinal:\r
6345       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6346       fromX = fromY = -1;\r
6347       break;\r
6348 \r
6349     case EP_BlackMarshall:\r
6350       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6351       fromX = fromY = -1;\r
6352       break;\r
6353 \r
6354     case EP_BlackKing:\r
6355       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6356       fromX = fromY = -1;\r
6357       break;\r
6358 \r
6359     case EP_EmptySquare:\r
6360       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6361       fromX = fromY = -1;\r
6362       break;\r
6363 \r
6364     case EP_ClearBoard:\r
6365       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6366       fromX = fromY = -1;\r
6367       break;\r
6368 \r
6369     case EP_White:\r
6370       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6371       fromX = fromY = -1;\r
6372       break;\r
6373 \r
6374     case EP_Black:\r
6375       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6376       fromX = fromY = -1;\r
6377       break;\r
6378 \r
6379     case EP_Promote:\r
6380       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6381       fromX = fromY = -1;\r
6382       break;\r
6383 \r
6384     case EP_Demote:\r
6385       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6386       fromX = fromY = -1;\r
6387       break;\r
6388 \r
6389     case DP_Pawn:\r
6390       DropMenuEvent(WhitePawn, fromX, fromY);\r
6391       fromX = fromY = -1;\r
6392       break;\r
6393 \r
6394     case DP_Knight:\r
6395       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6396       fromX = fromY = -1;\r
6397       break;\r
6398 \r
6399     case DP_Bishop:\r
6400       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6401       fromX = fromY = -1;\r
6402       break;\r
6403 \r
6404     case DP_Rook:\r
6405       DropMenuEvent(WhiteRook, fromX, fromY);\r
6406       fromX = fromY = -1;\r
6407       break;\r
6408 \r
6409     case DP_Queen:\r
6410       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6411       fromX = fromY = -1;\r
6412       break;\r
6413 \r
6414     default:\r
6415       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6416     }\r
6417     break;\r
6418 \r
6419   case WM_TIMER:\r
6420     switch (wParam) {\r
6421     case CLOCK_TIMER_ID:\r
6422       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6423       clockTimerEvent = 0;\r
6424       DecrementClocks(); /* call into back end */\r
6425       break;\r
6426     case LOAD_GAME_TIMER_ID:\r
6427       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6428       loadGameTimerEvent = 0;\r
6429       AutoPlayGameLoop(); /* call into back end */\r
6430       break;\r
6431     case ANALYSIS_TIMER_ID:\r
6432       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6433                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6434         AnalysisPeriodicEvent(0);\r
6435       } else {\r
6436         KillTimer(hwnd, analysisTimerEvent);\r
6437         analysisTimerEvent = 0;\r
6438       }\r
6439       break;\r
6440     case DELAYED_TIMER_ID:\r
6441       KillTimer(hwnd, delayedTimerEvent);\r
6442       delayedTimerEvent = 0;\r
6443       delayedTimerCallback();\r
6444       break;\r
6445     }\r
6446     break;\r
6447 \r
6448   case WM_USER_Input:\r
6449     InputEvent(hwnd, message, wParam, lParam);\r
6450     break;\r
6451 \r
6452   /* [AS] Also move "attached" child windows */\r
6453   case WM_WINDOWPOSCHANGING:\r
6454 \r
6455     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6456         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6457 \r
6458         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6459             /* Window is moving */\r
6460             RECT rcMain;\r
6461 \r
6462 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6463             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6464             rcMain.right  = boardX + winWidth;\r
6465             rcMain.top    = boardY;\r
6466             rcMain.bottom = boardY + winHeight;\r
6467             \r
6468             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6469             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6470             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6471             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6472             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6473             boardX = lpwp->x;\r
6474             boardY = lpwp->y;\r
6475         }\r
6476     }\r
6477     break;\r
6478 \r
6479   /* [AS] Snapping */\r
6480   case WM_ENTERSIZEMOVE:\r
6481     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6482     if (hwnd == hwndMain) {\r
6483       doingSizing = TRUE;\r
6484       lastSizing = 0;\r
6485     }\r
6486     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6487     break;\r
6488 \r
6489   case WM_SIZING:\r
6490     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6491     if (hwnd == hwndMain) {\r
6492       lastSizing = wParam;\r
6493     }\r
6494     break;\r
6495 \r
6496   case WM_MOVING:\r
6497     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6498       return OnMoving( &sd, hwnd, wParam, lParam );\r
6499 \r
6500   case WM_EXITSIZEMOVE:\r
6501     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6502     if (hwnd == hwndMain) {\r
6503       RECT client;\r
6504       doingSizing = FALSE;\r
6505       InvalidateRect(hwnd, &boardRect, FALSE);\r
6506       GetClientRect(hwnd, &client);\r
6507       ResizeBoard(client.right, client.bottom, lastSizing);\r
6508       lastSizing = 0;\r
6509       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6510     }\r
6511     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6512     break;\r
6513 \r
6514   case WM_DESTROY: /* message: window being destroyed */\r
6515     PostQuitMessage(0);\r
6516     break;\r
6517 \r
6518   case WM_CLOSE:\r
6519     if (hwnd == hwndMain) {\r
6520       ExitEvent(0);\r
6521     }\r
6522     break;\r
6523 \r
6524   default:      /* Passes it on if unprocessed */\r
6525     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6526   }\r
6527   return 0;\r
6528 }\r
6529 \r
6530 /*---------------------------------------------------------------------------*\\r
6531  *\r
6532  * Misc utility routines\r
6533  *\r
6534 \*---------------------------------------------------------------------------*/\r
6535 \r
6536 /*\r
6537  * Decent random number generator, at least not as bad as Windows\r
6538  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6539  */\r
6540 unsigned int randstate;\r
6541 \r
6542 int\r
6543 myrandom(void)\r
6544 {\r
6545   randstate = randstate * 1664525 + 1013904223;\r
6546   return (int) randstate & 0x7fffffff;\r
6547 }\r
6548 \r
6549 void\r
6550 mysrandom(unsigned int seed)\r
6551 {\r
6552   randstate = seed;\r
6553 }\r
6554 \r
6555 \r
6556 /* \r
6557  * returns TRUE if user selects a different color, FALSE otherwise \r
6558  */\r
6559 \r
6560 BOOL\r
6561 ChangeColor(HWND hwnd, COLORREF *which)\r
6562 {\r
6563   static BOOL firstTime = TRUE;\r
6564   static DWORD customColors[16];\r
6565   CHOOSECOLOR cc;\r
6566   COLORREF newcolor;\r
6567   int i;\r
6568   ColorClass ccl;\r
6569 \r
6570   if (firstTime) {\r
6571     /* Make initial colors in use available as custom colors */\r
6572     /* Should we put the compiled-in defaults here instead? */\r
6573     i = 0;\r
6574     customColors[i++] = lightSquareColor & 0xffffff;\r
6575     customColors[i++] = darkSquareColor & 0xffffff;\r
6576     customColors[i++] = whitePieceColor & 0xffffff;\r
6577     customColors[i++] = blackPieceColor & 0xffffff;\r
6578     customColors[i++] = highlightSquareColor & 0xffffff;\r
6579     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6580 \r
6581     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6582       customColors[i++] = textAttribs[ccl].color;\r
6583     }\r
6584     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6585     firstTime = FALSE;\r
6586   }\r
6587 \r
6588   cc.lStructSize = sizeof(cc);\r
6589   cc.hwndOwner = hwnd;\r
6590   cc.hInstance = NULL;\r
6591   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6592   cc.lpCustColors = (LPDWORD) customColors;\r
6593   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6594 \r
6595   if (!ChooseColor(&cc)) return FALSE;\r
6596 \r
6597   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6598   if (newcolor == *which) return FALSE;\r
6599   *which = newcolor;\r
6600   return TRUE;\r
6601 \r
6602   /*\r
6603   InitDrawingColors();\r
6604   InvalidateRect(hwnd, &boardRect, FALSE);\r
6605   */\r
6606 }\r
6607 \r
6608 BOOLEAN\r
6609 MyLoadSound(MySound *ms)\r
6610 {\r
6611   BOOL ok = FALSE;\r
6612   struct stat st;\r
6613   FILE *f;\r
6614 \r
6615   if (ms->data) free(ms->data);\r
6616   ms->data = NULL;\r
6617 \r
6618   switch (ms->name[0]) {\r
6619   case NULLCHAR:\r
6620     /* Silence */\r
6621     ok = TRUE;\r
6622     break;\r
6623   case '$':\r
6624     /* System sound from Control Panel.  Don't preload here. */\r
6625     ok = TRUE;\r
6626     break;\r
6627   case '!':\r
6628     if (ms->name[1] == NULLCHAR) {\r
6629       /* "!" alone = silence */\r
6630       ok = TRUE;\r
6631     } else {\r
6632       /* Builtin wave resource.  Error if not found. */\r
6633       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6634       if (h == NULL) break;\r
6635       ms->data = (void *)LoadResource(hInst, h);\r
6636       if (h == NULL) break;\r
6637       ok = TRUE;\r
6638     }\r
6639     break;\r
6640   default:\r
6641     /* .wav file.  Error if not found. */\r
6642     f = fopen(ms->name, "rb");\r
6643     if (f == NULL) break;\r
6644     if (fstat(fileno(f), &st) < 0) break;\r
6645     ms->data = malloc(st.st_size);\r
6646     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6647     fclose(f);\r
6648     ok = TRUE;\r
6649     break;\r
6650   }\r
6651   if (!ok) {\r
6652     char buf[MSG_SIZ];\r
6653     sprintf(buf, "Error loading sound %s", ms->name);\r
6654     DisplayError(buf, GetLastError());\r
6655   }\r
6656   return ok;\r
6657 }\r
6658 \r
6659 BOOLEAN\r
6660 MyPlaySound(MySound *ms)\r
6661 {\r
6662   BOOLEAN ok = FALSE;\r
6663         if(appData.debugMode) fprintf(debugFP, "make sound %s %x %d\n", ms->name, ms, ms->name[0]);\r
6664   switch (ms->name[0]) {\r
6665   case NULLCHAR:\r
6666         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6667     /* Silence */\r
6668     ok = TRUE;\r
6669     break;\r
6670   case '$':\r
6671     /* System sound from Control Panel (deprecated feature).\r
6672        "$" alone or an unset sound name gets default beep (still in use). */\r
6673     if (ms->name[1]) {\r
6674       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6675     }\r
6676     if (!ok) ok = MessageBeep(MB_OK);\r
6677     break; \r
6678   case '!':\r
6679     /* Builtin wave resource, or "!" alone for silence */\r
6680     if (ms->name[1]) {\r
6681       if (ms->data == NULL) return FALSE;\r
6682       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6683     } else {\r
6684       ok = TRUE;\r
6685     }\r
6686     break;\r
6687   default:\r
6688     /* .wav file.  Error if not found. */\r
6689     if (ms->data == NULL) return FALSE;\r
6690     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6691     break;\r
6692   }\r
6693   /* Don't print an error: this can happen innocently if the sound driver\r
6694      is busy; for instance, if another instance of WinBoard is playing\r
6695      a sound at about the same time. */\r
6696 #if 0\r
6697   if (!ok) {\r
6698     char buf[MSG_SIZ];\r
6699     sprintf(buf, "Error playing sound %s", ms->name);\r
6700     DisplayError(buf, GetLastError());\r
6701   }\r
6702 #endif\r
6703   return ok;\r
6704 }\r
6705 \r
6706 \r
6707 LRESULT CALLBACK\r
6708 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6709 {\r
6710   BOOL ok;\r
6711   OPENFILENAME *ofn;\r
6712   static UINT *number; /* gross that this is static */\r
6713 \r
6714   switch (message) {\r
6715   case WM_INITDIALOG: /* message: initialize dialog box */\r
6716     /* Center the dialog over the application window */\r
6717     ofn = (OPENFILENAME *) lParam;\r
6718     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6719       number = (UINT *) ofn->lCustData;\r
6720       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6721     } else {\r
6722       number = NULL;\r
6723     }\r
6724     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6725     return FALSE;  /* Allow for further processing */\r
6726 \r
6727   case WM_COMMAND:\r
6728     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6729       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6730     }\r
6731     return FALSE;  /* Allow for further processing */\r
6732   }\r
6733   return FALSE;\r
6734 }\r
6735 \r
6736 UINT APIENTRY\r
6737 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6738 {\r
6739   static UINT *number;\r
6740   OPENFILENAME *ofname;\r
6741   OFNOTIFY *ofnot;\r
6742   switch (uiMsg) {\r
6743   case WM_INITDIALOG:\r
6744     ofname = (OPENFILENAME *)lParam;\r
6745     number = (UINT *)(ofname->lCustData);\r
6746     break;\r
6747   case WM_NOTIFY:\r
6748     ofnot = (OFNOTIFY *)lParam;\r
6749     if (ofnot->hdr.code == CDN_FILEOK) {\r
6750       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6751     }\r
6752     break;\r
6753   }\r
6754   return 0;\r
6755 }\r
6756 \r
6757 \r
6758 FILE *\r
6759 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6760                char *nameFilt, char *dlgTitle, UINT *number,\r
6761                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6762 {\r
6763   OPENFILENAME openFileName;\r
6764   char buf1[MSG_SIZ];\r
6765   FILE *f;\r
6766 \r
6767   if (fileName == NULL) fileName = buf1;\r
6768   if (defName == NULL) {\r
6769     strcpy(fileName, "*.");\r
6770     strcat(fileName, defExt);\r
6771   } else {\r
6772     strcpy(fileName, defName);\r
6773   }\r
6774   if (fileTitle) strcpy(fileTitle, "");\r
6775   if (number) *number = 0;\r
6776 \r
6777   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6778   openFileName.hwndOwner         = hwnd;\r
6779   openFileName.hInstance         = (HANDLE) hInst;\r
6780   openFileName.lpstrFilter       = nameFilt;\r
6781   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6782   openFileName.nMaxCustFilter    = 0L;\r
6783   openFileName.nFilterIndex      = 1L;\r
6784   openFileName.lpstrFile         = fileName;\r
6785   openFileName.nMaxFile          = MSG_SIZ;\r
6786   openFileName.lpstrFileTitle    = fileTitle;\r
6787   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6788   openFileName.lpstrInitialDir   = NULL;\r
6789   openFileName.lpstrTitle        = dlgTitle;\r
6790   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6791     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6792     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6793     | (oldDialog ? 0 : OFN_EXPLORER);\r
6794   openFileName.nFileOffset       = 0;\r
6795   openFileName.nFileExtension    = 0;\r
6796   openFileName.lpstrDefExt       = defExt;\r
6797   openFileName.lCustData         = (LONG) number;\r
6798   openFileName.lpfnHook          = oldDialog ?\r
6799     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6800   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6801 \r
6802   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6803                         GetOpenFileName(&openFileName)) {\r
6804     /* open the file */\r
6805     f = fopen(openFileName.lpstrFile, write);\r
6806     if (f == NULL) {\r
6807       MessageBox(hwnd, "File open failed", NULL,\r
6808                  MB_OK|MB_ICONEXCLAMATION);\r
6809       return NULL;\r
6810     }\r
6811   } else {\r
6812     int err = CommDlgExtendedError();\r
6813     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6814     return FALSE;\r
6815   }\r
6816   return f;\r
6817 }\r
6818 \r
6819 \r
6820 \r
6821 VOID APIENTRY\r
6822 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6823 {\r
6824   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6825 \r
6826   /*\r
6827    * Get the first pop-up menu in the menu template. This is the\r
6828    * menu that TrackPopupMenu displays.\r
6829    */\r
6830   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6831 \r
6832   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6833 \r
6834   /*\r
6835    * TrackPopup uses screen coordinates, so convert the\r
6836    * coordinates of the mouse click to screen coordinates.\r
6837    */\r
6838   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6839 \r
6840   /* Draw and track the floating pop-up menu. */\r
6841   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6842                  pt.x, pt.y, 0, hwnd, NULL);\r
6843 \r
6844   /* Destroy the menu.*/\r
6845   DestroyMenu(hmenu);\r
6846 }\r
6847    \r
6848 typedef struct {\r
6849   HWND hDlg, hText;\r
6850   int sizeX, sizeY, newSizeX, newSizeY;\r
6851   HDWP hdwp;\r
6852 } ResizeEditPlusButtonsClosure;\r
6853 \r
6854 BOOL CALLBACK\r
6855 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6856 {\r
6857   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6858   RECT rect;\r
6859   POINT pt;\r
6860 \r
6861   if (hChild == cl->hText) return TRUE;\r
6862   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6863   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6864   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6865   ScreenToClient(cl->hDlg, &pt);\r
6866   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6867     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6868   return TRUE;\r
6869 }\r
6870 \r
6871 /* Resize a dialog that has a (rich) edit field filling most of\r
6872    the top, with a row of buttons below */\r
6873 VOID\r
6874 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6875 {\r
6876   RECT rectText;\r
6877   int newTextHeight, newTextWidth;\r
6878   ResizeEditPlusButtonsClosure cl;\r
6879   \r
6880   /*if (IsIconic(hDlg)) return;*/\r
6881   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6882   \r
6883   cl.hdwp = BeginDeferWindowPos(8);\r
6884 \r
6885   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6886   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6887   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6888   if (newTextHeight < 0) {\r
6889     newSizeY += -newTextHeight;\r
6890     newTextHeight = 0;\r
6891   }\r
6892   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6893     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6894 \r
6895   cl.hDlg = hDlg;\r
6896   cl.hText = hText;\r
6897   cl.sizeX = sizeX;\r
6898   cl.sizeY = sizeY;\r
6899   cl.newSizeX = newSizeX;\r
6900   cl.newSizeY = newSizeY;\r
6901   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6902 \r
6903   EndDeferWindowPos(cl.hdwp);\r
6904 }\r
6905 \r
6906 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6907 {\r
6908     RECT    rChild, rParent;\r
6909     int     wChild, hChild, wParent, hParent;\r
6910     int     wScreen, hScreen, xNew, yNew;\r
6911     HDC     hdc;\r
6912 \r
6913     /* Get the Height and Width of the child window */\r
6914     GetWindowRect (hwndChild, &rChild);\r
6915     wChild = rChild.right - rChild.left;\r
6916     hChild = rChild.bottom - rChild.top;\r
6917 \r
6918     /* Get the Height and Width of the parent window */\r
6919     GetWindowRect (hwndParent, &rParent);\r
6920     wParent = rParent.right - rParent.left;\r
6921     hParent = rParent.bottom - rParent.top;\r
6922 \r
6923     /* Get the display limits */\r
6924     hdc = GetDC (hwndChild);\r
6925     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6926     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6927     ReleaseDC(hwndChild, hdc);\r
6928 \r
6929     /* Calculate new X position, then adjust for screen */\r
6930     xNew = rParent.left + ((wParent - wChild) /2);\r
6931     if (xNew < 0) {\r
6932         xNew = 0;\r
6933     } else if ((xNew+wChild) > wScreen) {\r
6934         xNew = wScreen - wChild;\r
6935     }\r
6936 \r
6937     /* Calculate new Y position, then adjust for screen */\r
6938     if( mode == 0 ) {\r
6939         yNew = rParent.top  + ((hParent - hChild) /2);\r
6940     }\r
6941     else {\r
6942         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6943     }\r
6944 \r
6945     if (yNew < 0) {\r
6946         yNew = 0;\r
6947     } else if ((yNew+hChild) > hScreen) {\r
6948         yNew = hScreen - hChild;\r
6949     }\r
6950 \r
6951     /* Set it, and return */\r
6952     return SetWindowPos (hwndChild, NULL,\r
6953                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6954 }\r
6955 \r
6956 /* Center one window over another */\r
6957 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6958 {\r
6959     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6960 }\r
6961 \r
6962 /*---------------------------------------------------------------------------*\\r
6963  *\r
6964  * Startup Dialog functions\r
6965  *\r
6966 \*---------------------------------------------------------------------------*/\r
6967 void\r
6968 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6969 {\r
6970   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6971 \r
6972   while (*cd != NULL) {\r
6973     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6974     cd++;\r
6975   }\r
6976 }\r
6977 \r
6978 void\r
6979 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6980 {\r
6981   char buf1[ARG_MAX];\r
6982   int len;\r
6983 \r
6984   if (str[0] == '@') {\r
6985     FILE* f = fopen(str + 1, "r");\r
6986     if (f == NULL) {\r
6987       DisplayFatalError(str + 1, errno, 2);\r
6988       return;\r
6989     }\r
6990     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6991     fclose(f);\r
6992     buf1[len] = NULLCHAR;\r
6993     str = buf1;\r
6994   }\r
6995 \r
6996   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6997 \r
6998   for (;;) {\r
6999     char buf[MSG_SIZ];\r
7000     char *end = strchr(str, '\n');\r
7001     if (end == NULL) return;\r
7002     memcpy(buf, str, end - str);\r
7003     buf[end - str] = NULLCHAR;\r
7004     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
7005     str = end + 1;\r
7006   }\r
7007 }\r
7008 \r
7009 void\r
7010 SetStartupDialogEnables(HWND hDlg)\r
7011 {\r
7012   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
7013     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7014     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
7015   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7016     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
7017   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
7018     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
7019   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
7020     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
7021   EnableWindow(GetDlgItem(hDlg, IDOK),\r
7022     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7023     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
7024     IsDlgButtonChecked(hDlg, OPT_View));\r
7025 }\r
7026 \r
7027 char *\r
7028 QuoteForFilename(char *filename)\r
7029 {\r
7030   int dquote, space;\r
7031   dquote = strchr(filename, '"') != NULL;\r
7032   space = strchr(filename, ' ') != NULL;\r
7033   if (dquote || space) {\r
7034     if (dquote) {\r
7035       return "'";\r
7036     } else {\r
7037       return "\"";\r
7038     }\r
7039   } else {\r
7040     return "";\r
7041   }\r
7042 }\r
7043 \r
7044 VOID\r
7045 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
7046 {\r
7047   char buf[MSG_SIZ];\r
7048   char *q;\r
7049 \r
7050   InitComboStringsFromOption(hwndCombo, nthnames);\r
7051   q = QuoteForFilename(nthcp);\r
7052   sprintf(buf, "%s%s%s", q, nthcp, q);\r
7053   if (*nthdir != NULLCHAR) {\r
7054     q = QuoteForFilename(nthdir);\r
7055     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
7056   }\r
7057   if (*nthcp == NULLCHAR) {\r
7058     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7059   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7060     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7061     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7062   }\r
7063 }\r
7064 \r
7065 LRESULT CALLBACK\r
7066 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7067 {\r
7068   char buf[MSG_SIZ];\r
7069   HANDLE hwndCombo;\r
7070   char *p;\r
7071 \r
7072   switch (message) {\r
7073   case WM_INITDIALOG:\r
7074     /* Center the dialog */\r
7075     CenterWindow (hDlg, GetDesktopWindow());\r
7076     /* Initialize the dialog items */\r
7077     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7078                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7079                   firstChessProgramNames);\r
7080     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7081                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7082                   secondChessProgramNames);\r
7083     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7084     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7085     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7086     if (*appData.icsHelper != NULLCHAR) {\r
7087       char *q = QuoteForFilename(appData.icsHelper);\r
7088       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7089     }\r
7090     if (*appData.icsHost == NULLCHAR) {\r
7091       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7092       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7093     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7094       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7095       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7096     }\r
7097 \r
7098     if (appData.icsActive) {\r
7099       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7100     }\r
7101     else if (appData.noChessProgram) {\r
7102       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7103     }\r
7104     else {\r
7105       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7106     }\r
7107 \r
7108     SetStartupDialogEnables(hDlg);\r
7109     return TRUE;\r
7110 \r
7111   case WM_COMMAND:\r
7112     switch (LOWORD(wParam)) {\r
7113     case IDOK:\r
7114       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7115         strcpy(buf, "/fcp=");\r
7116         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7117         p = buf;\r
7118         ParseArgs(StringGet, &p);\r
7119         strcpy(buf, "/scp=");\r
7120         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7121         p = buf;\r
7122         ParseArgs(StringGet, &p);\r
7123         appData.noChessProgram = FALSE;\r
7124         appData.icsActive = FALSE;\r
7125       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7126         strcpy(buf, "/ics /icshost=");\r
7127         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7128         p = buf;\r
7129         ParseArgs(StringGet, &p);\r
7130         if (appData.zippyPlay) {\r
7131           strcpy(buf, "/fcp=");\r
7132           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7133           p = buf;\r
7134           ParseArgs(StringGet, &p);\r
7135         }\r
7136       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7137         appData.noChessProgram = TRUE;\r
7138         appData.icsActive = FALSE;\r
7139       } else {\r
7140         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7141                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7142         return TRUE;\r
7143       }\r
7144       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7145         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7146         p = buf;\r
7147         ParseArgs(StringGet, &p);\r
7148       }\r
7149       EndDialog(hDlg, TRUE);\r
7150       return TRUE;\r
7151 \r
7152     case IDCANCEL:\r
7153       ExitEvent(0);\r
7154       return TRUE;\r
7155 \r
7156     case IDM_HELPCONTENTS:\r
7157       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7158         MessageBox (GetFocus(),\r
7159                     "Unable to activate help",\r
7160                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7161       }\r
7162       break;\r
7163 \r
7164     default:\r
7165       SetStartupDialogEnables(hDlg);\r
7166       break;\r
7167     }\r
7168     break;\r
7169   }\r
7170   return FALSE;\r
7171 }\r
7172 \r
7173 /*---------------------------------------------------------------------------*\\r
7174  *\r
7175  * About box dialog functions\r
7176  *\r
7177 \*---------------------------------------------------------------------------*/\r
7178 \r
7179 /* Process messages for "About" dialog box */\r
7180 LRESULT CALLBACK\r
7181 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7182 {\r
7183   switch (message) {\r
7184   case WM_INITDIALOG: /* message: initialize dialog box */\r
7185     /* Center the dialog over the application window */\r
7186     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7187     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7188     return (TRUE);\r
7189 \r
7190   case WM_COMMAND: /* message: received a command */\r
7191     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7192         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7193       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7194       return (TRUE);\r
7195     }\r
7196     break;\r
7197   }\r
7198   return (FALSE);\r
7199 }\r
7200 \r
7201 /*---------------------------------------------------------------------------*\\r
7202  *\r
7203  * Comment Dialog functions\r
7204  *\r
7205 \*---------------------------------------------------------------------------*/\r
7206 \r
7207 LRESULT CALLBACK\r
7208 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7209 {\r
7210   static HANDLE hwndText = NULL;\r
7211   int len, newSizeX, newSizeY, flags;\r
7212   static int sizeX, sizeY;\r
7213   char *str;\r
7214   RECT rect;\r
7215   MINMAXINFO *mmi;\r
7216 \r
7217   switch (message) {\r
7218   case WM_INITDIALOG: /* message: initialize dialog box */\r
7219     /* Initialize the dialog items */\r
7220     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7221     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7222     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7223     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7224     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7225     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7226     SetWindowText(hDlg, commentTitle);\r
7227     if (editComment) {\r
7228       SetFocus(hwndText);\r
7229     } else {\r
7230       SetFocus(GetDlgItem(hDlg, IDOK));\r
7231     }\r
7232     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7233                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7234                 MAKELPARAM(FALSE, 0));\r
7235     /* Size and position the dialog */\r
7236     if (!commentDialog) {\r
7237       commentDialog = hDlg;\r
7238       flags = SWP_NOZORDER;\r
7239       GetClientRect(hDlg, &rect);\r
7240       sizeX = rect.right;\r
7241       sizeY = rect.bottom;\r
7242       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7243           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7244         WINDOWPLACEMENT wp;\r
7245         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7246         wp.length = sizeof(WINDOWPLACEMENT);\r
7247         wp.flags = 0;\r
7248         wp.showCmd = SW_SHOW;\r
7249         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7250         wp.rcNormalPosition.left = commentX;\r
7251         wp.rcNormalPosition.right = commentX + commentW;\r
7252         wp.rcNormalPosition.top = commentY;\r
7253         wp.rcNormalPosition.bottom = commentY + commentH;\r
7254         SetWindowPlacement(hDlg, &wp);\r
7255 \r
7256         GetClientRect(hDlg, &rect);\r
7257         newSizeX = rect.right;\r
7258         newSizeY = rect.bottom;\r
7259         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7260                               newSizeX, newSizeY);\r
7261         sizeX = newSizeX;\r
7262         sizeY = newSizeY;\r
7263       }\r
7264     }\r
7265     return FALSE;\r
7266 \r
7267   case WM_COMMAND: /* message: received a command */\r
7268     switch (LOWORD(wParam)) {\r
7269     case IDOK:\r
7270       if (editComment) {\r
7271         char *p, *q;\r
7272         /* Read changed options from the dialog box */\r
7273         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7274         len = GetWindowTextLength(hwndText);\r
7275         str = (char *) malloc(len + 1);\r
7276         GetWindowText(hwndText, str, len + 1);\r
7277         p = q = str;\r
7278         while (*q) {\r
7279           if (*q == '\r')\r
7280             q++;\r
7281           else\r
7282             *p++ = *q++;\r
7283         }\r
7284         *p = NULLCHAR;\r
7285         ReplaceComment(commentIndex, str);\r
7286         free(str);\r
7287       }\r
7288       CommentPopDown();\r
7289       return TRUE;\r
7290 \r
7291     case IDCANCEL:\r
7292     case OPT_CancelComment:\r
7293       CommentPopDown();\r
7294       return TRUE;\r
7295 \r
7296     case OPT_ClearComment:\r
7297       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7298       break;\r
7299 \r
7300     case OPT_EditComment:\r
7301       EditCommentEvent();\r
7302       return TRUE;\r
7303 \r
7304     default:\r
7305       break;\r
7306     }\r
7307     break;\r
7308 \r
7309   case WM_SIZE:\r
7310     newSizeX = LOWORD(lParam);\r
7311     newSizeY = HIWORD(lParam);\r
7312     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7313     sizeX = newSizeX;\r
7314     sizeY = newSizeY;\r
7315     break;\r
7316 \r
7317   case WM_GETMINMAXINFO:\r
7318     /* Prevent resizing window too small */\r
7319     mmi = (MINMAXINFO *) lParam;\r
7320     mmi->ptMinTrackSize.x = 100;\r
7321     mmi->ptMinTrackSize.y = 100;\r
7322     break;\r
7323   }\r
7324   return FALSE;\r
7325 }\r
7326 \r
7327 VOID\r
7328 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7329 {\r
7330   FARPROC lpProc;\r
7331   char *p, *q;\r
7332 \r
7333   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7334 \r
7335   if (str == NULL) str = "";\r
7336   p = (char *) malloc(2 * strlen(str) + 2);\r
7337   q = p;\r
7338   while (*str) {\r
7339     if (*str == '\n') *q++ = '\r';\r
7340     *q++ = *str++;\r
7341   }\r
7342   *q = NULLCHAR;\r
7343   if (commentText != NULL) free(commentText);\r
7344 \r
7345   commentIndex = index;\r
7346   commentTitle = title;\r
7347   commentText = p;\r
7348   editComment = edit;\r
7349 \r
7350   if (commentDialog) {\r
7351     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7352     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7353   } else {\r
7354     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7355     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7356                  hwndMain, (DLGPROC)lpProc);\r
7357     FreeProcInstance(lpProc);\r
7358   }\r
7359   commentDialogUp = TRUE;\r
7360 }\r
7361 \r
7362 \r
7363 /*---------------------------------------------------------------------------*\\r
7364  *\r
7365  * Type-in move dialog functions\r
7366  * \r
7367 \*---------------------------------------------------------------------------*/\r
7368 \r
7369 LRESULT CALLBACK\r
7370 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7371 {\r
7372   char move[MSG_SIZ];\r
7373   HWND hInput;\r
7374   ChessMove moveType;\r
7375   int fromX, fromY, toX, toY;\r
7376   char promoChar;\r
7377 \r
7378   switch (message) {\r
7379   case WM_INITDIALOG:\r
7380     move[0] = (char) lParam;\r
7381     move[1] = NULLCHAR;\r
7382     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7383     hInput = GetDlgItem(hDlg, OPT_Move);\r
7384     SetWindowText(hInput, move);\r
7385     SetFocus(hInput);\r
7386     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7387     return FALSE;\r
7388 \r
7389   case WM_COMMAND:\r
7390     switch (LOWORD(wParam)) {\r
7391     case IDOK:\r
7392       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7393       { int n; Board board;\r
7394         // [HGM] FENedit\r
7395         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7396                 EditPositionPasteFEN(move);\r
7397                 EndDialog(hDlg, TRUE);\r
7398                 return TRUE;\r
7399         }\r
7400         // [HGM] movenum: allow move number to be typed in any mode\r
7401         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7402           currentMove = 2*n-1;\r
7403           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7404           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7405           EndDialog(hDlg, TRUE);\r
7406           DrawPosition(TRUE, boards[currentMove]);\r
7407           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7408           else DisplayMessage("", "");\r
7409           return TRUE;\r
7410         }\r
7411       }\r
7412       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7413         gameMode != Training) {\r
7414         DisplayMoveError("Displayed move is not current");\r
7415       } else {\r
7416 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7417         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7418           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7419         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7420         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7421           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7422           if (gameMode != Training)\r
7423               forwardMostMove = currentMove;\r
7424           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7425         } else {\r
7426           DisplayMoveError("Could not parse move");\r
7427         }\r
7428       }\r
7429       EndDialog(hDlg, TRUE);\r
7430       return TRUE;\r
7431     case IDCANCEL:\r
7432       EndDialog(hDlg, FALSE);\r
7433       return TRUE;\r
7434     default:\r
7435       break;\r
7436     }\r
7437     break;\r
7438   }\r
7439   return FALSE;\r
7440 }\r
7441 \r
7442 VOID\r
7443 PopUpMoveDialog(char firstchar)\r
7444 {\r
7445     FARPROC lpProc;\r
7446     \r
7447     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7448         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7449         gameMode == AnalyzeMode || gameMode == EditGame || \r
7450         gameMode == EditPosition || gameMode == IcsExamining ||\r
7451         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7452         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7453                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7454                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7455         gameMode == Training) {\r
7456       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7457       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7458         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7459       FreeProcInstance(lpProc);\r
7460     }\r
7461 }\r
7462 \r
7463 /*---------------------------------------------------------------------------*\\r
7464  *\r
7465  * Type-in name dialog functions\r
7466  * \r
7467 \*---------------------------------------------------------------------------*/\r
7468 \r
7469 LRESULT CALLBACK\r
7470 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7471 {\r
7472   char move[MSG_SIZ];\r
7473   HWND hInput;\r
7474 \r
7475   switch (message) {\r
7476   case WM_INITDIALOG:\r
7477     move[0] = (char) lParam;\r
7478     move[1] = NULLCHAR;\r
7479     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7480     hInput = GetDlgItem(hDlg, OPT_Name);\r
7481     SetWindowText(hInput, move);\r
7482     SetFocus(hInput);\r
7483     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7484     return FALSE;\r
7485 \r
7486   case WM_COMMAND:\r
7487     switch (LOWORD(wParam)) {\r
7488     case IDOK:\r
7489       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7490       appData.userName = strdup(move);\r
7491       SetUserLogo();\r
7492 \r
7493       EndDialog(hDlg, TRUE);\r
7494       return TRUE;\r
7495     case IDCANCEL:\r
7496       EndDialog(hDlg, FALSE);\r
7497       return TRUE;\r
7498     default:\r
7499       break;\r
7500     }\r
7501     break;\r
7502   }\r
7503   return FALSE;\r
7504 }\r
7505 \r
7506 VOID\r
7507 PopUpNameDialog(char firstchar)\r
7508 {\r
7509     FARPROC lpProc;\r
7510     \r
7511       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7512       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7513         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7514       FreeProcInstance(lpProc);\r
7515 }\r
7516 \r
7517 /*---------------------------------------------------------------------------*\\r
7518  *\r
7519  *  Error dialogs\r
7520  * \r
7521 \*---------------------------------------------------------------------------*/\r
7522 \r
7523 /* Nonmodal error box */\r
7524 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7525                              WPARAM wParam, LPARAM lParam);\r
7526 \r
7527 VOID\r
7528 ErrorPopUp(char *title, char *content)\r
7529 {\r
7530   FARPROC lpProc;\r
7531   char *p, *q;\r
7532   BOOLEAN modal = hwndMain == NULL;\r
7533 \r
7534   p = content;\r
7535   q = errorMessage;\r
7536   while (*p) {\r
7537     if (*p == '\n') {\r
7538       if (modal) {\r
7539         *q++ = ' ';\r
7540         p++;\r
7541       } else {\r
7542         *q++ = '\r';\r
7543         *q++ = *p++;\r
7544       }\r
7545     } else {\r
7546       *q++ = *p++;\r
7547     }\r
7548   }\r
7549   *q = NULLCHAR;\r
7550   strncpy(errorTitle, title, sizeof(errorTitle));\r
7551   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7552   \r
7553   if (modal) {\r
7554     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7555   } else {\r
7556     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7557     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7558                  hwndMain, (DLGPROC)lpProc);\r
7559     FreeProcInstance(lpProc);\r
7560   }\r
7561 }\r
7562 \r
7563 VOID\r
7564 ErrorPopDown()\r
7565 {\r
7566   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7567   if (errorDialog == NULL) return;\r
7568   DestroyWindow(errorDialog);\r
7569   errorDialog = NULL;\r
7570 }\r
7571 \r
7572 LRESULT CALLBACK\r
7573 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7574 {\r
7575   HANDLE hwndText;\r
7576   RECT rChild;\r
7577 \r
7578   switch (message) {\r
7579   case WM_INITDIALOG:\r
7580     GetWindowRect(hDlg, &rChild);\r
7581 \r
7582     /*\r
7583     SetWindowPos(hDlg, NULL, rChild.left,\r
7584       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7585       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7586     */\r
7587 \r
7588     /* \r
7589         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7590         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7591         and it doesn't work when you resize the dialog.\r
7592         For now, just give it a default position.\r
7593     */\r
7594     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7595 \r
7596     errorDialog = hDlg;\r
7597     SetWindowText(hDlg, errorTitle);\r
7598     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7599     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7600     return FALSE;\r
7601 \r
7602   case WM_COMMAND:\r
7603     switch (LOWORD(wParam)) {\r
7604     case IDOK:\r
7605     case IDCANCEL:\r
7606       if (errorDialog == hDlg) errorDialog = NULL;\r
7607       DestroyWindow(hDlg);\r
7608       return TRUE;\r
7609 \r
7610     default:\r
7611       break;\r
7612     }\r
7613     break;\r
7614   }\r
7615   return FALSE;\r
7616 }\r
7617 \r
7618 #ifdef GOTHIC\r
7619 HWND gothicDialog = NULL;\r
7620 \r
7621 LRESULT CALLBACK\r
7622 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7623 {\r
7624   HANDLE hwndText;\r
7625   RECT rChild;\r
7626   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7627 \r
7628   switch (message) {\r
7629   case WM_INITDIALOG:\r
7630     GetWindowRect(hDlg, &rChild);\r
7631 \r
7632     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7633                                                              SWP_NOZORDER);\r
7634 \r
7635     /* \r
7636         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7637         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7638         and it doesn't work when you resize the dialog.\r
7639         For now, just give it a default position.\r
7640     */\r
7641     gothicDialog = hDlg;\r
7642     SetWindowText(hDlg, errorTitle);\r
7643     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7644     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7645     return FALSE;\r
7646 \r
7647   case WM_COMMAND:\r
7648     switch (LOWORD(wParam)) {\r
7649     case IDOK:\r
7650     case IDCANCEL:\r
7651       if (errorDialog == hDlg) errorDialog = NULL;\r
7652       DestroyWindow(hDlg);\r
7653       return TRUE;\r
7654 \r
7655     default:\r
7656       break;\r
7657     }\r
7658     break;\r
7659   }\r
7660   return FALSE;\r
7661 }\r
7662 \r
7663 VOID\r
7664 GothicPopUp(char *title, VariantClass variant)\r
7665 {\r
7666   FARPROC lpProc;\r
7667   static char *lastTitle;\r
7668 \r
7669   strncpy(errorTitle, title, sizeof(errorTitle));\r
7670   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7671 \r
7672   if(lastTitle != title && gothicDialog != NULL) {\r
7673     DestroyWindow(gothicDialog);\r
7674     gothicDialog = NULL;\r
7675   }\r
7676   if(variant != VariantNormal && gothicDialog == NULL) {\r
7677     title = lastTitle;\r
7678     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7679     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7680                  hwndMain, (DLGPROC)lpProc);\r
7681     FreeProcInstance(lpProc);\r
7682   }\r
7683 }\r
7684 #endif\r
7685 \r
7686 /*---------------------------------------------------------------------------*\\r
7687  *\r
7688  *  Ics Interaction console functions\r
7689  *\r
7690 \*---------------------------------------------------------------------------*/\r
7691 \r
7692 #define HISTORY_SIZE 64\r
7693 static char *history[HISTORY_SIZE];\r
7694 int histIn = 0, histP = 0;\r
7695 \r
7696 VOID\r
7697 SaveInHistory(char *cmd)\r
7698 {\r
7699   if (history[histIn] != NULL) {\r
7700     free(history[histIn]);\r
7701     history[histIn] = NULL;\r
7702   }\r
7703   if (*cmd == NULLCHAR) return;\r
7704   history[histIn] = StrSave(cmd);\r
7705   histIn = (histIn + 1) % HISTORY_SIZE;\r
7706   if (history[histIn] != NULL) {\r
7707     free(history[histIn]);\r
7708     history[histIn] = NULL;\r
7709   }\r
7710   histP = histIn;\r
7711 }\r
7712 \r
7713 char *\r
7714 PrevInHistory(char *cmd)\r
7715 {\r
7716   int newhp;\r
7717   if (histP == histIn) {\r
7718     if (history[histIn] != NULL) free(history[histIn]);\r
7719     history[histIn] = StrSave(cmd);\r
7720   }\r
7721   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7722   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7723   histP = newhp;\r
7724   return history[histP];\r
7725 }\r
7726 \r
7727 char *\r
7728 NextInHistory()\r
7729 {\r
7730   if (histP == histIn) return NULL;\r
7731   histP = (histP + 1) % HISTORY_SIZE;\r
7732   return history[histP];\r
7733 }\r
7734 \r
7735 typedef struct {\r
7736   char *item;\r
7737   char *command;\r
7738   BOOLEAN getname;\r
7739   BOOLEAN immediate;\r
7740 } IcsTextMenuEntry;\r
7741 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7742 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7743 \r
7744 void\r
7745 ParseIcsTextMenu(char *icsTextMenuString)\r
7746 {\r
7747 //  int flags = 0;\r
7748   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7749   char *p = icsTextMenuString;\r
7750   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7751     free(e->item);\r
7752     e->item = NULL;\r
7753     if (e->command != NULL) {\r
7754       free(e->command);\r
7755       e->command = NULL;\r
7756     }\r
7757     e++;\r
7758   }\r
7759   e = icsTextMenuEntry;\r
7760   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7761     if (*p == ';' || *p == '\n') {\r
7762       e->item = strdup("-");\r
7763       e->command = NULL;\r
7764       p++;\r
7765     } else if (*p == '-') {\r
7766       e->item = strdup("-");\r
7767       e->command = NULL;\r
7768       p++;\r
7769       if (*p) p++;\r
7770     } else {\r
7771       char *q, *r, *s, *t;\r
7772       char c;\r
7773       q = strchr(p, ',');\r
7774       if (q == NULL) break;\r
7775       *q = NULLCHAR;\r
7776       r = strchr(q + 1, ',');\r
7777       if (r == NULL) break;\r
7778       *r = NULLCHAR;\r
7779       s = strchr(r + 1, ',');\r
7780       if (s == NULL) break;\r
7781       *s = NULLCHAR;\r
7782       c = ';';\r
7783       t = strchr(s + 1, c);\r
7784       if (t == NULL) {\r
7785         c = '\n';\r
7786         t = strchr(s + 1, c);\r
7787       }\r
7788       if (t != NULL) *t = NULLCHAR;\r
7789       e->item = strdup(p);\r
7790       e->command = strdup(q + 1);\r
7791       e->getname = *(r + 1) != '0';\r
7792       e->immediate = *(s + 1) != '0';\r
7793       *q = ',';\r
7794       *r = ',';\r
7795       *s = ',';\r
7796       if (t == NULL) break;\r
7797       *t = c;\r
7798       p = t + 1;\r
7799     }\r
7800     e++;\r
7801   } \r
7802 }\r
7803 \r
7804 HMENU\r
7805 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7806 {\r
7807   HMENU hmenu, h;\r
7808   int i = 0;\r
7809   hmenu = LoadMenu(hInst, "TextMenu");\r
7810   h = GetSubMenu(hmenu, 0);\r
7811   while (e->item) {\r
7812     if (strcmp(e->item, "-") == 0) {\r
7813       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7814     } else {\r
7815       if (e->item[0] == '|') {\r
7816         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7817                    IDM_CommandX + i, &e->item[1]);\r
7818       } else {\r
7819         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7820       }\r
7821     }\r
7822     e++;\r
7823     i++;\r
7824   } \r
7825   return hmenu;\r
7826 }\r
7827 \r
7828 WNDPROC consoleTextWindowProc;\r
7829 \r
7830 void\r
7831 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7832 {\r
7833   char buf[MSG_SIZ], name[MSG_SIZ];\r
7834   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7835   CHARRANGE sel;\r
7836 \r
7837   if (!getname) {\r
7838     SetWindowText(hInput, command);\r
7839     if (immediate) {\r
7840       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7841     } else {\r
7842       sel.cpMin = 999999;\r
7843       sel.cpMax = 999999;\r
7844       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7845       SetFocus(hInput);\r
7846     }\r
7847     return;\r
7848   }    \r
7849   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7850   if (sel.cpMin == sel.cpMax) {\r
7851     /* Expand to surrounding word */\r
7852     TEXTRANGE tr;\r
7853     do {\r
7854       tr.chrg.cpMax = sel.cpMin;\r
7855       tr.chrg.cpMin = --sel.cpMin;\r
7856       if (sel.cpMin < 0) break;\r
7857       tr.lpstrText = name;\r
7858       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7859     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7860     sel.cpMin++;\r
7861 \r
7862     do {\r
7863       tr.chrg.cpMin = sel.cpMax;\r
7864       tr.chrg.cpMax = ++sel.cpMax;\r
7865       tr.lpstrText = name;\r
7866       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7867     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7868     sel.cpMax--;\r
7869 \r
7870     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7871       MessageBeep(MB_ICONEXCLAMATION);\r
7872       return;\r
7873     }\r
7874     tr.chrg = sel;\r
7875     tr.lpstrText = name;\r
7876     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7877   } else {\r
7878     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7879       MessageBeep(MB_ICONEXCLAMATION);\r
7880       return;\r
7881     }\r
7882     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7883   }\r
7884   if (immediate) {\r
7885     sprintf(buf, "%s %s", command, name);\r
7886     SetWindowText(hInput, buf);\r
7887     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7888   } else {\r
7889     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7890     SetWindowText(hInput, buf);\r
7891     sel.cpMin = 999999;\r
7892     sel.cpMax = 999999;\r
7893     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7894     SetFocus(hInput);\r
7895   }\r
7896 }\r
7897 \r
7898 LRESULT CALLBACK \r
7899 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7900 {\r
7901   HWND hInput;\r
7902   CHARRANGE sel;\r
7903 \r
7904   switch (message) {\r
7905   case WM_KEYDOWN:\r
7906     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7907     switch (wParam) {\r
7908     case VK_PRIOR:\r
7909       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7910       return 0;\r
7911     case VK_NEXT:\r
7912       sel.cpMin = 999999;\r
7913       sel.cpMax = 999999;\r
7914       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7915       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7916       return 0;\r
7917     }\r
7918     break;\r
7919   case WM_CHAR:\r
7920    if(wParam != '\022') {\r
7921     if (wParam == '\t') {\r
7922       if (GetKeyState(VK_SHIFT) < 0) {\r
7923         /* shifted */\r
7924         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7925         if (buttonDesc[0].hwnd) {\r
7926           SetFocus(buttonDesc[0].hwnd);\r
7927         } else {\r
7928           SetFocus(hwndMain);\r
7929         }\r
7930       } else {\r
7931         /* unshifted */\r
7932         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7933       }\r
7934     } else {\r
7935       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7936       JAWS_DELETE( SetFocus(hInput); )\r
7937       SendMessage(hInput, message, wParam, lParam);\r
7938     }\r
7939     return 0;\r
7940    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7941   case WM_RBUTTONUP:\r
7942     if (GetKeyState(VK_SHIFT) & ~1) {\r
7943       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7944         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7945     } else {\r
7946       POINT pt;\r
7947       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7948       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7949       if (sel.cpMin == sel.cpMax) {\r
7950         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7951         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7952       }\r
7953       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7954         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7955       }\r
7956       pt.x = LOWORD(lParam);\r
7957       pt.y = HIWORD(lParam);\r
7958       MenuPopup(hwnd, pt, hmenu, -1);\r
7959     }\r
7960     return 0;\r
7961   case WM_PASTE:\r
7962     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7963     SetFocus(hInput);\r
7964     return SendMessage(hInput, message, wParam, lParam);\r
7965   case WM_MBUTTONDOWN:\r
7966     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7967   case WM_RBUTTONDOWN:\r
7968     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7969       /* Move selection here if it was empty */\r
7970       POINT pt;\r
7971       pt.x = LOWORD(lParam);\r
7972       pt.y = HIWORD(lParam);\r
7973       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7974       if (sel.cpMin == sel.cpMax) {\r
7975         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7976         sel.cpMax = sel.cpMin;\r
7977         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7978       }\r
7979       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7980     }\r
7981     return 0;\r
7982   case WM_COMMAND:\r
7983     switch (LOWORD(wParam)) {\r
7984     case IDM_QuickPaste:\r
7985       {\r
7986         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7987         if (sel.cpMin == sel.cpMax) {\r
7988           MessageBeep(MB_ICONEXCLAMATION);\r
7989           return 0;\r
7990         }\r
7991         SendMessage(hwnd, WM_COPY, 0, 0);\r
7992         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7993         SendMessage(hInput, WM_PASTE, 0, 0);\r
7994         SetFocus(hInput);\r
7995         return 0;\r
7996       }\r
7997     case IDM_Cut:\r
7998       SendMessage(hwnd, WM_CUT, 0, 0);\r
7999       return 0;\r
8000     case IDM_Paste:\r
8001       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8002       return 0;\r
8003     case IDM_Copy:\r
8004       SendMessage(hwnd, WM_COPY, 0, 0);\r
8005       return 0;\r
8006     default:\r
8007       {\r
8008         int i = LOWORD(wParam) - IDM_CommandX;\r
8009         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
8010             icsTextMenuEntry[i].command != NULL) {\r
8011           CommandX(hwnd, icsTextMenuEntry[i].command,\r
8012                    icsTextMenuEntry[i].getname,\r
8013                    icsTextMenuEntry[i].immediate);\r
8014           return 0;\r
8015         }\r
8016       }\r
8017       break;\r
8018     }\r
8019     break;\r
8020   }\r
8021   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
8022 }\r
8023 \r
8024 WNDPROC consoleInputWindowProc;\r
8025 \r
8026 LRESULT CALLBACK\r
8027 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8028 {\r
8029   char buf[MSG_SIZ];\r
8030   char *p;\r
8031   static BOOL sendNextChar = FALSE;\r
8032   static BOOL quoteNextChar = FALSE;\r
8033   InputSource *is = consoleInputSource;\r
8034   CHARFORMAT cf;\r
8035   CHARRANGE sel;\r
8036 \r
8037   switch (message) {\r
8038   case WM_CHAR:\r
8039     if (!appData.localLineEditing || sendNextChar) {\r
8040       is->buf[0] = (CHAR) wParam;\r
8041       is->count = 1;\r
8042       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8043       sendNextChar = FALSE;\r
8044       return 0;\r
8045     }\r
8046     if (quoteNextChar) {\r
8047       buf[0] = (char) wParam;\r
8048       buf[1] = NULLCHAR;\r
8049       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
8050       quoteNextChar = FALSE;\r
8051       return 0;\r
8052     }\r
8053     switch (wParam) {\r
8054     case '\r':   /* Enter key */\r
8055       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
8056       if (consoleEcho) SaveInHistory(is->buf);\r
8057       is->buf[is->count++] = '\n';\r
8058       is->buf[is->count] = NULLCHAR;\r
8059       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8060       if (consoleEcho) {\r
8061         ConsoleOutput(is->buf, is->count, TRUE);\r
8062       } else if (appData.localLineEditing) {\r
8063         ConsoleOutput("\n", 1, TRUE);\r
8064       }\r
8065       /* fall thru */\r
8066     case '\033': /* Escape key */\r
8067       SetWindowText(hwnd, "");\r
8068       cf.cbSize = sizeof(CHARFORMAT);\r
8069       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8070       if (consoleEcho) {\r
8071         cf.crTextColor = textAttribs[ColorNormal].color;\r
8072       } else {\r
8073         cf.crTextColor = COLOR_ECHOOFF;\r
8074       }\r
8075       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8076       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8077       return 0;\r
8078     case '\t':   /* Tab key */\r
8079       if (GetKeyState(VK_SHIFT) < 0) {\r
8080         /* shifted */\r
8081         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8082       } else {\r
8083         /* unshifted */\r
8084         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8085         if (buttonDesc[0].hwnd) {\r
8086           SetFocus(buttonDesc[0].hwnd);\r
8087         } else {\r
8088           SetFocus(hwndMain);\r
8089         }\r
8090       }\r
8091       return 0;\r
8092     case '\023': /* Ctrl+S */\r
8093       sendNextChar = TRUE;\r
8094       return 0;\r
8095     case '\021': /* Ctrl+Q */\r
8096       quoteNextChar = TRUE;\r
8097       return 0;\r
8098     JAWS_REPLAY\r
8099     default:\r
8100       break;\r
8101     }\r
8102     break;\r
8103   case WM_KEYDOWN:\r
8104     switch (wParam) {\r
8105     case VK_UP:\r
8106       GetWindowText(hwnd, buf, MSG_SIZ);\r
8107       p = PrevInHistory(buf);\r
8108       if (p != NULL) {\r
8109         SetWindowText(hwnd, p);\r
8110         sel.cpMin = 999999;\r
8111         sel.cpMax = 999999;\r
8112         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8113         return 0;\r
8114       }\r
8115       break;\r
8116     case VK_DOWN:\r
8117       p = NextInHistory();\r
8118       if (p != NULL) {\r
8119         SetWindowText(hwnd, p);\r
8120         sel.cpMin = 999999;\r
8121         sel.cpMax = 999999;\r
8122         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8123         return 0;\r
8124       }\r
8125       break;\r
8126     case VK_HOME:\r
8127     case VK_END:\r
8128       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8129       /* fall thru */\r
8130     case VK_PRIOR:\r
8131     case VK_NEXT:\r
8132       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8133       return 0;\r
8134     }\r
8135     break;\r
8136   case WM_MBUTTONDOWN:\r
8137     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8138       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8139     break;\r
8140   case WM_RBUTTONUP:\r
8141     if (GetKeyState(VK_SHIFT) & ~1) {\r
8142       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8143         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8144     } else {\r
8145       POINT pt;\r
8146       HMENU hmenu;\r
8147       hmenu = LoadMenu(hInst, "InputMenu");\r
8148       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8149       if (sel.cpMin == sel.cpMax) {\r
8150         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8151         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8152       }\r
8153       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8154         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8155       }\r
8156       pt.x = LOWORD(lParam);\r
8157       pt.y = HIWORD(lParam);\r
8158       MenuPopup(hwnd, pt, hmenu, -1);\r
8159     }\r
8160     return 0;\r
8161   case WM_COMMAND:\r
8162     switch (LOWORD(wParam)) { \r
8163     case IDM_Undo:\r
8164       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8165       return 0;\r
8166     case IDM_SelectAll:\r
8167       sel.cpMin = 0;\r
8168       sel.cpMax = -1; /*999999?*/\r
8169       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8170       return 0;\r
8171     case IDM_Cut:\r
8172       SendMessage(hwnd, WM_CUT, 0, 0);\r
8173       return 0;\r
8174     case IDM_Paste:\r
8175       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8176       return 0;\r
8177     case IDM_Copy:\r
8178       SendMessage(hwnd, WM_COPY, 0, 0);\r
8179       return 0;\r
8180     }\r
8181     break;\r
8182   }\r
8183   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8184 }\r
8185 \r
8186 #define CO_MAX  100000\r
8187 #define CO_TRIM   1000\r
8188 \r
8189 LRESULT CALLBACK\r
8190 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8191 {\r
8192   static SnapData sd;\r
8193   static HWND hText, hInput /*, hFocus*/;\r
8194 //  InputSource *is = consoleInputSource;\r
8195   RECT rect;\r
8196   static int sizeX, sizeY;\r
8197   int newSizeX, newSizeY;\r
8198   MINMAXINFO *mmi;\r
8199 \r
8200   switch (message) {\r
8201   case WM_INITDIALOG: /* message: initialize dialog box */\r
8202     hwndConsole = hDlg;\r
8203     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8204     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8205     SetFocus(hInput);\r
8206     consoleTextWindowProc = (WNDPROC)\r
8207       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8208     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8209     consoleInputWindowProc = (WNDPROC)\r
8210       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8211     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8212     Colorize(ColorNormal, TRUE);\r
8213     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8214     ChangedConsoleFont();\r
8215     GetClientRect(hDlg, &rect);\r
8216     sizeX = rect.right;\r
8217     sizeY = rect.bottom;\r
8218     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8219         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8220       WINDOWPLACEMENT wp;\r
8221       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8222       wp.length = sizeof(WINDOWPLACEMENT);\r
8223       wp.flags = 0;\r
8224       wp.showCmd = SW_SHOW;\r
8225       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8226       wp.rcNormalPosition.left = wpConsole.x;\r
8227       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8228       wp.rcNormalPosition.top = wpConsole.y;\r
8229       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8230       SetWindowPlacement(hDlg, &wp);\r
8231     }\r
8232 #if 1\r
8233    // [HGM] Chessknight's change 2004-07-13\r
8234    else { /* Determine Defaults */\r
8235        WINDOWPLACEMENT wp;\r
8236        wpConsole.x = winWidth + 1;\r
8237        wpConsole.y = boardY;\r
8238        wpConsole.width = screenWidth -  winWidth;\r
8239        wpConsole.height = winHeight;\r
8240        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8241        wp.length = sizeof(WINDOWPLACEMENT);\r
8242        wp.flags = 0;\r
8243        wp.showCmd = SW_SHOW;\r
8244        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8245        wp.rcNormalPosition.left = wpConsole.x;\r
8246        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8247        wp.rcNormalPosition.top = wpConsole.y;\r
8248        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8249        SetWindowPlacement(hDlg, &wp);\r
8250     }\r
8251 #endif\r
8252     return FALSE;\r
8253 \r
8254   case WM_SETFOCUS:\r
8255     SetFocus(hInput);\r
8256     return 0;\r
8257 \r
8258   case WM_CLOSE:\r
8259     ExitEvent(0);\r
8260     /* not reached */\r
8261     break;\r
8262 \r
8263   case WM_SIZE:\r
8264     if (IsIconic(hDlg)) break;\r
8265     newSizeX = LOWORD(lParam);\r
8266     newSizeY = HIWORD(lParam);\r
8267     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8268       RECT rectText, rectInput;\r
8269       POINT pt;\r
8270       int newTextHeight, newTextWidth;\r
8271       GetWindowRect(hText, &rectText);\r
8272       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8273       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8274       if (newTextHeight < 0) {\r
8275         newSizeY += -newTextHeight;\r
8276         newTextHeight = 0;\r
8277       }\r
8278       SetWindowPos(hText, NULL, 0, 0,\r
8279         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8280       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8281       pt.x = rectInput.left;\r
8282       pt.y = rectInput.top + newSizeY - sizeY;\r
8283       ScreenToClient(hDlg, &pt);\r
8284       SetWindowPos(hInput, NULL, \r
8285         pt.x, pt.y, /* needs client coords */   \r
8286         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8287         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8288     }\r
8289     sizeX = newSizeX;\r
8290     sizeY = newSizeY;\r
8291     break;\r
8292 \r
8293   case WM_GETMINMAXINFO:\r
8294     /* Prevent resizing window too small */\r
8295     mmi = (MINMAXINFO *) lParam;\r
8296     mmi->ptMinTrackSize.x = 100;\r
8297     mmi->ptMinTrackSize.y = 100;\r
8298     break;\r
8299 \r
8300   /* [AS] Snapping */\r
8301   case WM_ENTERSIZEMOVE:\r
8302     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8303 \r
8304   case WM_SIZING:\r
8305     return OnSizing( &sd, hDlg, wParam, lParam );\r
8306 \r
8307   case WM_MOVING:\r
8308     return OnMoving( &sd, hDlg, wParam, lParam );\r
8309 \r
8310   case WM_EXITSIZEMOVE:\r
8311     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8312   }\r
8313 \r
8314   return DefWindowProc(hDlg, message, wParam, lParam);\r
8315 }\r
8316 \r
8317 \r
8318 VOID\r
8319 ConsoleCreate()\r
8320 {\r
8321   HWND hCons;\r
8322   if (hwndConsole) return;\r
8323   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8324   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8325 }\r
8326 \r
8327 \r
8328 VOID\r
8329 ConsoleOutput(char* data, int length, int forceVisible)\r
8330 {\r
8331   HWND hText;\r
8332   int trim, exlen;\r
8333   char *p, *q;\r
8334   char buf[CO_MAX+1];\r
8335   POINT pEnd;\r
8336   RECT rect;\r
8337   static int delayLF = 0;\r
8338   CHARRANGE savesel, sel;\r
8339 \r
8340   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8341   p = data;\r
8342   q = buf;\r
8343   if (delayLF) {\r
8344     *q++ = '\r';\r
8345     *q++ = '\n';\r
8346     delayLF = 0;\r
8347   }\r
8348   while (length--) {\r
8349     if (*p == '\n') {\r
8350       if (*++p) {\r
8351         *q++ = '\r';\r
8352         *q++ = '\n';\r
8353       } else {\r
8354         delayLF = 1;\r
8355       }\r
8356     } else if (*p == '\007') {\r
8357        MyPlaySound(&sounds[(int)SoundBell]);\r
8358        p++;\r
8359     } else {\r
8360       *q++ = *p++;\r
8361     }\r
8362   }\r
8363   *q = NULLCHAR;\r
8364   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8365   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8366   /* Save current selection */\r
8367   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8368   exlen = GetWindowTextLength(hText);\r
8369   /* Find out whether current end of text is visible */\r
8370   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8371   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8372   /* Trim existing text if it's too long */\r
8373   if (exlen + (q - buf) > CO_MAX) {\r
8374     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8375     sel.cpMin = 0;\r
8376     sel.cpMax = trim;\r
8377     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8378     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8379     exlen -= trim;\r
8380     savesel.cpMin -= trim;\r
8381     savesel.cpMax -= trim;\r
8382     if (exlen < 0) exlen = 0;\r
8383     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8384     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8385   }\r
8386   /* Append the new text */\r
8387   sel.cpMin = exlen;\r
8388   sel.cpMax = exlen;\r
8389   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8390   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8391   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8392   if (forceVisible || exlen == 0 ||\r
8393       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8394        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8395     /* Scroll to make new end of text visible if old end of text\r
8396        was visible or new text is an echo of user typein */\r
8397     sel.cpMin = 9999999;\r
8398     sel.cpMax = 9999999;\r
8399     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8400     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8401     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8402     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8403   }\r
8404   if (savesel.cpMax == exlen || forceVisible) {\r
8405     /* Move insert point to new end of text if it was at the old\r
8406        end of text or if the new text is an echo of user typein */\r
8407     sel.cpMin = 9999999;\r
8408     sel.cpMax = 9999999;\r
8409     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8410   } else {\r
8411     /* Restore previous selection */\r
8412     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8413   }\r
8414   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8415 }\r
8416 \r
8417 /*---------*/\r
8418 \r
8419 \r
8420 void\r
8421 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8422 {\r
8423   char buf[100];\r
8424   char *str;\r
8425   COLORREF oldFg, oldBg;\r
8426   HFONT oldFont;\r
8427   RECT rect;\r
8428 \r
8429   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8430 \r
8431   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8432   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8433   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8434 \r
8435   rect.left = x;\r
8436   rect.right = x + squareSize;\r
8437   rect.top  = y;\r
8438   rect.bottom = y + squareSize;\r
8439   str = buf;\r
8440 \r
8441   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8442                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8443              y, ETO_CLIPPED|ETO_OPAQUE,\r
8444              &rect, str, strlen(str), NULL);\r
8445 \r
8446   (void) SetTextColor(hdc, oldFg);\r
8447   (void) SetBkColor(hdc, oldBg);\r
8448   (void) SelectObject(hdc, oldFont);\r
8449 }\r
8450 \r
8451 void\r
8452 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8453               RECT *rect, char *color, char *flagFell)\r
8454 {\r
8455   char buf[100];\r
8456   char *str;\r
8457   COLORREF oldFg, oldBg;\r
8458   HFONT oldFont;\r
8459 \r
8460   if (appData.clockMode) {\r
8461     if (tinyLayout)\r
8462       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8463     else\r
8464       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8465     str = buf;\r
8466   } else {\r
8467     str = color;\r
8468   }\r
8469 \r
8470   if (highlight) {\r
8471     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8472     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8473   } else {\r
8474     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8475     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8476   }\r
8477   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8478 \r
8479   JAWS_SILENCE\r
8480 \r
8481   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8482              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8483              rect, str, strlen(str), NULL);\r
8484   if(logoHeight > 0 && appData.clockMode) {\r
8485       RECT r;\r
8486       sprintf(buf, "%s %s", buf+7, flagFell);\r
8487       r.top = rect->top + logoHeight/2;\r
8488       r.left = rect->left;\r
8489       r.right = rect->right;\r
8490       r.bottom = rect->bottom;\r
8491       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8492                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8493                  &r, str, strlen(str), NULL);\r
8494   }\r
8495   (void) SetTextColor(hdc, oldFg);\r
8496   (void) SetBkColor(hdc, oldBg);\r
8497   (void) SelectObject(hdc, oldFont);\r
8498 }\r
8499 \r
8500 \r
8501 int\r
8502 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8503            OVERLAPPED *ovl)\r
8504 {\r
8505   int ok, err;\r
8506 \r
8507   /* [AS]  */\r
8508   if( count <= 0 ) {\r
8509     if (appData.debugMode) {\r
8510       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8511     }\r
8512 \r
8513     return ERROR_INVALID_USER_BUFFER;\r
8514   }\r
8515 \r
8516   ResetEvent(ovl->hEvent);\r
8517   ovl->Offset = ovl->OffsetHigh = 0;\r
8518   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8519   if (ok) {\r
8520     err = NO_ERROR;\r
8521   } else {\r
8522     err = GetLastError();\r
8523     if (err == ERROR_IO_PENDING) {\r
8524       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8525       if (ok)\r
8526         err = NO_ERROR;\r
8527       else\r
8528         err = GetLastError();\r
8529     }\r
8530   }\r
8531   return err;\r
8532 }\r
8533 \r
8534 int\r
8535 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8536             OVERLAPPED *ovl)\r
8537 {\r
8538   int ok, err;\r
8539 \r
8540   ResetEvent(ovl->hEvent);\r
8541   ovl->Offset = ovl->OffsetHigh = 0;\r
8542   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8543   if (ok) {\r
8544     err = NO_ERROR;\r
8545   } else {\r
8546     err = GetLastError();\r
8547     if (err == ERROR_IO_PENDING) {\r
8548       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8549       if (ok)\r
8550         err = NO_ERROR;\r
8551       else\r
8552         err = GetLastError();\r
8553     }\r
8554   }\r
8555   return err;\r
8556 }\r
8557 \r
8558 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8559 void CheckForInputBufferFull( InputSource * is )\r
8560 {\r
8561     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8562         /* Look for end of line */\r
8563         char * p = is->buf;\r
8564         \r
8565         while( p < is->next && *p != '\n' ) {\r
8566             p++;\r
8567         }\r
8568 \r
8569         if( p >= is->next ) {\r
8570             if (appData.debugMode) {\r
8571                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8572             }\r
8573 \r
8574             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8575             is->count = (DWORD) -1;\r
8576             is->next = is->buf;\r
8577         }\r
8578     }\r
8579 }\r
8580 \r
8581 DWORD\r
8582 InputThread(LPVOID arg)\r
8583 {\r
8584   InputSource *is;\r
8585   OVERLAPPED ovl;\r
8586 \r
8587   is = (InputSource *) arg;\r
8588   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8589   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8590   while (is->hThread != NULL) {\r
8591     is->error = DoReadFile(is->hFile, is->next,\r
8592                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8593                            &is->count, &ovl);\r
8594     if (is->error == NO_ERROR) {\r
8595       is->next += is->count;\r
8596     } else {\r
8597       if (is->error == ERROR_BROKEN_PIPE) {\r
8598         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8599         is->count = 0;\r
8600       } else {\r
8601         is->count = (DWORD) -1;\r
8602         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8603         break; \r
8604       }\r
8605     }\r
8606 \r
8607     CheckForInputBufferFull( is );\r
8608 \r
8609     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8610 \r
8611     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8612 \r
8613     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8614   }\r
8615 \r
8616   CloseHandle(ovl.hEvent);\r
8617   CloseHandle(is->hFile);\r
8618 \r
8619   if (appData.debugMode) {\r
8620     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8621   }\r
8622 \r
8623   return 0;\r
8624 }\r
8625 \r
8626 \r
8627 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8628 DWORD\r
8629 NonOvlInputThread(LPVOID arg)\r
8630 {\r
8631   InputSource *is;\r
8632   char *p, *q;\r
8633   int i;\r
8634   char prev;\r
8635 \r
8636   is = (InputSource *) arg;\r
8637   while (is->hThread != NULL) {\r
8638     is->error = ReadFile(is->hFile, is->next,\r
8639                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8640                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8641     if (is->error == NO_ERROR) {\r
8642       /* Change CRLF to LF */\r
8643       if (is->next > is->buf) {\r
8644         p = is->next - 1;\r
8645         i = is->count + 1;\r
8646       } else {\r
8647         p = is->next;\r
8648         i = is->count;\r
8649       }\r
8650       q = p;\r
8651       prev = NULLCHAR;\r
8652       while (i > 0) {\r
8653         if (prev == '\r' && *p == '\n') {\r
8654           *(q-1) = '\n';\r
8655           is->count--;\r
8656         } else { \r
8657           *q++ = *p;\r
8658         }\r
8659         prev = *p++;\r
8660         i--;\r
8661       }\r
8662       *q = NULLCHAR;\r
8663       is->next = q;\r
8664     } else {\r
8665       if (is->error == ERROR_BROKEN_PIPE) {\r
8666         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8667         is->count = 0; \r
8668       } else {\r
8669         is->count = (DWORD) -1;\r
8670       }\r
8671     }\r
8672 \r
8673     CheckForInputBufferFull( is );\r
8674 \r
8675     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8676 \r
8677     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8678 \r
8679     if (is->count < 0) break;  /* Quit on error */\r
8680   }\r
8681   CloseHandle(is->hFile);\r
8682   return 0;\r
8683 }\r
8684 \r
8685 DWORD\r
8686 SocketInputThread(LPVOID arg)\r
8687 {\r
8688   InputSource *is;\r
8689 \r
8690   is = (InputSource *) arg;\r
8691   while (is->hThread != NULL) {\r
8692     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8693     if ((int)is->count == SOCKET_ERROR) {\r
8694       is->count = (DWORD) -1;\r
8695       is->error = WSAGetLastError();\r
8696     } else {\r
8697       is->error = NO_ERROR;\r
8698       is->next += is->count;\r
8699       if (is->count == 0 && is->second == is) {\r
8700         /* End of file on stderr; quit with no message */\r
8701         break;\r
8702       }\r
8703     }\r
8704     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8705 \r
8706     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8707 \r
8708     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8709   }\r
8710   return 0;\r
8711 }\r
8712 \r
8713 VOID\r
8714 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8715 {\r
8716   InputSource *is;\r
8717 \r
8718   is = (InputSource *) lParam;\r
8719   if (is->lineByLine) {\r
8720     /* Feed in lines one by one */\r
8721     char *p = is->buf;\r
8722     char *q = p;\r
8723     while (q < is->next) {\r
8724       if (*q++ == '\n') {\r
8725         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8726         p = q;\r
8727       }\r
8728     }\r
8729     \r
8730     /* Move any partial line to the start of the buffer */\r
8731     q = is->buf;\r
8732     while (p < is->next) {\r
8733       *q++ = *p++;\r
8734     }\r
8735     is->next = q;\r
8736 \r
8737     if (is->error != NO_ERROR || is->count == 0) {\r
8738       /* Notify backend of the error.  Note: If there was a partial\r
8739          line at the end, it is not flushed through. */\r
8740       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8741     }\r
8742   } else {\r
8743     /* Feed in the whole chunk of input at once */\r
8744     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8745     is->next = is->buf;\r
8746   }\r
8747 }\r
8748 \r
8749 /*---------------------------------------------------------------------------*\\r
8750  *\r
8751  *  Menu enables. Used when setting various modes.\r
8752  *\r
8753 \*---------------------------------------------------------------------------*/\r
8754 \r
8755 typedef struct {\r
8756   int item;\r
8757   int flags;\r
8758 } Enables;\r
8759 \r
8760 VOID\r
8761 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8762 {\r
8763   while (enab->item > 0) {\r
8764     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8765     enab++;\r
8766   }\r
8767 }\r
8768 \r
8769 Enables gnuEnables[] = {\r
8770   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8771   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8772   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8773   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8774   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8775   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8776   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8777   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8778   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8779   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8780   { -1, -1 }\r
8781 };\r
8782 \r
8783 Enables icsEnables[] = {\r
8784   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8785   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8786   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8787   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8788   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8789   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8790   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8791   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8792   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8793   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8794   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8795   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8796   { -1, -1 }\r
8797 };\r
8798 \r
8799 #ifdef ZIPPY\r
8800 Enables zippyEnables[] = {\r
8801   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8802   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8803   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8804   { -1, -1 }\r
8805 };\r
8806 #endif\r
8807 \r
8808 Enables ncpEnables[] = {\r
8809   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8810   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8811   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8812   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8813   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8814   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8815   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8816   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8817   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8818   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8819   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8820   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8821   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8822   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8823   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8824   { -1, -1 }\r
8825 };\r
8826 \r
8827 Enables trainingOnEnables[] = {\r
8828   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8829   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8830   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8831   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8832   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8833   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8834   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8835   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8836   { -1, -1 }\r
8837 };\r
8838 \r
8839 Enables trainingOffEnables[] = {\r
8840   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8841   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8842   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8843   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8844   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8845   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8846   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8847   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8848   { -1, -1 }\r
8849 };\r
8850 \r
8851 /* These modify either ncpEnables or gnuEnables */\r
8852 Enables cmailEnables[] = {\r
8853   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8854   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8855   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8856   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8857   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8858   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8859   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8860   { -1, -1 }\r
8861 };\r
8862 \r
8863 Enables machineThinkingEnables[] = {\r
8864   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8865   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8866   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8867   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8868   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8869   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8870   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8871   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8872   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8873   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8874   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8875   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8876   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8877   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8878   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8879   { -1, -1 }\r
8880 };\r
8881 \r
8882 Enables userThinkingEnables[] = {\r
8883   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8884   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8885   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8886   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8887   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8888   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8889   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8890   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8891   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8892   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8893   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8894   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8895   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8896   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8897   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8898   { -1, -1 }\r
8899 };\r
8900 \r
8901 /*---------------------------------------------------------------------------*\\r
8902  *\r
8903  *  Front-end interface functions exported by XBoard.\r
8904  *  Functions appear in same order as prototypes in frontend.h.\r
8905  * \r
8906 \*---------------------------------------------------------------------------*/\r
8907 VOID\r
8908 ModeHighlight()\r
8909 {\r
8910   static UINT prevChecked = 0;\r
8911   static int prevPausing = 0;\r
8912   UINT nowChecked;\r
8913 \r
8914   if (pausing != prevPausing) {\r
8915     prevPausing = pausing;\r
8916     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8917                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8918     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8919   }\r
8920 \r
8921   switch (gameMode) {\r
8922   case BeginningOfGame:\r
8923     if (appData.icsActive)\r
8924       nowChecked = IDM_IcsClient;\r
8925     else if (appData.noChessProgram)\r
8926       nowChecked = IDM_EditGame;\r
8927     else\r
8928       nowChecked = IDM_MachineBlack;\r
8929     break;\r
8930   case MachinePlaysBlack:\r
8931     nowChecked = IDM_MachineBlack;\r
8932     break;\r
8933   case MachinePlaysWhite:\r
8934     nowChecked = IDM_MachineWhite;\r
8935     break;\r
8936   case TwoMachinesPlay:\r
8937     nowChecked = IDM_TwoMachines;\r
8938     break;\r
8939   case AnalyzeMode:\r
8940     nowChecked = IDM_AnalysisMode;\r
8941     break;\r
8942   case AnalyzeFile:\r
8943     nowChecked = IDM_AnalyzeFile;\r
8944     break;\r
8945   case EditGame:\r
8946     nowChecked = IDM_EditGame;\r
8947     break;\r
8948   case PlayFromGameFile:\r
8949     nowChecked = IDM_LoadGame;\r
8950     break;\r
8951   case EditPosition:\r
8952     nowChecked = IDM_EditPosition;\r
8953     break;\r
8954   case Training:\r
8955     nowChecked = IDM_Training;\r
8956     break;\r
8957   case IcsPlayingWhite:\r
8958   case IcsPlayingBlack:\r
8959   case IcsObserving:\r
8960   case IcsIdle:\r
8961     nowChecked = IDM_IcsClient;\r
8962     break;\r
8963   default:\r
8964   case EndOfGame:\r
8965     nowChecked = 0;\r
8966     break;\r
8967   }\r
8968   if (prevChecked != 0)\r
8969     (void) CheckMenuItem(GetMenu(hwndMain),\r
8970                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8971   if (nowChecked != 0)\r
8972     (void) CheckMenuItem(GetMenu(hwndMain),\r
8973                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8974 \r
8975   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8976     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8977                           MF_BYCOMMAND|MF_ENABLED);\r
8978   } else {\r
8979     (void) EnableMenuItem(GetMenu(hwndMain), \r
8980                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8981   }\r
8982 \r
8983   prevChecked = nowChecked;\r
8984 \r
8985   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8986   if (appData.icsActive) {\r
8987        if (appData.icsEngineAnalyze) {\r
8988                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8989                        MF_BYCOMMAND|MF_CHECKED);\r
8990        } else {\r
8991                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8992                        MF_BYCOMMAND|MF_UNCHECKED);\r
8993        }\r
8994   }\r
8995 }\r
8996 \r
8997 VOID\r
8998 SetICSMode()\r
8999 {\r
9000   HMENU hmenu = GetMenu(hwndMain);\r
9001   SetMenuEnables(hmenu, icsEnables);\r
9002   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
9003     MF_BYPOSITION|MF_ENABLED);\r
9004 #ifdef ZIPPY\r
9005   if (appData.zippyPlay) {\r
9006     SetMenuEnables(hmenu, zippyEnables);\r
9007     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
9008          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9009           MF_BYCOMMAND|MF_ENABLED);\r
9010   }\r
9011 #endif\r
9012 }\r
9013 \r
9014 VOID\r
9015 SetGNUMode()\r
9016 {\r
9017   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
9018 }\r
9019 \r
9020 VOID\r
9021 SetNCPMode()\r
9022 {\r
9023   HMENU hmenu = GetMenu(hwndMain);\r
9024   SetMenuEnables(hmenu, ncpEnables);\r
9025   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
9026     MF_BYPOSITION|MF_GRAYED);\r
9027     DrawMenuBar(hwndMain);\r
9028 }\r
9029 \r
9030 VOID\r
9031 SetCmailMode()\r
9032 {\r
9033   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9034 }\r
9035 \r
9036 VOID \r
9037 SetTrainingModeOn()\r
9038 {\r
9039   int i;\r
9040   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9041   for (i = 0; i < N_BUTTONS; i++) {\r
9042     if (buttonDesc[i].hwnd != NULL)\r
9043       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9044   }\r
9045   CommentPopDown();\r
9046 }\r
9047 \r
9048 VOID SetTrainingModeOff()\r
9049 {\r
9050   int i;\r
9051   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9052   for (i = 0; i < N_BUTTONS; i++) {\r
9053     if (buttonDesc[i].hwnd != NULL)\r
9054       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9055   }\r
9056 }\r
9057 \r
9058 \r
9059 VOID\r
9060 SetUserThinkingEnables()\r
9061 {\r
9062   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9063 }\r
9064 \r
9065 VOID\r
9066 SetMachineThinkingEnables()\r
9067 {\r
9068   HMENU hMenu = GetMenu(hwndMain);\r
9069   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9070 \r
9071   SetMenuEnables(hMenu, machineThinkingEnables);\r
9072 \r
9073   if (gameMode == MachinePlaysBlack) {\r
9074     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9075   } else if (gameMode == MachinePlaysWhite) {\r
9076     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9077   } else if (gameMode == TwoMachinesPlay) {\r
9078     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9079   }\r
9080 }\r
9081 \r
9082 \r
9083 VOID\r
9084 DisplayTitle(char *str)\r
9085 {\r
9086   char title[MSG_SIZ], *host;\r
9087   if (str[0] != NULLCHAR) {\r
9088     strcpy(title, str);\r
9089   } else if (appData.icsActive) {\r
9090     if (appData.icsCommPort[0] != NULLCHAR)\r
9091       host = "ICS";\r
9092     else \r
9093       host = appData.icsHost;\r
9094     sprintf(title, "%s: %s", szTitle, host);\r
9095   } else if (appData.noChessProgram) {\r
9096     strcpy(title, szTitle);\r
9097   } else {\r
9098     strcpy(title, szTitle);\r
9099     strcat(title, ": ");\r
9100     strcat(title, first.tidy);\r
9101   }\r
9102   SetWindowText(hwndMain, title);\r
9103 }\r
9104 \r
9105 \r
9106 VOID\r
9107 DisplayMessage(char *str1, char *str2)\r
9108 {\r
9109   HDC hdc;\r
9110   HFONT oldFont;\r
9111   int remain = MESSAGE_TEXT_MAX - 1;\r
9112   int len;\r
9113 \r
9114   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9115   messageText[0] = NULLCHAR;\r
9116   if (*str1) {\r
9117     len = strlen(str1);\r
9118     if (len > remain) len = remain;\r
9119     strncpy(messageText, str1, len);\r
9120     messageText[len] = NULLCHAR;\r
9121     remain -= len;\r
9122   }\r
9123   if (*str2 && remain >= 2) {\r
9124     if (*str1) {\r
9125       strcat(messageText, "  ");\r
9126       remain -= 2;\r
9127     }\r
9128     len = strlen(str2);\r
9129     if (len > remain) len = remain;\r
9130     strncat(messageText, str2, len);\r
9131   }\r
9132   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9133 \r
9134   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9135 \r
9136   SAYMACHINEMOVE();\r
9137 \r
9138   hdc = GetDC(hwndMain);\r
9139   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9140   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9141              &messageRect, messageText, strlen(messageText), NULL);\r
9142   (void) SelectObject(hdc, oldFont);\r
9143   (void) ReleaseDC(hwndMain, hdc);\r
9144 }\r
9145 \r
9146 VOID\r
9147 DisplayError(char *str, int error)\r
9148 {\r
9149   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9150   int len;\r
9151 \r
9152   if (error == 0) {\r
9153     strcpy(buf, str);\r
9154   } else {\r
9155     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9156                         NULL, error, LANG_NEUTRAL,\r
9157                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9158     if (len > 0) {\r
9159       sprintf(buf, "%s:\n%s", str, buf2);\r
9160     } else {\r
9161       ErrorMap *em = errmap;\r
9162       while (em->err != 0 && em->err != error) em++;\r
9163       if (em->err != 0) {\r
9164         sprintf(buf, "%s:\n%s", str, em->msg);\r
9165       } else {\r
9166         sprintf(buf, "%s:\nError code %d", str, error);\r
9167       }\r
9168     }\r
9169   }\r
9170   \r
9171   ErrorPopUp("Error", buf);\r
9172 }\r
9173 \r
9174 \r
9175 VOID\r
9176 DisplayMoveError(char *str)\r
9177 {\r
9178   fromX = fromY = -1;\r
9179   ClearHighlights();\r
9180   DrawPosition(FALSE, NULL);\r
9181   if (appData.popupMoveErrors) {\r
9182     ErrorPopUp("Error", str);\r
9183   } else {\r
9184     DisplayMessage(str, "");\r
9185     moveErrorMessageUp = TRUE;\r
9186   }\r
9187 }\r
9188 \r
9189 VOID\r
9190 DisplayFatalError(char *str, int error, int exitStatus)\r
9191 {\r
9192   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9193   int len;\r
9194   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9195 \r
9196   if (error != 0) {\r
9197     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9198                         NULL, error, LANG_NEUTRAL,\r
9199                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9200     if (len > 0) {\r
9201       sprintf(buf, "%s:\n%s", str, buf2);\r
9202     } else {\r
9203       ErrorMap *em = errmap;\r
9204       while (em->err != 0 && em->err != error) em++;\r
9205       if (em->err != 0) {\r
9206         sprintf(buf, "%s:\n%s", str, em->msg);\r
9207       } else {\r
9208         sprintf(buf, "%s:\nError code %d", str, error);\r
9209       }\r
9210     }\r
9211     str = buf;\r
9212   }\r
9213   if (appData.debugMode) {\r
9214     fprintf(debugFP, "%s: %s\n", label, str);\r
9215   }\r
9216   if (appData.popupExitMessage) {\r
9217     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9218                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9219   }\r
9220   ExitEvent(exitStatus);\r
9221 }\r
9222 \r
9223 \r
9224 VOID\r
9225 DisplayInformation(char *str)\r
9226 {\r
9227   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9228 }\r
9229 \r
9230 \r
9231 VOID\r
9232 DisplayNote(char *str)\r
9233 {\r
9234   ErrorPopUp("Note", str);\r
9235 }\r
9236 \r
9237 \r
9238 typedef struct {\r
9239   char *title, *question, *replyPrefix;\r
9240   ProcRef pr;\r
9241 } QuestionParams;\r
9242 \r
9243 LRESULT CALLBACK\r
9244 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9245 {\r
9246   static QuestionParams *qp;\r
9247   char reply[MSG_SIZ];\r
9248   int len, err;\r
9249 \r
9250   switch (message) {\r
9251   case WM_INITDIALOG:\r
9252     qp = (QuestionParams *) lParam;\r
9253     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9254     SetWindowText(hDlg, qp->title);\r
9255     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9256     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9257     return FALSE;\r
9258 \r
9259   case WM_COMMAND:\r
9260     switch (LOWORD(wParam)) {\r
9261     case IDOK:\r
9262       strcpy(reply, qp->replyPrefix);\r
9263       if (*reply) strcat(reply, " ");\r
9264       len = strlen(reply);\r
9265       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9266       strcat(reply, "\n");\r
9267       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9268       EndDialog(hDlg, TRUE);\r
9269       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9270       return TRUE;\r
9271     case IDCANCEL:\r
9272       EndDialog(hDlg, FALSE);\r
9273       return TRUE;\r
9274     default:\r
9275       break;\r
9276     }\r
9277     break;\r
9278   }\r
9279   return FALSE;\r
9280 }\r
9281 \r
9282 VOID\r
9283 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9284 {\r
9285     QuestionParams qp;\r
9286     FARPROC lpProc;\r
9287     \r
9288     qp.title = title;\r
9289     qp.question = question;\r
9290     qp.replyPrefix = replyPrefix;\r
9291     qp.pr = pr;\r
9292     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9293     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9294       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9295     FreeProcInstance(lpProc);\r
9296 }\r
9297 \r
9298 /* [AS] Pick FRC position */\r
9299 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9300 {\r
9301     static int * lpIndexFRC;\r
9302     BOOL index_is_ok;\r
9303     char buf[16];\r
9304 \r
9305     switch( message )\r
9306     {\r
9307     case WM_INITDIALOG:\r
9308         lpIndexFRC = (int *) lParam;\r
9309 \r
9310         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9311 \r
9312         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9313         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9314         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9315         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9316 \r
9317         break;\r
9318 \r
9319     case WM_COMMAND:\r
9320         switch( LOWORD(wParam) ) {\r
9321         case IDOK:\r
9322             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9323             EndDialog( hDlg, 0 );\r
9324             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9325             return TRUE;\r
9326         case IDCANCEL:\r
9327             EndDialog( hDlg, 1 );   \r
9328             return TRUE;\r
9329         case IDC_NFG_Edit:\r
9330             if( HIWORD(wParam) == EN_CHANGE ) {\r
9331                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9332 \r
9333                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9334             }\r
9335             return TRUE;\r
9336         case IDC_NFG_Random:\r
9337             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9338             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9339             return TRUE;\r
9340         }\r
9341 \r
9342         break;\r
9343     }\r
9344 \r
9345     return FALSE;\r
9346 }\r
9347 \r
9348 int NewGameFRC()\r
9349 {\r
9350     int result;\r
9351     int index = appData.defaultFrcPosition;\r
9352     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9353 \r
9354     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9355 \r
9356     if( result == 0 ) {\r
9357         appData.defaultFrcPosition = index;\r
9358     }\r
9359 \r
9360     return result;\r
9361 }\r
9362 \r
9363 /* [AS] Game list options */\r
9364 typedef struct {\r
9365     char id;\r
9366     char * name;\r
9367 } GLT_Item;\r
9368 \r
9369 static GLT_Item GLT_ItemInfo[] = {\r
9370     { GLT_EVENT,      "Event" },\r
9371     { GLT_SITE,       "Site" },\r
9372     { GLT_DATE,       "Date" },\r
9373     { GLT_ROUND,      "Round" },\r
9374     { GLT_PLAYERS,    "Players" },\r
9375     { GLT_RESULT,     "Result" },\r
9376     { GLT_WHITE_ELO,  "White Rating" },\r
9377     { GLT_BLACK_ELO,  "Black Rating" },\r
9378     { GLT_TIME_CONTROL,"Time Control" },\r
9379     { GLT_VARIANT,    "Variant" },\r
9380     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9381     { 0, 0 }\r
9382 };\r
9383 \r
9384 const char * GLT_FindItem( char id )\r
9385 {\r
9386     const char * result = 0;\r
9387 \r
9388     GLT_Item * list = GLT_ItemInfo;\r
9389 \r
9390     while( list->id != 0 ) {\r
9391         if( list->id == id ) {\r
9392             result = list->name;\r
9393             break;\r
9394         }\r
9395 \r
9396         list++;\r
9397     }\r
9398 \r
9399     return result;\r
9400 }\r
9401 \r
9402 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9403 {\r
9404     const char * name = GLT_FindItem( id );\r
9405 \r
9406     if( name != 0 ) {\r
9407         if( index >= 0 ) {\r
9408             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9409         }\r
9410         else {\r
9411             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9412         }\r
9413     }\r
9414 }\r
9415 \r
9416 void GLT_TagsToList( HWND hDlg, char * tags )\r
9417 {\r
9418     char * pc = tags;\r
9419 \r
9420     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9421 \r
9422     while( *pc ) {\r
9423         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9424         pc++;\r
9425     }\r
9426 \r
9427     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9428 \r
9429     pc = GLT_ALL_TAGS;\r
9430 \r
9431     while( *pc ) {\r
9432         if( strchr( tags, *pc ) == 0 ) {\r
9433             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9434         }\r
9435         pc++;\r
9436     }\r
9437 \r
9438     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9439 }\r
9440 \r
9441 char GLT_ListItemToTag( HWND hDlg, int index )\r
9442 {\r
9443     char result = '\0';\r
9444     char name[128];\r
9445 \r
9446     GLT_Item * list = GLT_ItemInfo;\r
9447 \r
9448     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9449         while( list->id != 0 ) {\r
9450             if( strcmp( list->name, name ) == 0 ) {\r
9451                 result = list->id;\r
9452                 break;\r
9453             }\r
9454 \r
9455             list++;\r
9456         }\r
9457     }\r
9458 \r
9459     return result;\r
9460 }\r
9461 \r
9462 void GLT_MoveSelection( HWND hDlg, int delta )\r
9463 {\r
9464     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9465     int idx2 = idx1 + delta;\r
9466     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9467 \r
9468     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9469         char buf[128];\r
9470 \r
9471         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9472         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9473         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9474         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9475     }\r
9476 }\r
9477 \r
9478 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9479 {\r
9480     static char glt[64];\r
9481     static char * lpUserGLT;\r
9482 \r
9483     switch( message )\r
9484     {\r
9485     case WM_INITDIALOG:\r
9486         lpUserGLT = (char *) lParam;\r
9487         \r
9488         strcpy( glt, lpUserGLT );\r
9489 \r
9490         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9491 \r
9492         /* Initialize list */\r
9493         GLT_TagsToList( hDlg, glt );\r
9494 \r
9495         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9496 \r
9497         break;\r
9498 \r
9499     case WM_COMMAND:\r
9500         switch( LOWORD(wParam) ) {\r
9501         case IDOK:\r
9502             {\r
9503                 char * pc = lpUserGLT;\r
9504                 int idx = 0;\r
9505 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9506                 char id;\r
9507 \r
9508                 do {\r
9509                     id = GLT_ListItemToTag( hDlg, idx );\r
9510 \r
9511                     *pc++ = id;\r
9512                     idx++;\r
9513                 } while( id != '\0' );\r
9514             }\r
9515             EndDialog( hDlg, 0 );\r
9516             return TRUE;\r
9517         case IDCANCEL:\r
9518             EndDialog( hDlg, 1 );\r
9519             return TRUE;\r
9520 \r
9521         case IDC_GLT_Default:\r
9522             strcpy( glt, GLT_DEFAULT_TAGS );\r
9523             GLT_TagsToList( hDlg, glt );\r
9524             return TRUE;\r
9525 \r
9526         case IDC_GLT_Restore:\r
9527             strcpy( glt, lpUserGLT );\r
9528             GLT_TagsToList( hDlg, glt );\r
9529             return TRUE;\r
9530 \r
9531         case IDC_GLT_Up:\r
9532             GLT_MoveSelection( hDlg, -1 );\r
9533             return TRUE;\r
9534 \r
9535         case IDC_GLT_Down:\r
9536             GLT_MoveSelection( hDlg, +1 );\r
9537             return TRUE;\r
9538         }\r
9539 \r
9540         break;\r
9541     }\r
9542 \r
9543     return FALSE;\r
9544 }\r
9545 \r
9546 int GameListOptions()\r
9547 {\r
9548     char glt[64];\r
9549     int result;\r
9550     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9551 \r
9552     strcpy( glt, appData.gameListTags );\r
9553 \r
9554     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9555 \r
9556     if( result == 0 ) {\r
9557         /* [AS] Memory leak here! */\r
9558         appData.gameListTags = strdup( glt ); \r
9559     }\r
9560 \r
9561     return result;\r
9562 }\r
9563 \r
9564 \r
9565 VOID\r
9566 DisplayIcsInteractionTitle(char *str)\r
9567 {\r
9568   char consoleTitle[MSG_SIZ];\r
9569 \r
9570   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9571   SetWindowText(hwndConsole, consoleTitle);\r
9572 }\r
9573 \r
9574 void\r
9575 DrawPosition(int fullRedraw, Board board)\r
9576 {\r
9577   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9578 }\r
9579 \r
9580 \r
9581 VOID\r
9582 ResetFrontEnd()\r
9583 {\r
9584   fromX = fromY = -1;\r
9585   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9586     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9587     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9588     dragInfo.lastpos = dragInfo.pos;\r
9589     dragInfo.start.x = dragInfo.start.y = -1;\r
9590     dragInfo.from = dragInfo.start;\r
9591     ReleaseCapture();\r
9592     DrawPosition(TRUE, NULL);\r
9593   }\r
9594 }\r
9595 \r
9596 \r
9597 VOID\r
9598 CommentPopUp(char *title, char *str)\r
9599 {\r
9600   HWND hwnd = GetActiveWindow();\r
9601   EitherCommentPopUp(0, title, str, FALSE);\r
9602   SetActiveWindow(hwnd);\r
9603 }\r
9604 \r
9605 VOID\r
9606 CommentPopDown(void)\r
9607 {\r
9608   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9609   if (commentDialog) {\r
9610     ShowWindow(commentDialog, SW_HIDE);\r
9611   }\r
9612   commentDialogUp = FALSE;\r
9613 }\r
9614 \r
9615 VOID\r
9616 EditCommentPopUp(int index, char *title, char *str)\r
9617 {\r
9618   EitherCommentPopUp(index, title, str, TRUE);\r
9619 }\r
9620 \r
9621 \r
9622 VOID\r
9623 RingBell()\r
9624 {\r
9625   MyPlaySound(&sounds[(int)SoundMove]);\r
9626 }\r
9627 \r
9628 VOID PlayIcsWinSound()\r
9629 {\r
9630   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9631 }\r
9632 \r
9633 VOID PlayIcsLossSound()\r
9634 {\r
9635   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9636 }\r
9637 \r
9638 VOID PlayIcsDrawSound()\r
9639 {\r
9640   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9641 }\r
9642 \r
9643 VOID PlayIcsUnfinishedSound()\r
9644 {\r
9645   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9646 }\r
9647 \r
9648 VOID\r
9649 PlayAlarmSound()\r
9650 {\r
9651   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9652 }\r
9653 \r
9654 \r
9655 VOID\r
9656 EchoOn()\r
9657 {\r
9658   HWND hInput;\r
9659   consoleEcho = TRUE;\r
9660   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9661   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9662   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9663 }\r
9664 \r
9665 \r
9666 VOID\r
9667 EchoOff()\r
9668 {\r
9669   CHARFORMAT cf;\r
9670   HWND hInput;\r
9671   consoleEcho = FALSE;\r
9672   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9673   /* This works OK: set text and background both to the same color */\r
9674   cf = consoleCF;\r
9675   cf.crTextColor = COLOR_ECHOOFF;\r
9676   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9677   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9678 }\r
9679 \r
9680 /* No Raw()...? */\r
9681 \r
9682 void Colorize(ColorClass cc, int continuation)\r
9683 {\r
9684   currentColorClass = cc;\r
9685   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9686   consoleCF.crTextColor = textAttribs[cc].color;\r
9687   consoleCF.dwEffects = textAttribs[cc].effects;\r
9688   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9689 }\r
9690 \r
9691 char *\r
9692 UserName()\r
9693 {\r
9694   static char buf[MSG_SIZ];\r
9695   DWORD bufsiz = MSG_SIZ;\r
9696 \r
9697   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9698         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9699   }\r
9700   if (!GetUserName(buf, &bufsiz)) {\r
9701     /*DisplayError("Error getting user name", GetLastError());*/\r
9702     strcpy(buf, "User");\r
9703   }\r
9704   return buf;\r
9705 }\r
9706 \r
9707 char *\r
9708 HostName()\r
9709 {\r
9710   static char buf[MSG_SIZ];\r
9711   DWORD bufsiz = MSG_SIZ;\r
9712 \r
9713   if (!GetComputerName(buf, &bufsiz)) {\r
9714     /*DisplayError("Error getting host name", GetLastError());*/\r
9715     strcpy(buf, "Unknown");\r
9716   }\r
9717   return buf;\r
9718 }\r
9719 \r
9720 \r
9721 int\r
9722 ClockTimerRunning()\r
9723 {\r
9724   return clockTimerEvent != 0;\r
9725 }\r
9726 \r
9727 int\r
9728 StopClockTimer()\r
9729 {\r
9730   if (clockTimerEvent == 0) return FALSE;\r
9731   KillTimer(hwndMain, clockTimerEvent);\r
9732   clockTimerEvent = 0;\r
9733   return TRUE;\r
9734 }\r
9735 \r
9736 void\r
9737 StartClockTimer(long millisec)\r
9738 {\r
9739   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9740                              (UINT) millisec, NULL);\r
9741 }\r
9742 \r
9743 void\r
9744 DisplayWhiteClock(long timeRemaining, int highlight)\r
9745 {\r
9746   HDC hdc;\r
9747   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9748 \r
9749   if(appData.noGUI) return;\r
9750   hdc = GetDC(hwndMain);\r
9751   if (!IsIconic(hwndMain)) {\r
9752     DisplayAClock(hdc, timeRemaining, highlight, \r
9753                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9754   }\r
9755   if (highlight && iconCurrent == iconBlack) {\r
9756     iconCurrent = iconWhite;\r
9757     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9758     if (IsIconic(hwndMain)) {\r
9759       DrawIcon(hdc, 2, 2, iconCurrent);\r
9760     }\r
9761   }\r
9762   (void) ReleaseDC(hwndMain, hdc);\r
9763   if (hwndConsole)\r
9764     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9765 }\r
9766 \r
9767 void\r
9768 DisplayBlackClock(long timeRemaining, int highlight)\r
9769 {\r
9770   HDC hdc;\r
9771   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9772 \r
9773   if(appData.noGUI) return;\r
9774   hdc = GetDC(hwndMain);\r
9775   if (!IsIconic(hwndMain)) {\r
9776     DisplayAClock(hdc, timeRemaining, highlight, \r
9777                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9778   }\r
9779   if (highlight && iconCurrent == iconWhite) {\r
9780     iconCurrent = iconBlack;\r
9781     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9782     if (IsIconic(hwndMain)) {\r
9783       DrawIcon(hdc, 2, 2, iconCurrent);\r
9784     }\r
9785   }\r
9786   (void) ReleaseDC(hwndMain, hdc);\r
9787   if (hwndConsole)\r
9788     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9789 }\r
9790 \r
9791 \r
9792 int\r
9793 LoadGameTimerRunning()\r
9794 {\r
9795   return loadGameTimerEvent != 0;\r
9796 }\r
9797 \r
9798 int\r
9799 StopLoadGameTimer()\r
9800 {\r
9801   if (loadGameTimerEvent == 0) return FALSE;\r
9802   KillTimer(hwndMain, loadGameTimerEvent);\r
9803   loadGameTimerEvent = 0;\r
9804   return TRUE;\r
9805 }\r
9806 \r
9807 void\r
9808 StartLoadGameTimer(long millisec)\r
9809 {\r
9810   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9811                                 (UINT) millisec, NULL);\r
9812 }\r
9813 \r
9814 void\r
9815 AutoSaveGame()\r
9816 {\r
9817   char *defName;\r
9818   FILE *f;\r
9819   char fileTitle[MSG_SIZ];\r
9820 \r
9821   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9822   f = OpenFileDialog(hwndMain, "a", defName,\r
9823                      appData.oldSaveStyle ? "gam" : "pgn",\r
9824                      GAME_FILT, \r
9825                      "Save Game to File", NULL, fileTitle, NULL);\r
9826   if (f != NULL) {\r
9827     SaveGame(f, 0, "");\r
9828     fclose(f);\r
9829   }\r
9830 }\r
9831 \r
9832 \r
9833 void\r
9834 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9835 {\r
9836   if (delayedTimerEvent != 0) {\r
9837     if (appData.debugMode) {\r
9838       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9839     }\r
9840     KillTimer(hwndMain, delayedTimerEvent);\r
9841     delayedTimerEvent = 0;\r
9842     delayedTimerCallback();\r
9843   }\r
9844   delayedTimerCallback = cb;\r
9845   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9846                                 (UINT) millisec, NULL);\r
9847 }\r
9848 \r
9849 DelayedEventCallback\r
9850 GetDelayedEvent()\r
9851 {\r
9852   if (delayedTimerEvent) {\r
9853     return delayedTimerCallback;\r
9854   } else {\r
9855     return NULL;\r
9856   }\r
9857 }\r
9858 \r
9859 void\r
9860 CancelDelayedEvent()\r
9861 {\r
9862   if (delayedTimerEvent) {\r
9863     KillTimer(hwndMain, delayedTimerEvent);\r
9864     delayedTimerEvent = 0;\r
9865   }\r
9866 }\r
9867 \r
9868 DWORD GetWin32Priority(int nice)\r
9869 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9870 /*\r
9871 REALTIME_PRIORITY_CLASS     0x00000100\r
9872 HIGH_PRIORITY_CLASS         0x00000080\r
9873 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9874 NORMAL_PRIORITY_CLASS       0x00000020\r
9875 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9876 IDLE_PRIORITY_CLASS         0x00000040\r
9877 */\r
9878         if (nice < -15) return 0x00000080;\r
9879         if (nice < 0)   return 0x00008000;\r
9880         if (nice == 0)  return 0x00000020;\r
9881         if (nice < 15)  return 0x00004000;\r
9882         return 0x00000040;\r
9883 }\r
9884 \r
9885 /* Start a child process running the given program.\r
9886    The process's standard output can be read from "from", and its\r
9887    standard input can be written to "to".\r
9888    Exit with fatal error if anything goes wrong.\r
9889    Returns an opaque pointer that can be used to destroy the process\r
9890    later.\r
9891 */\r
9892 int\r
9893 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9894 {\r
9895 #define BUFSIZE 4096\r
9896 \r
9897   HANDLE hChildStdinRd, hChildStdinWr,\r
9898     hChildStdoutRd, hChildStdoutWr;\r
9899   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9900   SECURITY_ATTRIBUTES saAttr;\r
9901   BOOL fSuccess;\r
9902   PROCESS_INFORMATION piProcInfo;\r
9903   STARTUPINFO siStartInfo;\r
9904   ChildProc *cp;\r
9905   char buf[MSG_SIZ];\r
9906   DWORD err;\r
9907 \r
9908   if (appData.debugMode) {\r
9909     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9910   }\r
9911 \r
9912   *pr = NoProc;\r
9913 \r
9914   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9915   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9916   saAttr.bInheritHandle = TRUE;\r
9917   saAttr.lpSecurityDescriptor = NULL;\r
9918 \r
9919   /*\r
9920    * The steps for redirecting child's STDOUT:\r
9921    *     1. Create anonymous pipe to be STDOUT for child.\r
9922    *     2. Create a noninheritable duplicate of read handle,\r
9923    *         and close the inheritable read handle.\r
9924    */\r
9925 \r
9926   /* Create a pipe for the child's STDOUT. */\r
9927   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9928     return GetLastError();\r
9929   }\r
9930 \r
9931   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9932   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9933                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9934                              FALSE,     /* not inherited */\r
9935                              DUPLICATE_SAME_ACCESS);\r
9936   if (! fSuccess) {\r
9937     return GetLastError();\r
9938   }\r
9939   CloseHandle(hChildStdoutRd);\r
9940 \r
9941   /*\r
9942    * The steps for redirecting child's STDIN:\r
9943    *     1. Create anonymous pipe to be STDIN for child.\r
9944    *     2. Create a noninheritable duplicate of write handle,\r
9945    *         and close the inheritable write handle.\r
9946    */\r
9947 \r
9948   /* Create a pipe for the child's STDIN. */\r
9949   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9950     return GetLastError();\r
9951   }\r
9952 \r
9953   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9954   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9955                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9956                              FALSE,     /* not inherited */\r
9957                              DUPLICATE_SAME_ACCESS);\r
9958   if (! fSuccess) {\r
9959     return GetLastError();\r
9960   }\r
9961   CloseHandle(hChildStdinWr);\r
9962 \r
9963   /* Arrange to (1) look in dir for the child .exe file, and\r
9964    * (2) have dir be the child's working directory.  Interpret\r
9965    * dir relative to the directory WinBoard loaded from. */\r
9966   GetCurrentDirectory(MSG_SIZ, buf);\r
9967   SetCurrentDirectory(installDir);\r
9968   SetCurrentDirectory(dir);\r
9969 \r
9970   /* Now create the child process. */\r
9971 \r
9972   siStartInfo.cb = sizeof(STARTUPINFO);\r
9973   siStartInfo.lpReserved = NULL;\r
9974   siStartInfo.lpDesktop = NULL;\r
9975   siStartInfo.lpTitle = NULL;\r
9976   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9977   siStartInfo.cbReserved2 = 0;\r
9978   siStartInfo.lpReserved2 = NULL;\r
9979   siStartInfo.hStdInput = hChildStdinRd;\r
9980   siStartInfo.hStdOutput = hChildStdoutWr;\r
9981   siStartInfo.hStdError = hChildStdoutWr;\r
9982 \r
9983   fSuccess = CreateProcess(NULL,\r
9984                            cmdLine,        /* command line */\r
9985                            NULL,           /* process security attributes */\r
9986                            NULL,           /* primary thread security attrs */\r
9987                            TRUE,           /* handles are inherited */\r
9988                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9989                            NULL,           /* use parent's environment */\r
9990                            NULL,\r
9991                            &siStartInfo, /* STARTUPINFO pointer */\r
9992                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9993 \r
9994   err = GetLastError();\r
9995   SetCurrentDirectory(buf); /* return to prev directory */\r
9996   if (! fSuccess) {\r
9997     return err;\r
9998   }\r
9999 \r
10000   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
10001     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
10002     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
10003   }\r
10004 \r
10005   /* Close the handles we don't need in the parent */\r
10006   CloseHandle(piProcInfo.hThread);\r
10007   CloseHandle(hChildStdinRd);\r
10008   CloseHandle(hChildStdoutWr);\r
10009 \r
10010   /* Prepare return value */\r
10011   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10012   cp->kind = CPReal;\r
10013   cp->hProcess = piProcInfo.hProcess;\r
10014   cp->pid = piProcInfo.dwProcessId;\r
10015   cp->hFrom = hChildStdoutRdDup;\r
10016   cp->hTo = hChildStdinWrDup;\r
10017 \r
10018   *pr = (void *) cp;\r
10019 \r
10020   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10021      2000 where engines sometimes don't see the initial command(s)\r
10022      from WinBoard and hang.  I don't understand how that can happen,\r
10023      but the Sleep is harmless, so I've put it in.  Others have also\r
10024      reported what may be the same problem, so hopefully this will fix\r
10025      it for them too.  */\r
10026   Sleep(500);\r
10027 \r
10028   return NO_ERROR;\r
10029 }\r
10030 \r
10031 \r
10032 void\r
10033 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10034 {\r
10035   ChildProc *cp; int result;\r
10036 \r
10037   cp = (ChildProc *) pr;\r
10038   if (cp == NULL) return;\r
10039 \r
10040   switch (cp->kind) {\r
10041   case CPReal:\r
10042     /* TerminateProcess is considered harmful, so... */\r
10043     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10044     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10045     /* The following doesn't work because the chess program\r
10046        doesn't "have the same console" as WinBoard.  Maybe\r
10047        we could arrange for this even though neither WinBoard\r
10048        nor the chess program uses a console for stdio? */\r
10049     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10050 \r
10051     /* [AS] Special termination modes for misbehaving programs... */\r
10052     if( signal == 9 ) { \r
10053         result = TerminateProcess( cp->hProcess, 0 );\r
10054 \r
10055         if ( appData.debugMode) {\r
10056             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10057         }\r
10058     }\r
10059     else if( signal == 10 ) {\r
10060         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10061 \r
10062         if( dw != WAIT_OBJECT_0 ) {\r
10063             result = TerminateProcess( cp->hProcess, 0 );\r
10064 \r
10065             if ( appData.debugMode) {\r
10066                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10067             }\r
10068 \r
10069         }\r
10070     }\r
10071 \r
10072     CloseHandle(cp->hProcess);\r
10073     break;\r
10074 \r
10075   case CPComm:\r
10076     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10077     break;\r
10078 \r
10079   case CPSock:\r
10080     closesocket(cp->sock);\r
10081     WSACleanup();\r
10082     break;\r
10083 \r
10084   case CPRcmd:\r
10085     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10086     closesocket(cp->sock);\r
10087     closesocket(cp->sock2);\r
10088     WSACleanup();\r
10089     break;\r
10090   }\r
10091   free(cp);\r
10092 }\r
10093 \r
10094 void\r
10095 InterruptChildProcess(ProcRef pr)\r
10096 {\r
10097   ChildProc *cp;\r
10098 \r
10099   cp = (ChildProc *) pr;\r
10100   if (cp == NULL) return;\r
10101   switch (cp->kind) {\r
10102   case CPReal:\r
10103     /* The following doesn't work because the chess program\r
10104        doesn't "have the same console" as WinBoard.  Maybe\r
10105        we could arrange for this even though neither WinBoard\r
10106        nor the chess program uses a console for stdio */\r
10107     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10108     break;\r
10109 \r
10110   case CPComm:\r
10111   case CPSock:\r
10112     /* Can't interrupt */\r
10113     break;\r
10114 \r
10115   case CPRcmd:\r
10116     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10117     break;\r
10118   }\r
10119 }\r
10120 \r
10121 \r
10122 int\r
10123 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10124 {\r
10125   char cmdLine[MSG_SIZ];\r
10126 \r
10127   if (port[0] == NULLCHAR) {\r
10128     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10129   } else {\r
10130     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10131   }\r
10132   return StartChildProcess(cmdLine, "", pr);\r
10133 }\r
10134 \r
10135 \r
10136 /* Code to open TCP sockets */\r
10137 \r
10138 int\r
10139 OpenTCP(char *host, char *port, ProcRef *pr)\r
10140 {\r
10141   ChildProc *cp;\r
10142   int err;\r
10143   SOCKET s;\r
10144   struct sockaddr_in sa, mysa;\r
10145   struct hostent FAR *hp;\r
10146   unsigned short uport;\r
10147   WORD wVersionRequested;\r
10148   WSADATA wsaData;\r
10149 \r
10150   /* Initialize socket DLL */\r
10151   wVersionRequested = MAKEWORD(1, 1);\r
10152   err = WSAStartup(wVersionRequested, &wsaData);\r
10153   if (err != 0) return err;\r
10154 \r
10155   /* Make socket */\r
10156   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10157     err = WSAGetLastError();\r
10158     WSACleanup();\r
10159     return err;\r
10160   }\r
10161 \r
10162   /* Bind local address using (mostly) don't-care values.\r
10163    */\r
10164   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10165   mysa.sin_family = AF_INET;\r
10166   mysa.sin_addr.s_addr = INADDR_ANY;\r
10167   uport = (unsigned short) 0;\r
10168   mysa.sin_port = htons(uport);\r
10169   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10170       == SOCKET_ERROR) {\r
10171     err = WSAGetLastError();\r
10172     WSACleanup();\r
10173     return err;\r
10174   }\r
10175 \r
10176   /* Resolve remote host name */\r
10177   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10178   if (!(hp = gethostbyname(host))) {\r
10179     unsigned int b0, b1, b2, b3;\r
10180 \r
10181     err = WSAGetLastError();\r
10182 \r
10183     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10184       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10185       hp->h_addrtype = AF_INET;\r
10186       hp->h_length = 4;\r
10187       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10188       hp->h_addr_list[0] = (char *) malloc(4);\r
10189       hp->h_addr_list[0][0] = (char) b0;\r
10190       hp->h_addr_list[0][1] = (char) b1;\r
10191       hp->h_addr_list[0][2] = (char) b2;\r
10192       hp->h_addr_list[0][3] = (char) b3;\r
10193     } else {\r
10194       WSACleanup();\r
10195       return err;\r
10196     }\r
10197   }\r
10198   sa.sin_family = hp->h_addrtype;\r
10199   uport = (unsigned short) atoi(port);\r
10200   sa.sin_port = htons(uport);\r
10201   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10202 \r
10203   /* Make connection */\r
10204   if (connect(s, (struct sockaddr *) &sa,\r
10205               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10206     err = WSAGetLastError();\r
10207     WSACleanup();\r
10208     return err;\r
10209   }\r
10210 \r
10211   /* Prepare return value */\r
10212   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10213   cp->kind = CPSock;\r
10214   cp->sock = s;\r
10215   *pr = (ProcRef *) cp;\r
10216 \r
10217   return NO_ERROR;\r
10218 }\r
10219 \r
10220 int\r
10221 OpenCommPort(char *name, ProcRef *pr)\r
10222 {\r
10223   HANDLE h;\r
10224   COMMTIMEOUTS ct;\r
10225   ChildProc *cp;\r
10226   char fullname[MSG_SIZ];\r
10227 \r
10228   if (*name != '\\')\r
10229     sprintf(fullname, "\\\\.\\%s", name);\r
10230   else\r
10231     strcpy(fullname, name);\r
10232 \r
10233   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10234                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10235   if (h == (HANDLE) -1) {\r
10236     return GetLastError();\r
10237   }\r
10238   hCommPort = h;\r
10239 \r
10240   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10241 \r
10242   /* Accumulate characters until a 100ms pause, then parse */\r
10243   ct.ReadIntervalTimeout = 100;\r
10244   ct.ReadTotalTimeoutMultiplier = 0;\r
10245   ct.ReadTotalTimeoutConstant = 0;\r
10246   ct.WriteTotalTimeoutMultiplier = 0;\r
10247   ct.WriteTotalTimeoutConstant = 0;\r
10248   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10249 \r
10250   /* Prepare return value */\r
10251   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10252   cp->kind = CPComm;\r
10253   cp->hFrom = h;\r
10254   cp->hTo = h;\r
10255   *pr = (ProcRef *) cp;\r
10256 \r
10257   return NO_ERROR;\r
10258 }\r
10259 \r
10260 int\r
10261 OpenLoopback(ProcRef *pr)\r
10262 {\r
10263   DisplayFatalError("Not implemented", 0, 1);\r
10264   return NO_ERROR;\r
10265 }\r
10266 \r
10267 \r
10268 int\r
10269 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10270 {\r
10271   ChildProc *cp;\r
10272   int err;\r
10273   SOCKET s, s2, s3;\r
10274   struct sockaddr_in sa, mysa;\r
10275   struct hostent FAR *hp;\r
10276   unsigned short uport;\r
10277   WORD wVersionRequested;\r
10278   WSADATA wsaData;\r
10279   int fromPort;\r
10280   char stderrPortStr[MSG_SIZ];\r
10281 \r
10282   /* Initialize socket DLL */\r
10283   wVersionRequested = MAKEWORD(1, 1);\r
10284   err = WSAStartup(wVersionRequested, &wsaData);\r
10285   if (err != 0) return err;\r
10286 \r
10287   /* Resolve remote host name */\r
10288   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10289   if (!(hp = gethostbyname(host))) {\r
10290     unsigned int b0, b1, b2, b3;\r
10291 \r
10292     err = WSAGetLastError();\r
10293 \r
10294     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10295       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10296       hp->h_addrtype = AF_INET;\r
10297       hp->h_length = 4;\r
10298       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10299       hp->h_addr_list[0] = (char *) malloc(4);\r
10300       hp->h_addr_list[0][0] = (char) b0;\r
10301       hp->h_addr_list[0][1] = (char) b1;\r
10302       hp->h_addr_list[0][2] = (char) b2;\r
10303       hp->h_addr_list[0][3] = (char) b3;\r
10304     } else {\r
10305       WSACleanup();\r
10306       return err;\r
10307     }\r
10308   }\r
10309   sa.sin_family = hp->h_addrtype;\r
10310   uport = (unsigned short) 514;\r
10311   sa.sin_port = htons(uport);\r
10312   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10313 \r
10314   /* Bind local socket to unused "privileged" port address\r
10315    */\r
10316   s = INVALID_SOCKET;\r
10317   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10318   mysa.sin_family = AF_INET;\r
10319   mysa.sin_addr.s_addr = INADDR_ANY;\r
10320   for (fromPort = 1023;; fromPort--) {\r
10321     if (fromPort < 0) {\r
10322       WSACleanup();\r
10323       return WSAEADDRINUSE;\r
10324     }\r
10325     if (s == INVALID_SOCKET) {\r
10326       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10327         err = WSAGetLastError();\r
10328         WSACleanup();\r
10329         return err;\r
10330       }\r
10331     }\r
10332     uport = (unsigned short) fromPort;\r
10333     mysa.sin_port = htons(uport);\r
10334     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10335         == SOCKET_ERROR) {\r
10336       err = WSAGetLastError();\r
10337       if (err == WSAEADDRINUSE) continue;\r
10338       WSACleanup();\r
10339       return err;\r
10340     }\r
10341     if (connect(s, (struct sockaddr *) &sa,\r
10342       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10343       err = WSAGetLastError();\r
10344       if (err == WSAEADDRINUSE) {\r
10345         closesocket(s);\r
10346         s = -1;\r
10347         continue;\r
10348       }\r
10349       WSACleanup();\r
10350       return err;\r
10351     }\r
10352     break;\r
10353   }\r
10354 \r
10355   /* Bind stderr local socket to unused "privileged" port address\r
10356    */\r
10357   s2 = INVALID_SOCKET;\r
10358   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10359   mysa.sin_family = AF_INET;\r
10360   mysa.sin_addr.s_addr = INADDR_ANY;\r
10361   for (fromPort = 1023;; fromPort--) {\r
10362     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10363     if (fromPort < 0) {\r
10364       (void) closesocket(s);\r
10365       WSACleanup();\r
10366       return WSAEADDRINUSE;\r
10367     }\r
10368     if (s2 == INVALID_SOCKET) {\r
10369       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10370         err = WSAGetLastError();\r
10371         closesocket(s);\r
10372         WSACleanup();\r
10373         return err;\r
10374       }\r
10375     }\r
10376     uport = (unsigned short) fromPort;\r
10377     mysa.sin_port = htons(uport);\r
10378     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10379         == SOCKET_ERROR) {\r
10380       err = WSAGetLastError();\r
10381       if (err == WSAEADDRINUSE) continue;\r
10382       (void) closesocket(s);\r
10383       WSACleanup();\r
10384       return err;\r
10385     }\r
10386     if (listen(s2, 1) == SOCKET_ERROR) {\r
10387       err = WSAGetLastError();\r
10388       if (err == WSAEADDRINUSE) {\r
10389         closesocket(s2);\r
10390         s2 = INVALID_SOCKET;\r
10391         continue;\r
10392       }\r
10393       (void) closesocket(s);\r
10394       (void) closesocket(s2);\r
10395       WSACleanup();\r
10396       return err;\r
10397     }\r
10398     break;\r
10399   }\r
10400   prevStderrPort = fromPort; // remember port used\r
10401   sprintf(stderrPortStr, "%d", fromPort);\r
10402 \r
10403   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10404     err = WSAGetLastError();\r
10405     (void) closesocket(s);\r
10406     (void) closesocket(s2);\r
10407     WSACleanup();\r
10408     return err;\r
10409   }\r
10410 \r
10411   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10412     err = WSAGetLastError();\r
10413     (void) closesocket(s);\r
10414     (void) closesocket(s2);\r
10415     WSACleanup();\r
10416     return err;\r
10417   }\r
10418   if (*user == NULLCHAR) user = UserName();\r
10419   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10420     err = WSAGetLastError();\r
10421     (void) closesocket(s);\r
10422     (void) closesocket(s2);\r
10423     WSACleanup();\r
10424     return err;\r
10425   }\r
10426   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10427     err = WSAGetLastError();\r
10428     (void) closesocket(s);\r
10429     (void) closesocket(s2);\r
10430     WSACleanup();\r
10431     return err;\r
10432   }\r
10433 \r
10434   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10435     err = WSAGetLastError();\r
10436     (void) closesocket(s);\r
10437     (void) closesocket(s2);\r
10438     WSACleanup();\r
10439     return err;\r
10440   }\r
10441   (void) closesocket(s2);  /* Stop listening */\r
10442 \r
10443   /* Prepare return value */\r
10444   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10445   cp->kind = CPRcmd;\r
10446   cp->sock = s;\r
10447   cp->sock2 = s3;\r
10448   *pr = (ProcRef *) cp;\r
10449 \r
10450   return NO_ERROR;\r
10451 }\r
10452 \r
10453 \r
10454 InputSourceRef\r
10455 AddInputSource(ProcRef pr, int lineByLine,\r
10456                InputCallback func, VOIDSTAR closure)\r
10457 {\r
10458   InputSource *is, *is2 = NULL;\r
10459   ChildProc *cp = (ChildProc *) pr;\r
10460 \r
10461   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10462   is->lineByLine = lineByLine;\r
10463   is->func = func;\r
10464   is->closure = closure;\r
10465   is->second = NULL;\r
10466   is->next = is->buf;\r
10467   if (pr == NoProc) {\r
10468     is->kind = CPReal;\r
10469     consoleInputSource = is;\r
10470   } else {\r
10471     is->kind = cp->kind;\r
10472     /* \r
10473         [AS] Try to avoid a race condition if the thread is given control too early:\r
10474         we create all threads suspended so that the is->hThread variable can be\r
10475         safely assigned, then let the threads start with ResumeThread.\r
10476     */\r
10477     switch (cp->kind) {\r
10478     case CPReal:\r
10479       is->hFile = cp->hFrom;\r
10480       cp->hFrom = NULL; /* now owned by InputThread */\r
10481       is->hThread =\r
10482         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10483                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10484       break;\r
10485 \r
10486     case CPComm:\r
10487       is->hFile = cp->hFrom;\r
10488       cp->hFrom = NULL; /* now owned by InputThread */\r
10489       is->hThread =\r
10490         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10491                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10492       break;\r
10493 \r
10494     case CPSock:\r
10495       is->sock = cp->sock;\r
10496       is->hThread =\r
10497         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10498                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10499       break;\r
10500 \r
10501     case CPRcmd:\r
10502       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10503       *is2 = *is;\r
10504       is->sock = cp->sock;\r
10505       is->second = is2;\r
10506       is2->sock = cp->sock2;\r
10507       is2->second = is2;\r
10508       is->hThread =\r
10509         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10510                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10511       is2->hThread =\r
10512         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10513                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10514       break;\r
10515     }\r
10516 \r
10517     if( is->hThread != NULL ) {\r
10518         ResumeThread( is->hThread );\r
10519     }\r
10520 \r
10521     if( is2 != NULL && is2->hThread != NULL ) {\r
10522         ResumeThread( is2->hThread );\r
10523     }\r
10524   }\r
10525 \r
10526   return (InputSourceRef) is;\r
10527 }\r
10528 \r
10529 void\r
10530 RemoveInputSource(InputSourceRef isr)\r
10531 {\r
10532   InputSource *is;\r
10533 \r
10534   is = (InputSource *) isr;\r
10535   is->hThread = NULL;  /* tell thread to stop */\r
10536   CloseHandle(is->hThread);\r
10537   if (is->second != NULL) {\r
10538     is->second->hThread = NULL;\r
10539     CloseHandle(is->second->hThread);\r
10540   }\r
10541 }\r
10542 \r
10543 \r
10544 int\r
10545 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10546 {\r
10547   DWORD dOutCount;\r
10548   int outCount = SOCKET_ERROR;\r
10549   ChildProc *cp = (ChildProc *) pr;\r
10550   static OVERLAPPED ovl;\r
10551 \r
10552   if (pr == NoProc) {\r
10553     ConsoleOutput(message, count, FALSE);\r
10554     return count;\r
10555   } \r
10556 \r
10557   if (ovl.hEvent == NULL) {\r
10558     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10559   }\r
10560   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10561 \r
10562   switch (cp->kind) {\r
10563   case CPSock:\r
10564   case CPRcmd:\r
10565     outCount = send(cp->sock, message, count, 0);\r
10566     if (outCount == SOCKET_ERROR) {\r
10567       *outError = WSAGetLastError();\r
10568     } else {\r
10569       *outError = NO_ERROR;\r
10570     }\r
10571     break;\r
10572 \r
10573   case CPReal:\r
10574     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10575                   &dOutCount, NULL)) {\r
10576       *outError = NO_ERROR;\r
10577       outCount = (int) dOutCount;\r
10578     } else {\r
10579       *outError = GetLastError();\r
10580     }\r
10581     break;\r
10582 \r
10583   case CPComm:\r
10584     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10585                             &dOutCount, &ovl);\r
10586     if (*outError == NO_ERROR) {\r
10587       outCount = (int) dOutCount;\r
10588     }\r
10589     break;\r
10590   }\r
10591   return outCount;\r
10592 }\r
10593 \r
10594 int\r
10595 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10596                        long msdelay)\r
10597 {\r
10598   /* Ignore delay, not implemented for WinBoard */\r
10599   return OutputToProcess(pr, message, count, outError);\r
10600 }\r
10601 \r
10602 \r
10603 void\r
10604 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10605                         char *buf, int count, int error)\r
10606 {\r
10607   DisplayFatalError("Not implemented", 0, 1);\r
10608 }\r
10609 \r
10610 /* see wgamelist.c for Game List functions */\r
10611 /* see wedittags.c for Edit Tags functions */\r
10612 \r
10613 \r
10614 VOID\r
10615 ICSInitScript()\r
10616 {\r
10617   FILE *f;\r
10618   char buf[MSG_SIZ];\r
10619   char *dummy;\r
10620 \r
10621   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10622     f = fopen(buf, "r");\r
10623     if (f != NULL) {\r
10624       ProcessICSInitScript(f);\r
10625       fclose(f);\r
10626     }\r
10627   }\r
10628 }\r
10629 \r
10630 \r
10631 VOID\r
10632 StartAnalysisClock()\r
10633 {\r
10634   if (analysisTimerEvent) return;\r
10635   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10636                                         (UINT) 2000, NULL);\r
10637 }\r
10638 \r
10639 LRESULT CALLBACK\r
10640 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10641 {\r
10642   static HANDLE hwndText;\r
10643   RECT rect;\r
10644   static int sizeX, sizeY;\r
10645   int newSizeX, newSizeY, flags;\r
10646   MINMAXINFO *mmi;\r
10647 \r
10648   switch (message) {\r
10649   case WM_INITDIALOG: /* message: initialize dialog box */\r
10650     /* Initialize the dialog items */\r
10651     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10652     SetWindowText(hDlg, analysisTitle);\r
10653     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10654     /* Size and position the dialog */\r
10655     if (!analysisDialog) {\r
10656       analysisDialog = hDlg;\r
10657       flags = SWP_NOZORDER;\r
10658       GetClientRect(hDlg, &rect);\r
10659       sizeX = rect.right;\r
10660       sizeY = rect.bottom;\r
10661       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10662           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10663         WINDOWPLACEMENT wp;\r
10664         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10665         wp.length = sizeof(WINDOWPLACEMENT);\r
10666         wp.flags = 0;\r
10667         wp.showCmd = SW_SHOW;\r
10668         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10669         wp.rcNormalPosition.left = analysisX;\r
10670         wp.rcNormalPosition.right = analysisX + analysisW;\r
10671         wp.rcNormalPosition.top = analysisY;\r
10672         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10673         SetWindowPlacement(hDlg, &wp);\r
10674 \r
10675         GetClientRect(hDlg, &rect);\r
10676         newSizeX = rect.right;\r
10677         newSizeY = rect.bottom;\r
10678         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10679                               newSizeX, newSizeY);\r
10680         sizeX = newSizeX;\r
10681         sizeY = newSizeY;\r
10682       }\r
10683     }\r
10684     return FALSE;\r
10685 \r
10686   case WM_COMMAND: /* message: received a command */\r
10687     switch (LOWORD(wParam)) {\r
10688     case IDCANCEL:\r
10689       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10690           ExitAnalyzeMode();\r
10691           ModeHighlight();\r
10692           return TRUE;\r
10693       }\r
10694       EditGameEvent();\r
10695       return TRUE;\r
10696     default:\r
10697       break;\r
10698     }\r
10699     break;\r
10700 \r
10701   case WM_SIZE:\r
10702     newSizeX = LOWORD(lParam);\r
10703     newSizeY = HIWORD(lParam);\r
10704     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10705     sizeX = newSizeX;\r
10706     sizeY = newSizeY;\r
10707     break;\r
10708 \r
10709   case WM_GETMINMAXINFO:\r
10710     /* Prevent resizing window too small */\r
10711     mmi = (MINMAXINFO *) lParam;\r
10712     mmi->ptMinTrackSize.x = 100;\r
10713     mmi->ptMinTrackSize.y = 100;\r
10714     break;\r
10715   }\r
10716   return FALSE;\r
10717 }\r
10718 \r
10719 VOID\r
10720 AnalysisPopUp(char* title, char* str)\r
10721 {\r
10722   FARPROC lpProc;\r
10723   char *p, *q;\r
10724 \r
10725   /* [AS] */\r
10726   EngineOutputPopUp();\r
10727   return;\r
10728 \r
10729   if (str == NULL) str = "";\r
10730   p = (char *) malloc(2 * strlen(str) + 2);\r
10731   q = p;\r
10732   while (*str) {\r
10733     if (*str == '\n') *q++ = '\r';\r
10734     *q++ = *str++;\r
10735   }\r
10736   *q = NULLCHAR;\r
10737   if (analysisText != NULL) free(analysisText);\r
10738   analysisText = p;\r
10739 \r
10740   if (analysisDialog) {\r
10741     SetWindowText(analysisDialog, title);\r
10742     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10743     ShowWindow(analysisDialog, SW_SHOW);\r
10744   } else {\r
10745     analysisTitle = title;\r
10746     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10747     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10748                  hwndMain, (DLGPROC)lpProc);\r
10749     FreeProcInstance(lpProc);\r
10750   }\r
10751   analysisDialogUp = TRUE;  \r
10752 }\r
10753 \r
10754 VOID\r
10755 AnalysisPopDown()\r
10756 {\r
10757   if (analysisDialog) {\r
10758     ShowWindow(analysisDialog, SW_HIDE);\r
10759   }\r
10760   analysisDialogUp = FALSE;  \r
10761 }\r
10762 \r
10763 \r
10764 VOID\r
10765 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10766 {\r
10767   highlightInfo.sq[0].x = fromX;\r
10768   highlightInfo.sq[0].y = fromY;\r
10769   highlightInfo.sq[1].x = toX;\r
10770   highlightInfo.sq[1].y = toY;\r
10771 }\r
10772 \r
10773 VOID\r
10774 ClearHighlights()\r
10775 {\r
10776   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10777     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10778 }\r
10779 \r
10780 VOID\r
10781 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10782 {\r
10783   premoveHighlightInfo.sq[0].x = fromX;\r
10784   premoveHighlightInfo.sq[0].y = fromY;\r
10785   premoveHighlightInfo.sq[1].x = toX;\r
10786   premoveHighlightInfo.sq[1].y = toY;\r
10787 }\r
10788 \r
10789 VOID\r
10790 ClearPremoveHighlights()\r
10791 {\r
10792   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10793     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10794 }\r
10795 \r
10796 VOID\r
10797 ShutDownFrontEnd()\r
10798 {\r
10799   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10800   DeleteClipboardTempFiles();\r
10801 }\r
10802 \r
10803 void\r
10804 BoardToTop()\r
10805 {\r
10806     if (IsIconic(hwndMain))\r
10807       ShowWindow(hwndMain, SW_RESTORE);\r
10808 \r
10809     SetActiveWindow(hwndMain);\r
10810 }\r
10811 \r
10812 /*\r
10813  * Prototypes for animation support routines\r
10814  */\r
10815 static void ScreenSquare(int column, int row, POINT * pt);\r
10816 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10817      POINT frames[], int * nFrames);\r
10818 \r
10819 \r
10820 void\r
10821 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10822 {       // [HGM] atomic: animate blast wave\r
10823         int i;\r
10824 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10825         explodeInfo.fromX = fromX;\r
10826         explodeInfo.fromY = fromY;\r
10827         explodeInfo.toX = toX;\r
10828         explodeInfo.toY = toY;\r
10829         for(i=1; i<nFrames; i++) {\r
10830             explodeInfo.radius = (i*180)/(nFrames-1);\r
10831             DrawPosition(FALSE, NULL);\r
10832             Sleep(appData.animSpeed);\r
10833         }\r
10834         explodeInfo.radius = 0;\r
10835         DrawPosition(TRUE, NULL);\r
10836 }\r
10837 \r
10838 #define kFactor 4\r
10839 \r
10840 void\r
10841 AnimateMove(board, fromX, fromY, toX, toY)\r
10842      Board board;\r
10843      int fromX;\r
10844      int fromY;\r
10845      int toX;\r
10846      int toY;\r
10847 {\r
10848   ChessSquare piece;\r
10849   POINT start, finish, mid;\r
10850   POINT frames[kFactor * 2 + 1];\r
10851   int nFrames, n;\r
10852 \r
10853   if (!appData.animate) return;\r
10854   if (doingSizing) return;\r
10855   if (fromY < 0 || fromX < 0) return;\r
10856   piece = board[fromY][fromX];\r
10857   if (piece >= EmptySquare) return;\r
10858 \r
10859   ScreenSquare(fromX, fromY, &start);\r
10860   ScreenSquare(toX, toY, &finish);\r
10861 \r
10862   /* All pieces except knights move in straight line */\r
10863   if (piece != WhiteKnight && piece != BlackKnight) {\r
10864     mid.x = start.x + (finish.x - start.x) / 2;\r
10865     mid.y = start.y + (finish.y - start.y) / 2;\r
10866   } else {\r
10867     /* Knight: make diagonal movement then straight */\r
10868     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10869        mid.x = start.x + (finish.x - start.x) / 2;\r
10870        mid.y = finish.y;\r
10871      } else {\r
10872        mid.x = finish.x;\r
10873        mid.y = start.y + (finish.y - start.y) / 2;\r
10874      }\r
10875   }\r
10876   \r
10877   /* Don't use as many frames for very short moves */\r
10878   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10879     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10880   else\r
10881     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10882 \r
10883   animInfo.from.x = fromX;\r
10884   animInfo.from.y = fromY;\r
10885   animInfo.to.x = toX;\r
10886   animInfo.to.y = toY;\r
10887   animInfo.lastpos = start;\r
10888   animInfo.piece = piece;\r
10889   for (n = 0; n < nFrames; n++) {\r
10890     animInfo.pos = frames[n];\r
10891     DrawPosition(FALSE, NULL);\r
10892     animInfo.lastpos = animInfo.pos;\r
10893     Sleep(appData.animSpeed);\r
10894   }\r
10895   animInfo.pos = finish;\r
10896   DrawPosition(FALSE, NULL);\r
10897   animInfo.piece = EmptySquare;\r
10898   if(gameInfo.variant == VariantAtomic && \r
10899      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10900         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10901 }\r
10902 \r
10903 /*      Convert board position to corner of screen rect and color       */\r
10904 \r
10905 static void\r
10906 ScreenSquare(column, row, pt)\r
10907      int column; int row; POINT * pt;\r
10908 {\r
10909   if (flipView) {\r
10910     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10911     pt->y = lineGap + row * (squareSize + lineGap);\r
10912   } else {\r
10913     pt->x = lineGap + column * (squareSize + lineGap);\r
10914     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10915   }\r
10916 }\r
10917 \r
10918 /*      Generate a series of frame coords from start->mid->finish.\r
10919         The movement rate doubles until the half way point is\r
10920         reached, then halves back down to the final destination,\r
10921         which gives a nice slow in/out effect. The algorithmn\r
10922         may seem to generate too many intermediates for short\r
10923         moves, but remember that the purpose is to attract the\r
10924         viewers attention to the piece about to be moved and\r
10925         then to where it ends up. Too few frames would be less\r
10926         noticeable.                                             */\r
10927 \r
10928 static void\r
10929 Tween(start, mid, finish, factor, frames, nFrames)\r
10930      POINT * start; POINT * mid;\r
10931      POINT * finish; int factor;\r
10932      POINT frames[]; int * nFrames;\r
10933 {\r
10934   int n, fraction = 1, count = 0;\r
10935 \r
10936   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10937   for (n = 0; n < factor; n++)\r
10938     fraction *= 2;\r
10939   for (n = 0; n < factor; n++) {\r
10940     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10941     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10942     count ++;\r
10943     fraction = fraction / 2;\r
10944   }\r
10945   \r
10946   /* Midpoint */\r
10947   frames[count] = *mid;\r
10948   count ++;\r
10949   \r
10950   /* Slow out, stepping 1/2, then 1/4, ... */\r
10951   fraction = 2;\r
10952   for (n = 0; n < factor; n++) {\r
10953     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10954     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10955     count ++;\r
10956     fraction = fraction * 2;\r
10957   }\r
10958   *nFrames = count;\r
10959 }\r
10960 \r
10961 void\r
10962 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10963 {\r
10964 #if 0\r
10965     char buf[256];\r
10966 \r
10967     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10968         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10969 \r
10970     OutputDebugString( buf );\r
10971 #endif\r
10972 \r
10973     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10974 \r
10975     EvalGraphSet( first, last, current, pvInfoList );\r
10976 }\r
10977 \r
10978 void SetProgramStats( FrontEndProgramStats * stats )\r
10979 {\r
10980 #if 0\r
10981     char buf[1024];\r
10982 \r
10983     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10984         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
10985 \r
10986     OutputDebugString( buf );\r
10987 #endif\r
10988 \r
10989     EngineOutputUpdate( stats );\r
10990 }\r