Added width updates to ICS client on font and window size changes
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "winboard.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "wgamelist.h"\r
89 #include "wedittags.h"\r
90 #include "woptions.h"\r
91 #include "wsockerr.h"\r
92 #include "defaults.h"\r
93 #include "help.h"\r
94 #include "wsnap.h"\r
95 \r
96 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
97 \r
98   int myrandom(void);\r
99   void mysrandom(unsigned int seed);\r
100 \r
101 extern int whiteFlag, blackFlag;\r
102 Boolean flipClock = FALSE;\r
103 extern HANDLE chatHandle[];\r
104 extern int ics_type;\r
105 \r
106 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
107 VOID NewVariantPopup(HWND hwnd);\r
108 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
109                    /*char*/int promoChar));\r
110 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
111 void DisplayMove P((int moveNumber));\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P(());\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129 } DragInfo;\r
130 \r
131 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
132 \r
133 typedef struct {\r
134   POINT sq[2];    /* board coordinates of from, to squares */\r
135 } HighlightInfo;\r
136 \r
137 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 \r
140 typedef struct { // [HGM] atomic\r
141   int fromX, fromY, toX, toY, radius;\r
142 } ExplodeInfo;\r
143 \r
144 static ExplodeInfo explodeInfo;\r
145 \r
146 /* Window class names */\r
147 char szAppName[] = "WinBoard";\r
148 char szConsoleName[] = "WBConsole";\r
149 \r
150 /* Title bar text */\r
151 char szTitle[] = "WinBoard";\r
152 char szConsoleTitle[] = "I C S Interaction";\r
153 \r
154 char *programName;\r
155 char *settingsFileName;\r
156 BOOLEAN saveSettingsOnExit;\r
157 char installDir[MSG_SIZ];\r
158 \r
159 BoardSize boardSize;\r
160 BOOLEAN chessProgram;\r
161 static int boardX, boardY;\r
162 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
163 static int squareSize, lineGap, minorSize;\r
164 static int winWidth, winHeight, winW, winH;\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
166 static int logoHeight = 0;\r
167 static char messageText[MESSAGE_TEXT_MAX];\r
168 static int clockTimerEvent = 0;\r
169 static int loadGameTimerEvent = 0;\r
170 static int analysisTimerEvent = 0;\r
171 static DelayedEventCallback delayedTimerCallback;\r
172 static int delayedTimerEvent = 0;\r
173 static int buttonCount = 2;\r
174 char *icsTextMenuString;\r
175 char *icsNames;\r
176 char *firstChessProgramNames;\r
177 char *secondChessProgramNames;\r
178 \r
179 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
180 \r
181 #define PALETTESIZE 256\r
182 \r
183 HINSTANCE hInst;          /* current instance */\r
184 HWND hwndMain = NULL;        /* root window*/\r
185 HWND hwndConsole = NULL;\r
186 BOOLEAN alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 HWND hCommPort = NULL;    /* currently open comm port */\r
194 static HWND hwndPause;    /* pause button */\r
195 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
196 static HBRUSH lightSquareBrush, darkSquareBrush,\r
197   blackSquareBrush, /* [HGM] for band between board and holdings */\r
198   explodeBrush,     /* [HGM] atomic */\r
199   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
200 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
201 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
202 static HPEN gridPen = NULL;\r
203 static HPEN highlightPen = NULL;\r
204 static HPEN premovePen = NULL;\r
205 static NPLOGPALETTE pLogPal;\r
206 static BOOL paletteChanged = FALSE;\r
207 static HICON iconWhite, iconBlack, iconCurrent;\r
208 static int doingSizing = FALSE;\r
209 static int lastSizing = 0;\r
210 static int prevStderrPort;\r
211 static HBITMAP userLogo;\r
212 \r
213 /* [AS] Support for background textures */\r
214 #define BACK_TEXTURE_MODE_DISABLED      0\r
215 #define BACK_TEXTURE_MODE_PLAIN         1\r
216 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 #if defined(_winmajor)\r
229 #define oldDialog (_winmajor < 4)\r
230 #else\r
231 #define oldDialog 0\r
232 #endif\r
233 #endif\r
234 \r
235 char *defaultTextAttribs[] = \r
236 {\r
237   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
238   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
239   COLOR_NONE\r
240 };\r
241 \r
242 typedef struct {\r
243   char *name;\r
244   int squareSize;\r
245   int lineGap;\r
246   int smallLayout;\r
247   int tinyLayout;\r
248   int cliWidth, cliHeight;\r
249 } SizeInfo;\r
250 \r
251 SizeInfo sizeInfo[] = \r
252 {\r
253   { "tiny",     21, 0, 1, 1, 0, 0 },\r
254   { "teeny",    25, 1, 1, 1, 0, 0 },\r
255   { "dinky",    29, 1, 1, 1, 0, 0 },\r
256   { "petite",   33, 1, 1, 1, 0, 0 },\r
257   { "slim",     37, 2, 1, 0, 0, 0 },\r
258   { "small",    40, 2, 1, 0, 0, 0 },\r
259   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
260   { "middling", 49, 2, 0, 0, 0, 0 },\r
261   { "average",  54, 2, 0, 0, 0, 0 },\r
262   { "moderate", 58, 3, 0, 0, 0, 0 },\r
263   { "medium",   64, 3, 0, 0, 0, 0 },\r
264   { "bulky",    72, 3, 0, 0, 0, 0 },\r
265   { "large",    80, 3, 0, 0, 0, 0 },\r
266   { "big",      87, 3, 0, 0, 0, 0 },\r
267   { "huge",     95, 3, 0, 0, 0, 0 },\r
268   { "giant",    108, 3, 0, 0, 0, 0 },\r
269   { "colossal", 116, 4, 0, 0, 0, 0 },\r
270   { "titanic",  129, 4, 0, 0, 0, 0 },\r
271   { NULL, 0, 0, 0, 0, 0, 0 }\r
272 };\r
273 \r
274 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
275 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
276 {\r
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283   { 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
284   { 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
285   { 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
286   { 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
287   { 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
288   { 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
289   { 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
290   { 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
291   { 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
292   { 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
293   { 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
294   { 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
295 };\r
296 \r
297 MyFont *font[NUM_SIZES][NUM_FONTS];\r
298 \r
299 typedef struct {\r
300   char *label;\r
301   int id;\r
302   HWND hwnd;\r
303   WNDPROC wndproc;\r
304 } MyButtonDesc;\r
305 \r
306 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
307 #define N_BUTTONS 5\r
308 \r
309 MyButtonDesc buttonDesc[N_BUTTONS] =\r
310 {\r
311   {"<<", IDM_ToStart, NULL, NULL},\r
312   {"<", IDM_Backward, NULL, NULL},\r
313   {"P", IDM_Pause, NULL, NULL},\r
314   {">", IDM_Forward, NULL, NULL},\r
315   {">>", IDM_ToEnd, NULL, NULL},\r
316 };\r
317 \r
318 int tinyLayout = 0, smallLayout = 0;\r
319 #define MENU_BAR_ITEMS 7\r
320 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
321   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
322   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
323 };\r
324 \r
325 \r
326 MySound sounds[(int)NSoundClasses];\r
327 MyTextAttribs textAttribs[(int)NColorClasses];\r
328 \r
329 MyColorizeAttribs colorizeAttribs[] = {\r
330   { (COLORREF)0, 0, "Shout Text" },\r
331   { (COLORREF)0, 0, "SShout/CShout" },\r
332   { (COLORREF)0, 0, "Channel 1 Text" },\r
333   { (COLORREF)0, 0, "Channel Text" },\r
334   { (COLORREF)0, 0, "Kibitz Text" },\r
335   { (COLORREF)0, 0, "Tell Text" },\r
336   { (COLORREF)0, 0, "Challenge Text" },\r
337   { (COLORREF)0, 0, "Request Text" },\r
338   { (COLORREF)0, 0, "Seek Text" },\r
339   { (COLORREF)0, 0, "Normal Text" },\r
340   { (COLORREF)0, 0, "None" }\r
341 };\r
342 \r
343 \r
344 \r
345 static char *commentTitle;\r
346 static char *commentText;\r
347 static int commentIndex;\r
348 static Boolean editComment = FALSE;\r
349 HWND commentDialog = NULL;\r
350 BOOLEAN commentDialogUp = FALSE;\r
351 static int commentX, commentY, commentH, commentW;\r
352 \r
353 static char *analysisTitle;\r
354 static char *analysisText;\r
355 HWND analysisDialog = NULL;\r
356 BOOLEAN analysisDialogUp = FALSE;\r
357 static int analysisX, analysisY, analysisH, analysisW;\r
358 \r
359 char errorTitle[MSG_SIZ];\r
360 char errorMessage[2*MSG_SIZ];\r
361 HWND errorDialog = NULL;\r
362 BOOLEAN moveErrorMessageUp = FALSE;\r
363 BOOLEAN consoleEcho = TRUE;\r
364 CHARFORMAT consoleCF;\r
365 COLORREF consoleBackgroundColor;\r
366 \r
367 char *programVersion;\r
368 \r
369 #define CPReal 1\r
370 #define CPComm 2\r
371 #define CPSock 3\r
372 #define CPRcmd 4\r
373 typedef int CPKind;\r
374 \r
375 typedef struct {\r
376   CPKind kind;\r
377   HANDLE hProcess;\r
378   DWORD pid;\r
379   HANDLE hTo;\r
380   HANDLE hFrom;\r
381   SOCKET sock;\r
382   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
383 } ChildProc;\r
384 \r
385 #define INPUT_SOURCE_BUF_SIZE 4096\r
386 \r
387 typedef struct _InputSource {\r
388   CPKind kind;\r
389   HANDLE hFile;\r
390   SOCKET sock;\r
391   int lineByLine;\r
392   HANDLE hThread;\r
393   DWORD id;\r
394   char buf[INPUT_SOURCE_BUF_SIZE];\r
395   char *next;\r
396   DWORD count;\r
397   int error;\r
398   InputCallback func;\r
399   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
400   VOIDSTAR closure;\r
401 } InputSource;\r
402 \r
403 InputSource *consoleInputSource;\r
404 \r
405 DCB dcb;\r
406 \r
407 /* forward */\r
408 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
409 VOID ConsoleCreate();\r
410 LRESULT CALLBACK\r
411   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
412 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
413 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
414 VOID ParseCommSettings(char *arg, DCB *dcb);\r
415 LRESULT CALLBACK\r
416   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
417 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
418 void ParseIcsTextMenu(char *icsTextMenuString);\r
419 VOID PopUpMoveDialog(char firstchar);\r
420 VOID PopUpNameDialog(char firstchar);\r
421 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
422 \r
423 /* [AS] */\r
424 int NewGameFRC();\r
425 int GameListOptions();\r
426 \r
427 HWND moveHistoryDialog = NULL;\r
428 BOOLEAN moveHistoryDialogUp = FALSE;\r
429 \r
430 WindowPlacement wpMoveHistory;\r
431 \r
432 HWND evalGraphDialog = NULL;\r
433 BOOLEAN evalGraphDialogUp = FALSE;\r
434 \r
435 WindowPlacement wpEvalGraph;\r
436 \r
437 HWND engineOutputDialog = NULL;\r
438 BOOLEAN engineOutputDialogUp = FALSE;\r
439 \r
440 WindowPlacement wpEngineOutput;\r
441 WindowPlacement wpGameList;\r
442 WindowPlacement wpConsole;\r
443 \r
444 VOID MoveHistoryPopUp();\r
445 VOID MoveHistoryPopDown();\r
446 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
447 BOOL MoveHistoryIsUp();\r
448 \r
449 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
450 VOID EvalGraphPopUp();\r
451 VOID EvalGraphPopDown();\r
452 BOOL EvalGraphIsUp();\r
453 \r
454 VOID EngineOutputPopUp();\r
455 VOID EngineOutputPopDown();\r
456 BOOL EngineOutputIsUp();\r
457 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
458 \r
459 VOID EngineOptionsPopup(); // [HGM] settings\r
460 \r
461 VOID GothicPopUp(char *title, VariantClass variant);\r
462 /*\r
463  * Setting "frozen" should disable all user input other than deleting\r
464  * the window.  We do this while engines are initializing themselves.\r
465  */\r
466 static int frozen = 0;\r
467 static int oldMenuItemState[MENU_BAR_ITEMS];\r
468 void FreezeUI()\r
469 {\r
470   HMENU hmenu;\r
471   int i;\r
472 \r
473   if (frozen) return;\r
474   frozen = 1;\r
475   hmenu = GetMenu(hwndMain);\r
476   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
477     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
478   }\r
479   DrawMenuBar(hwndMain);\r
480 }\r
481 \r
482 /* Undo a FreezeUI */\r
483 void ThawUI()\r
484 {\r
485   HMENU hmenu;\r
486   int i;\r
487 \r
488   if (!frozen) return;\r
489   frozen = 0;\r
490   hmenu = GetMenu(hwndMain);\r
491   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
492     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
493   }\r
494   DrawMenuBar(hwndMain);\r
495 }\r
496 \r
497 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
498 \r
499 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
500 #ifdef JAWS\r
501 #include "jaws.c"\r
502 #else\r
503 #define JAWS_INIT\r
504 #define JAWS_ARGS\r
505 #define JAWS_ALT_INTERCEPT\r
506 #define JAWS_KB_NAVIGATION\r
507 #define JAWS_MENU_ITEMS\r
508 #define JAWS_SILENCE\r
509 #define JAWS_REPLAY\r
510 #define JAWS_ACCEL\r
511 #define JAWS_COPYRIGHT\r
512 #define JAWS_DELETE(X) X\r
513 #define SAYMACHINEMOVE()\r
514 #define SAY(X)\r
515 #endif\r
516 \r
517 /*---------------------------------------------------------------------------*\\r
518  *\r
519  * WinMain\r
520  *\r
521 \*---------------------------------------------------------------------------*/\r
522 \r
523 int APIENTRY\r
524 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
525         LPSTR lpCmdLine, int nCmdShow)\r
526 {\r
527   MSG msg;\r
528   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
529 //  INITCOMMONCONTROLSEX ex;\r
530 \r
531   debugFP = stderr;\r
532 \r
533   LoadLibrary("RICHED32.DLL");\r
534   consoleCF.cbSize = sizeof(CHARFORMAT);\r
535 \r
536   if (!InitApplication(hInstance)) {\r
537     return (FALSE);\r
538   }\r
539   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
540     return (FALSE);\r
541   }\r
542 \r
543   JAWS_INIT\r
544 \r
545 //  InitCommonControlsEx(&ex);\r
546   InitCommonControls();\r
547 \r
548   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
549   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
550   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
551 \r
552   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
553 \r
554   while (GetMessage(&msg, /* message structure */\r
555                     NULL, /* handle of window receiving the message */\r
556                     0,    /* lowest message to examine */\r
557                     0))   /* highest message to examine */\r
558     {\r
559 \r
560       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
561         // [HGM] navigate: switch between all windows with tab\r
562         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
563         int i, currentElement = 0;\r
564 \r
565         // first determine what element of the chain we come from (if any)\r
566         if(appData.icsActive) {\r
567             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
568             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
569         }\r
570         if(engineOutputDialog && EngineOutputIsUp()) {\r
571             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
572             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
573         }\r
574         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
575             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
576         }\r
577         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
578         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
579         if(msg.hwnd == e1)                 currentElement = 2; else\r
580         if(msg.hwnd == e2)                 currentElement = 3; else\r
581         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
582         if(msg.hwnd == mh)                currentElement = 4; else\r
583         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
584         if(msg.hwnd == hText)  currentElement = 5; else\r
585         if(msg.hwnd == hInput) currentElement = 6; else\r
586         for (i = 0; i < N_BUTTONS; i++) {\r
587             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
588         }\r
589 \r
590         // determine where to go to\r
591         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
592           do {\r
593             currentElement = (currentElement + direction) % 7;\r
594             switch(currentElement) {\r
595                 case 0:\r
596                   h = hwndMain; break; // passing this case always makes the loop exit\r
597                 case 1:\r
598                   h = buttonDesc[0].hwnd; break; // could be NULL\r
599                 case 2:\r
600                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
601                   h = e1; break;\r
602                 case 3:\r
603                   if(!EngineOutputIsUp()) continue;\r
604                   h = e2; break;\r
605                 case 4:\r
606                   if(!MoveHistoryIsUp()) continue;\r
607                   h = mh; break;\r
608 //              case 6: // input to eval graph does not seem to get here!\r
609 //                if(!EvalGraphIsUp()) continue;\r
610 //                h = evalGraphDialog; break;\r
611                 case 5:\r
612                   if(!appData.icsActive) continue;\r
613                   SAY("display");\r
614                   h = hText; break;\r
615                 case 6:\r
616                   if(!appData.icsActive) continue;\r
617                   SAY("input");\r
618                   h = hInput; break;\r
619             }\r
620           } while(h == 0);\r
621 \r
622           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
623           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
624           SetFocus(h);\r
625 \r
626           continue; // this message now has been processed\r
627         }\r
628       }\r
629 \r
630       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
631           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
632           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
633           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
634           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
635           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
636           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
637           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
638           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
639           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
640         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
641         for(i=0; i<MAX_CHAT; i++) \r
642             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
643                 done = 1; break;\r
644         }\r
645         if(done) continue; // [HGM] chat: end patch\r
646         TranslateMessage(&msg); /* Translates virtual key codes */\r
647         DispatchMessage(&msg);  /* Dispatches message to window */\r
648       }\r
649     }\r
650 \r
651 \r
652   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
653 }\r
654 \r
655 /*---------------------------------------------------------------------------*\\r
656  *\r
657  * Initialization functions\r
658  *\r
659 \*---------------------------------------------------------------------------*/\r
660 \r
661 void\r
662 SetUserLogo()\r
663 {   // update user logo if necessary\r
664     static char oldUserName[MSG_SIZ], *curName;\r
665 \r
666     if(appData.autoLogo) {\r
667           curName = UserName();\r
668           if(strcmp(curName, oldUserName)) {\r
669                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
670                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
671                 strcpy(oldUserName, curName);\r
672           }\r
673     }\r
674 }\r
675 \r
676 BOOL\r
677 InitApplication(HINSTANCE hInstance)\r
678 {\r
679   WNDCLASS wc;\r
680 \r
681   /* Fill in window class structure with parameters that describe the */\r
682   /* main window. */\r
683 \r
684   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
685   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
686   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
687   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
688   wc.hInstance     = hInstance;         /* Owner of this class */\r
689   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
690   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
691   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
692   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
693   wc.lpszClassName = szAppName;                 /* Name to register as */\r
694 \r
695   /* Register the window class and return success/failure code. */\r
696   if (!RegisterClass(&wc)) return FALSE;\r
697 \r
698   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
699   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
700   wc.cbClsExtra    = 0;\r
701   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
702   wc.hInstance     = hInstance;\r
703   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
704   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
705   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
706   wc.lpszMenuName  = NULL;\r
707   wc.lpszClassName = szConsoleName;\r
708 \r
709   if (!RegisterClass(&wc)) return FALSE;\r
710   return TRUE;\r
711 }\r
712 \r
713 \r
714 /* Set by InitInstance, used by EnsureOnScreen */\r
715 int screenHeight, screenWidth;\r
716 \r
717 void\r
718 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
719 {\r
720 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
721   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
722   if (*x > screenWidth - 32) *x = 0;\r
723   if (*y > screenHeight - 32) *y = 0;\r
724   if (*x < minX) *x = minX;\r
725   if (*y < minY) *y = minY;\r
726 }\r
727 \r
728 BOOL\r
729 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
730 {\r
731   HWND hwnd; /* Main window handle. */\r
732   int ibs;\r
733   WINDOWPLACEMENT wp;\r
734   char *filepart;\r
735 \r
736   hInst = hInstance;    /* Store instance handle in our global variable */\r
737 \r
738   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
739     *filepart = NULLCHAR;\r
740   } else {\r
741     GetCurrentDirectory(MSG_SIZ, installDir);\r
742   }\r
743   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
744   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
745   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
746   if (appData.debugMode) {\r
747     debugFP = fopen(appData.nameOfDebugFile, "w");\r
748     setbuf(debugFP, NULL);\r
749   }\r
750 \r
751   InitBackEnd1();\r
752 \r
753 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
754 //  InitEngineUCI( installDir, &second );\r
755 \r
756   /* Create a main window for this application instance. */\r
757   hwnd = CreateWindow(szAppName, szTitle,\r
758                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
759                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
760                       NULL, NULL, hInstance, NULL);\r
761   hwndMain = hwnd;\r
762 \r
763   /* If window could not be created, return "failure" */\r
764   if (!hwnd) {\r
765     return (FALSE);\r
766   }\r
767 \r
768   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
769   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
770       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
771 \r
772       if (first.programLogo == NULL && appData.debugMode) {\r
773           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
774       }\r
775   } else if(appData.autoLogo) {\r
776       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
777         char buf[MSG_SIZ];\r
778         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
779         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
780       }\r
781   }\r
782 \r
783   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
784       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
785 \r
786       if (second.programLogo == NULL && appData.debugMode) {\r
787           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
788       }\r
789   } else if(appData.autoLogo) {\r
790       char buf[MSG_SIZ];\r
791       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
792         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
793         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
794       } else\r
795       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
796         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
797         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
798       }\r
799   }\r
800 \r
801   SetUserLogo();\r
802 \r
803   iconWhite = LoadIcon(hInstance, "icon_white");\r
804   iconBlack = LoadIcon(hInstance, "icon_black");\r
805   iconCurrent = iconWhite;\r
806   InitDrawingColors();\r
807   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
808   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
809   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
810     /* Compute window size for each board size, and use the largest\r
811        size that fits on this screen as the default. */\r
812     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
813     if (boardSize == (BoardSize)-1 &&\r
814         winH <= screenHeight\r
815            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
816         && winW <= screenWidth) {\r
817       boardSize = (BoardSize)ibs;\r
818     }\r
819   }\r
820 \r
821   InitDrawingSizes(boardSize, 0);\r
822   InitMenuChecks();\r
823   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
824 \r
825   /* [AS] Load textures if specified */\r
826   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
827   \r
828   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
829       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
830       liteBackTextureMode = appData.liteBackTextureMode;\r
831 \r
832       if (liteBackTexture == NULL && appData.debugMode) {\r
833           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
834       }\r
835   }\r
836   \r
837   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
838       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
839       darkBackTextureMode = appData.darkBackTextureMode;\r
840 \r
841       if (darkBackTexture == NULL && appData.debugMode) {\r
842           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
843       }\r
844   }\r
845 \r
846   mysrandom( (unsigned) time(NULL) );\r
847 \r
848   /* [AS] Restore layout */\r
849   if( wpMoveHistory.visible ) {\r
850       MoveHistoryPopUp();\r
851   }\r
852 \r
853   if( wpEvalGraph.visible ) {\r
854       EvalGraphPopUp();\r
855   }\r
856 \r
857   if( wpEngineOutput.visible ) {\r
858       EngineOutputPopUp();\r
859   }\r
860 \r
861   InitBackEnd2();\r
862 \r
863   /* Make the window visible; update its client area; and return "success" */\r
864   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
865   wp.length = sizeof(WINDOWPLACEMENT);\r
866   wp.flags = 0;\r
867   wp.showCmd = nCmdShow;\r
868   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
869   wp.rcNormalPosition.left = boardX;\r
870   wp.rcNormalPosition.right = boardX + winWidth;\r
871   wp.rcNormalPosition.top = boardY;\r
872   wp.rcNormalPosition.bottom = boardY + winHeight;\r
873   SetWindowPlacement(hwndMain, &wp);\r
874 \r
875   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
876                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
877 \r
878   if (hwndConsole) {\r
879 #if AOT_CONSOLE\r
880     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
881                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
882 #endif\r
883     ShowWindow(hwndConsole, nCmdShow);\r
884   }\r
885   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
886   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
887 \r
888   return TRUE;\r
889 \r
890 }\r
891 \r
892 \r
893 typedef enum {\r
894   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
895   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
896   ArgSettingsFilename,\r
897   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
898 } ArgType;\r
899 \r
900 typedef struct {\r
901   char *argName;\r
902   ArgType argType;\r
903   /***\r
904   union {\r
905     String *pString;       // ArgString\r
906     int *pInt;             // ArgInt\r
907     float *pFloat;         // ArgFloat\r
908     Boolean *pBoolean;     // ArgBoolean\r
909     COLORREF *pColor;      // ArgColor\r
910     ColorClass cc;         // ArgAttribs\r
911     String *pFilename;     // ArgFilename\r
912     BoardSize *pBoardSize; // ArgBoardSize\r
913     int whichFont;         // ArgFont\r
914     DCB *pDCB;             // ArgCommSettings\r
915     String *pFilename;     // ArgSettingsFilename\r
916   } argLoc;\r
917   ***/\r
918   LPVOID argLoc;\r
919   BOOL save;\r
920 } ArgDescriptor;\r
921 \r
922 int junk;\r
923 ArgDescriptor argDescriptors[] = {\r
924   /* positional arguments */\r
925   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
926   { "", ArgNone, NULL },\r
927   /* keyword arguments */\r
928   JAWS_ARGS\r
929   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
930   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
931   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
932   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
933   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
934   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
935   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
936   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
937   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
938   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
939   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
940   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
941   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
942   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
943   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
944   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
945   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
946   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
947     FALSE },\r
948   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
949     FALSE },\r
950   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
951     FALSE },\r
952   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
953   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
954     FALSE },\r
955   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
956   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
957   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
958   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
959   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
960   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
961   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
962   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
963   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
964   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
965   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
966   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
967   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
968   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
969   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
970   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
971   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
972   /*!!bitmapDirectory?*/\r
973   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
974   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
975   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
976   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
977   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
978   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
979   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
980   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
981   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
982   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
983   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
984   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
985   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
986   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
987   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
988   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
989   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
990   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
991   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
992   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
993   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
994   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
995   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
996   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
997   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
998   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
999   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
1000   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
1001   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
1002   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
1003   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
1004   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
1005   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
1006   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1007   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1008   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
1009   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
1010   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
1011   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
1012   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1013   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1014   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1015   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1016   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1017   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1018   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1019   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1020   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
1021   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1022   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1023   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1024   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1025   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1026   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1027   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1028   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1029   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1030   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1031   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1032   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1033   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1034   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1035   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1036   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1037   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1038   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1039   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1040   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1041   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1042   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1043   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1044   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1045   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1046   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1047   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1048   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1049   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1050   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1051   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1052   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1053   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1054   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1055   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1056   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1057   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1058   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1059   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1060   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1061   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1062   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1063   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1064   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1065   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1066   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1067     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1068   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1069   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1070   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1071   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1072   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1073   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1074   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1075   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1076     TRUE }, /* must come after all fonts */\r
1077   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1078   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1079     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1080   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1081   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1082   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1083   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1084   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1085   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1086   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1087   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1088   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1089   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1090   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1091   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1092   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1093   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1094   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1095   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1096   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1097   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1098   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1099   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1100   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1101   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1102   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1103   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1104   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1105   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1106   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1107   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1108   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1109   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1110   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1111   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1112   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1113   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1114   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1115   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1116   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1117   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1118   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1119   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1120   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1121   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1122   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1123   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1124   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1125   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1126   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1127   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1128   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1129   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1130   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1131   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1132   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1133   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1134   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1135   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1136   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1137   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1138   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1139   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1140   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1141   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1142   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1143   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1144   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1145   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1146   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1147   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1148   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1149   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1150   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1151   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1152   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1153   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1154   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1155   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1156   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1157   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1158   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1159   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1160   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1161   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1162   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1163   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1164   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1165   { "highlightLastMove", ArgBoolean,\r
1166     (LPVOID) &appData.highlightLastMove, TRUE },\r
1167   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1168   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1169   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1170   { "highlightDragging", ArgBoolean,\r
1171     (LPVOID) &appData.highlightDragging, TRUE },\r
1172   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1173   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1174   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1175   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1176   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1177   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1178   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1179   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1180   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1181   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1182   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1183   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1184   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1185   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1186   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1187   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1188   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1189   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1190   { "soundShout", ArgFilename,\r
1191     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1192   { "soundSShout", ArgFilename,\r
1193     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1194   { "soundChannel1", ArgFilename,\r
1195     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1196   { "soundChannel", ArgFilename,\r
1197     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1198   { "soundKibitz", ArgFilename,\r
1199     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1200   { "soundTell", ArgFilename,\r
1201     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1202   { "soundChallenge", ArgFilename,\r
1203     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1204   { "soundRequest", ArgFilename,\r
1205     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1206   { "soundSeek", ArgFilename,\r
1207     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1208   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1209   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1210   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1211   { "soundIcsLoss", ArgFilename, \r
1212     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1213   { "soundIcsDraw", ArgFilename, \r
1214     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1215   { "soundIcsUnfinished", ArgFilename, \r
1216     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1217   { "soundIcsAlarm", ArgFilename, \r
1218     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1219   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1220   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1221   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1222   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1223   { "reuseChessPrograms", ArgBoolean,\r
1224     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1225   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1226   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1227   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1228   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1229   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1230   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1231   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1232   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1233   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1234   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1235   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1236   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1237   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1238   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1239   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1240     TRUE },\r
1241   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1242     TRUE },\r
1243   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1244   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1245   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1246   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1247   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1248   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1249   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1250   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1251   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1252   /* [AS] New features */\r
1253   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1254   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1255   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1256   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1257   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1258   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1259   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1260   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1261   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1262   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1263   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1264   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1265   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1266   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1267   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1268   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1269   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1270   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1271   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1272   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1273   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1274   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1275   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1276   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1277   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1278   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1279   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1280   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1281   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1282   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1283   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1284   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1285   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1286   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1287   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1288   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1289   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1290   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1291   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1292   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1293   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1294   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1295   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1296   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1297   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1298   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1299   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1300   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1301   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1302   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1303 \r
1304   /* [HGM] board-size, adjudication and misc. options */\r
1305   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1306   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1307   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1308   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1309   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1310   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1311   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1312   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1313   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1314   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1315   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1316   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1317   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1318   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1319   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1320   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1321   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1322   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1323   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1324   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1325   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1326   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1327   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1328   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1329   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1330   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1331   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1332   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1333   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1334   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1335   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1336   { "keepAlive", ArgInt, (LPVOID) &appData.keepAlive, FALSE },\r
1337   { "icstype", ArgInt, (LPVOID) &ics_type, FALSE },\r
1338   { "forceIllegalMoves", ArgTrue, (LPVOID) &appData.forceIllegal, FALSE },\r
1339 \r
1340 #ifdef ZIPPY\r
1341   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1342   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1343   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1344   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1345   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1346   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1347   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1348   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1349   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1350   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1351   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1352   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1353   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1354     FALSE },\r
1355   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1356   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1357   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1358   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1359   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1360   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1361   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1362     FALSE },\r
1363   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1364   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1365   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1366   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1367   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1368   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1369   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1370   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1371   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1372   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1373   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1374   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1375   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1376   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1377   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1378   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1379   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1380   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1381   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1382 #endif\r
1383   /* [HGM] options for broadcasting and time odds */\r
1384   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1385   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1386   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1387   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1388   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1389   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1390   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1391   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1392   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1393   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1394   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1395 \r
1396   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1397   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1398   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1399   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1400   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1401   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1402   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1403   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1404   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1405   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1406   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1407   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1408   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1409   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1410   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1411   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1412   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1413   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1414   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1415   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1416   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1417   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1418   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1419   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1420   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1421   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1422   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1423   /* [AS] Layout stuff */\r
1424   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1425   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1426   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1427   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1428   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1429 \r
1430   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1431   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1432   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1433   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1434   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1435 \r
1436   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1437   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1438   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1439   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1440   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1441 \r
1442   { NULL, ArgNone, NULL, FALSE }\r
1443 };\r
1444 \r
1445 \r
1446 /* Kludge for indirection files on command line */\r
1447 char* lastIndirectionFilename;\r
1448 ArgDescriptor argDescriptorIndirection =\r
1449 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1450 \r
1451 \r
1452 VOID\r
1453 ExitArgError(char *msg, char *badArg)\r
1454 {\r
1455   char buf[MSG_SIZ];\r
1456 \r
1457   sprintf(buf, "%s %s", msg, badArg);\r
1458   DisplayFatalError(buf, 0, 2);\r
1459   exit(2);\r
1460 }\r
1461 \r
1462 /* Command line font name parser.  NULL name means do nothing.\r
1463    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1464    For backward compatibility, syntax without the colon is also\r
1465    accepted, but font names with digits in them won't work in that case.\r
1466 */\r
1467 VOID\r
1468 ParseFontName(char *name, MyFontParams *mfp)\r
1469 {\r
1470   char *p, *q;\r
1471   if (name == NULL) return;\r
1472   p = name;\r
1473   q = strchr(p, ':');\r
1474   if (q) {\r
1475     if (q - p >= sizeof(mfp->faceName))\r
1476       ExitArgError("Font name too long:", name);\r
1477     memcpy(mfp->faceName, p, q - p);\r
1478     mfp->faceName[q - p] = NULLCHAR;\r
1479     p = q + 1;\r
1480   } else {\r
1481     q = mfp->faceName;\r
1482     while (*p && !isdigit(*p)) {\r
1483       *q++ = *p++;\r
1484       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1485         ExitArgError("Font name too long:", name);\r
1486     }\r
1487     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1488     *q = NULLCHAR;\r
1489   }\r
1490   if (!*p) ExitArgError("Font point size missing:", name);\r
1491   mfp->pointSize = (float) atof(p);\r
1492   mfp->bold = (strchr(p, 'b') != NULL);\r
1493   mfp->italic = (strchr(p, 'i') != NULL);\r
1494   mfp->underline = (strchr(p, 'u') != NULL);\r
1495   mfp->strikeout = (strchr(p, 's') != NULL);\r
1496 }\r
1497 \r
1498 /* Color name parser.\r
1499    X version accepts X color names, but this one\r
1500    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1501 COLORREF\r
1502 ParseColorName(char *name)\r
1503 {\r
1504   int red, green, blue, count;\r
1505   char buf[MSG_SIZ];\r
1506 \r
1507   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1508   if (count != 3) {\r
1509     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1510       &red, &green, &blue);\r
1511   }\r
1512   if (count != 3) {\r
1513     sprintf(buf, "Can't parse color name %s", name);\r
1514     DisplayError(buf, 0);\r
1515     return RGB(0, 0, 0);\r
1516   }\r
1517   return PALETTERGB(red, green, blue);\r
1518 }\r
1519 \r
1520 \r
1521 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1522 {\r
1523   char *e = argValue;\r
1524   int eff = 0;\r
1525 \r
1526   while (*e) {\r
1527     if (*e == 'b')      eff |= CFE_BOLD;\r
1528     else if (*e == 'i') eff |= CFE_ITALIC;\r
1529     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1530     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1531     else if (*e == '#' || isdigit(*e)) break;\r
1532     e++;\r
1533   }\r
1534   *effects = eff;\r
1535   *color   = ParseColorName(e);\r
1536 }\r
1537 \r
1538 \r
1539 BoardSize\r
1540 ParseBoardSize(char *name)\r
1541 {\r
1542   BoardSize bs = SizeTiny;\r
1543   while (sizeInfo[bs].name != NULL) {\r
1544     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1545     bs++;\r
1546   }\r
1547   ExitArgError("Unrecognized board size value", name);\r
1548   return bs; /* not reached */\r
1549 }\r
1550 \r
1551 \r
1552 char\r
1553 StringGet(void *getClosure)\r
1554 {\r
1555   char **p = (char **) getClosure;\r
1556   return *((*p)++);\r
1557 }\r
1558 \r
1559 char\r
1560 FileGet(void *getClosure)\r
1561 {\r
1562   int c;\r
1563   FILE* f = (FILE*) getClosure;\r
1564 \r
1565   c = getc(f);\r
1566   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1567   if (c == EOF)\r
1568     return NULLCHAR;\r
1569   else\r
1570     return (char) c;\r
1571 }\r
1572 \r
1573 /* Parse settings file named "name". If file found, return the\r
1574    full name in fullname and return TRUE; else return FALSE */\r
1575 BOOLEAN\r
1576 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1577 {\r
1578   char *dummy;\r
1579   FILE *f;\r
1580   int ok; char buf[MSG_SIZ];\r
1581 \r
1582   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1583   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1584     sprintf(buf, "%s.ini", name);\r
1585     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1586   }\r
1587   if (ok) {\r
1588     f = fopen(fullname, "r");\r
1589     if (f != NULL) {\r
1590       ParseArgs(FileGet, f);\r
1591       fclose(f);\r
1592       return TRUE;\r
1593     }\r
1594   }\r
1595   return FALSE;\r
1596 }\r
1597 \r
1598 VOID\r
1599 ParseArgs(GetFunc get, void *cl)\r
1600 {\r
1601   char argName[ARG_MAX];\r
1602   char argValue[ARG_MAX];\r
1603   ArgDescriptor *ad;\r
1604   char start;\r
1605   char *q;\r
1606   int i, octval;\r
1607   char ch;\r
1608   int posarg = 0;\r
1609 \r
1610   ch = get(cl);\r
1611   for (;;) {\r
1612     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1613     if (ch == NULLCHAR) break;\r
1614     if (ch == ';') {\r
1615       /* Comment to end of line */\r
1616       ch = get(cl);\r
1617       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1618       continue;\r
1619     } else if (ch == '/' || ch == '-') {\r
1620       /* Switch */\r
1621       q = argName;\r
1622       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1623              ch != '\n' && ch != '\t') {\r
1624         *q++ = ch;\r
1625         ch = get(cl);\r
1626       }\r
1627       *q = NULLCHAR;\r
1628 \r
1629       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1630         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1631 \r
1632       if (ad->argName == NULL)\r
1633         ExitArgError("Unrecognized argument", argName);\r
1634 \r
1635     } else if (ch == '@') {\r
1636       /* Indirection file */\r
1637       ad = &argDescriptorIndirection;\r
1638       ch = get(cl);\r
1639     } else {\r
1640       /* Positional argument */\r
1641       ad = &argDescriptors[posarg++];\r
1642       strcpy(argName, ad->argName);\r
1643     }\r
1644 \r
1645     if (ad->argType == ArgTrue) {\r
1646       *(Boolean *) ad->argLoc = TRUE;\r
1647       continue;\r
1648     }\r
1649     if (ad->argType == ArgFalse) {\r
1650       *(Boolean *) ad->argLoc = FALSE;\r
1651       continue;\r
1652     }\r
1653 \r
1654     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1655     if (ch == NULLCHAR || ch == '\n') {\r
1656       ExitArgError("No value provided for argument", argName);\r
1657     }\r
1658     q = argValue;\r
1659     if (ch == '{') {\r
1660       // Quoting with { }.  No characters have to (or can) be escaped.\r
1661       // Thus the string cannot contain a '}' character.\r
1662       start = ch;\r
1663       ch = get(cl);\r
1664       while (start) {\r
1665         switch (ch) {\r
1666         case NULLCHAR:\r
1667           start = NULLCHAR;\r
1668           break;\r
1669           \r
1670         case '}':\r
1671           ch = get(cl);\r
1672           start = NULLCHAR;\r
1673           break;\r
1674 \r
1675         default:\r
1676           *q++ = ch;\r
1677           ch = get(cl);\r
1678           break;\r
1679         }\r
1680       }   \r
1681     } else if (ch == '\'' || ch == '"') {\r
1682       // Quoting with ' ' or " ", with \ as escape character.\r
1683       // Inconvenient for long strings that may contain Windows filenames.\r
1684       start = ch;\r
1685       ch = get(cl);\r
1686       while (start) {\r
1687         switch (ch) {\r
1688         case NULLCHAR:\r
1689           start = NULLCHAR;\r
1690           break;\r
1691 \r
1692         default:\r
1693         not_special:\r
1694           *q++ = ch;\r
1695           ch = get(cl);\r
1696           break;\r
1697 \r
1698         case '\'':\r
1699         case '\"':\r
1700           if (ch == start) {\r
1701             ch = get(cl);\r
1702             start = NULLCHAR;\r
1703             break;\r
1704           } else {\r
1705             goto not_special;\r
1706           }\r
1707 \r
1708         case '\\':\r
1709           if (ad->argType == ArgFilename\r
1710               || ad->argType == ArgSettingsFilename) {\r
1711               goto not_special;\r
1712           }\r
1713           ch = get(cl);\r
1714           switch (ch) {\r
1715           case NULLCHAR:\r
1716             ExitArgError("Incomplete \\ escape in value for", argName);\r
1717             break;\r
1718           case 'n':\r
1719             *q++ = '\n';\r
1720             ch = get(cl);\r
1721             break;\r
1722           case 'r':\r
1723             *q++ = '\r';\r
1724             ch = get(cl);\r
1725             break;\r
1726           case 't':\r
1727             *q++ = '\t';\r
1728             ch = get(cl);\r
1729             break;\r
1730           case 'b':\r
1731             *q++ = '\b';\r
1732             ch = get(cl);\r
1733             break;\r
1734           case 'f':\r
1735             *q++ = '\f';\r
1736             ch = get(cl);\r
1737             break;\r
1738           default:\r
1739             octval = 0;\r
1740             for (i = 0; i < 3; i++) {\r
1741               if (ch >= '0' && ch <= '7') {\r
1742                 octval = octval*8 + (ch - '0');\r
1743                 ch = get(cl);\r
1744               } else {\r
1745                 break;\r
1746               }\r
1747             }\r
1748             if (i > 0) {\r
1749               *q++ = (char) octval;\r
1750             } else {\r
1751               *q++ = ch;\r
1752               ch = get(cl);\r
1753             }\r
1754             break;\r
1755           }\r
1756           break;\r
1757         }\r
1758       }\r
1759     } else {\r
1760       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1761         *q++ = ch;\r
1762         ch = get(cl);\r
1763       }\r
1764     }\r
1765     *q = NULLCHAR;\r
1766 \r
1767     switch (ad->argType) {\r
1768     case ArgInt:\r
1769       *(int *) ad->argLoc = atoi(argValue);\r
1770       break;\r
1771 \r
1772     case ArgX:\r
1773       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1774       break;\r
1775 \r
1776     case ArgY:\r
1777       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1778       break;\r
1779 \r
1780     case ArgZ:\r
1781       *(int *) ad->argLoc = atoi(argValue);\r
1782       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1783       break;\r
1784 \r
1785     case ArgFloat:\r
1786       *(float *) ad->argLoc = (float) atof(argValue);\r
1787       break;\r
1788 \r
1789     case ArgString:\r
1790     case ArgFilename:\r
1791       *(char **) ad->argLoc = strdup(argValue);\r
1792       break;\r
1793 \r
1794     case ArgSettingsFilename:\r
1795       {\r
1796         char fullname[MSG_SIZ];\r
1797         if (ParseSettingsFile(argValue, fullname)) {\r
1798           if (ad->argLoc != NULL) {\r
1799             *(char **) ad->argLoc = strdup(fullname);\r
1800           }\r
1801         } else {\r
1802           if (ad->argLoc != NULL) {\r
1803           } else {\r
1804             ExitArgError("Failed to open indirection file", argValue);\r
1805           }\r
1806         }\r
1807       }\r
1808       break;\r
1809 \r
1810     case ArgBoolean:\r
1811       switch (argValue[0]) {\r
1812       case 't':\r
1813       case 'T':\r
1814         *(Boolean *) ad->argLoc = TRUE;\r
1815         break;\r
1816       case 'f':\r
1817       case 'F':\r
1818         *(Boolean *) ad->argLoc = FALSE;\r
1819         break;\r
1820       default:\r
1821         ExitArgError("Unrecognized boolean argument value", argValue);\r
1822         break;\r
1823       }\r
1824       break;\r
1825 \r
1826     case ArgColor:\r
1827       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1828       break;\r
1829 \r
1830     case ArgAttribs: {\r
1831       ColorClass cc = (ColorClass)ad->argLoc;\r
1832       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1833       }\r
1834       break;\r
1835       \r
1836     case ArgBoardSize:\r
1837       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1838       break;\r
1839 \r
1840     case ArgFont:\r
1841       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1842       break;\r
1843 \r
1844     case ArgCommSettings:\r
1845       ParseCommSettings(argValue, &dcb);\r
1846       break;\r
1847 \r
1848     case ArgNone:\r
1849       ExitArgError("Unrecognized argument", argValue);\r
1850       break;\r
1851     case ArgTrue:\r
1852     case ArgFalse: ;\r
1853     }\r
1854   }\r
1855 }\r
1856 \r
1857 VOID\r
1858 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1859 {\r
1860   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1861   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1862   DeleteDC(hdc);\r
1863   lf->lfWidth = 0;\r
1864   lf->lfEscapement = 0;\r
1865   lf->lfOrientation = 0;\r
1866   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1867   lf->lfItalic = mfp->italic;\r
1868   lf->lfUnderline = mfp->underline;\r
1869   lf->lfStrikeOut = mfp->strikeout;\r
1870   lf->lfCharSet = DEFAULT_CHARSET;\r
1871   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1872   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1873   lf->lfQuality = DEFAULT_QUALITY;\r
1874   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1875   strcpy(lf->lfFaceName, mfp->faceName);\r
1876 }\r
1877 \r
1878 VOID\r
1879 CreateFontInMF(MyFont *mf)\r
1880 {\r
1881   LFfromMFP(&mf->lf, &mf->mfp);\r
1882   if (mf->hf) DeleteObject(mf->hf);\r
1883   mf->hf = CreateFontIndirect(&mf->lf);\r
1884 }\r
1885 \r
1886 VOID\r
1887 SetDefaultTextAttribs()\r
1888 {\r
1889   ColorClass cc;\r
1890   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1891     ParseAttribs(&textAttribs[cc].color, \r
1892                  &textAttribs[cc].effects, \r
1893                  defaultTextAttribs[cc]);\r
1894   }\r
1895 }\r
1896 \r
1897 VOID\r
1898 SetDefaultSounds()\r
1899 {\r
1900   ColorClass cc;\r
1901   SoundClass sc;\r
1902   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1903     textAttribs[cc].sound.name = strdup("");\r
1904     textAttribs[cc].sound.data = NULL;\r
1905   }\r
1906   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1907     sounds[sc].name = strdup("");\r
1908     sounds[sc].data = NULL;\r
1909   }\r
1910   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1911 }\r
1912 \r
1913 VOID\r
1914 LoadAllSounds()\r
1915 {\r
1916   ColorClass cc;\r
1917   SoundClass sc;\r
1918   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1919     MyLoadSound(&textAttribs[cc].sound);\r
1920   }\r
1921   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1922     MyLoadSound(&sounds[sc]);\r
1923   }\r
1924 }\r
1925 \r
1926 VOID\r
1927 InitAppData(LPSTR lpCmdLine)\r
1928 {\r
1929   int i, j;\r
1930   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1931   char *dummy, *p;\r
1932 \r
1933   programName = szAppName;\r
1934 \r
1935   /* Initialize to defaults */\r
1936   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1937   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1938   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1939   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1940   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1941   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1942   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1943   SetDefaultTextAttribs();\r
1944   SetDefaultSounds();\r
1945   appData.movesPerSession = MOVES_PER_SESSION;\r
1946   appData.initString = INIT_STRING;\r
1947   appData.secondInitString = INIT_STRING;\r
1948   appData.firstComputerString = COMPUTER_STRING;\r
1949   appData.secondComputerString = COMPUTER_STRING;\r
1950   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1951   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1952   appData.firstPlaysBlack = FALSE;\r
1953   appData.noChessProgram = FALSE;\r
1954   chessProgram = FALSE;\r
1955   appData.firstHost = FIRST_HOST;\r
1956   appData.secondHost = SECOND_HOST;\r
1957   appData.firstDirectory = FIRST_DIRECTORY;\r
1958   appData.secondDirectory = SECOND_DIRECTORY;\r
1959   appData.bitmapDirectory = "";\r
1960   appData.remoteShell = REMOTE_SHELL;\r
1961   appData.remoteUser = "";\r
1962   appData.timeDelay = TIME_DELAY;\r
1963   appData.timeControl = TIME_CONTROL;\r
1964   appData.timeIncrement = TIME_INCREMENT;\r
1965   appData.icsActive = FALSE;\r
1966   appData.icsHost = "";\r
1967   appData.icsPort = ICS_PORT;\r
1968   appData.icsCommPort = ICS_COMM_PORT;\r
1969   appData.icsLogon = ICS_LOGON;\r
1970   appData.icsHelper = "";\r
1971   appData.useTelnet = FALSE;\r
1972   appData.telnetProgram = TELNET_PROGRAM;\r
1973   appData.gateway = "";\r
1974   appData.loadGameFile = "";\r
1975   appData.loadGameIndex = 0;\r
1976   appData.saveGameFile = "";\r
1977   appData.autoSaveGames = FALSE;\r
1978   appData.loadPositionFile = "";\r
1979   appData.loadPositionIndex = 1;\r
1980   appData.savePositionFile = "";\r
1981   appData.matchMode = FALSE;\r
1982   appData.matchGames = 0;\r
1983   appData.monoMode = FALSE;\r
1984   appData.debugMode = FALSE;\r
1985   appData.clockMode = TRUE;\r
1986   boardSize = (BoardSize) -1; /* determine by screen size */\r
1987   appData.Iconic = FALSE; /*unused*/\r
1988   appData.searchTime = "";\r
1989   appData.searchDepth = 0;\r
1990   appData.showCoords = FALSE;\r
1991   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1992   appData.autoCallFlag = FALSE;\r
1993   appData.flipView = FALSE;\r
1994   appData.autoFlipView = TRUE;\r
1995   appData.cmailGameName = "";\r
1996   appData.alwaysPromoteToQueen = FALSE;\r
1997   appData.oldSaveStyle = FALSE;\r
1998   appData.quietPlay = FALSE;\r
1999   appData.showThinking = FALSE;\r
2000   appData.ponderNextMove = TRUE;\r
2001   appData.periodicUpdates = TRUE;\r
2002   appData.popupExitMessage = TRUE;\r
2003   appData.popupMoveErrors = FALSE;\r
2004   appData.autoObserve = FALSE;\r
2005   appData.autoComment = FALSE;\r
2006   appData.animate = TRUE;\r
2007   appData.animSpeed = 10;\r
2008   appData.animateDragging = TRUE;\r
2009   appData.highlightLastMove = TRUE;\r
2010   appData.getMoveList = TRUE;\r
2011   appData.testLegality = TRUE;\r
2012   appData.premove = TRUE;\r
2013   appData.premoveWhite = FALSE;\r
2014   appData.premoveWhiteText = "";\r
2015   appData.premoveBlack = FALSE;\r
2016   appData.premoveBlackText = "";\r
2017   appData.icsAlarm = TRUE;\r
2018   appData.icsAlarmTime = 5000;\r
2019   appData.autoRaiseBoard = TRUE;\r
2020   appData.localLineEditing = TRUE;\r
2021   appData.colorize = TRUE;\r
2022   appData.reuseFirst = TRUE;\r
2023   appData.reuseSecond = TRUE;\r
2024   appData.blindfold = FALSE;\r
2025   appData.icsEngineAnalyze = FALSE;\r
2026   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2027   dcb.DCBlength = sizeof(DCB);\r
2028   dcb.BaudRate = 9600;\r
2029   dcb.fBinary = TRUE;\r
2030   dcb.fParity = FALSE;\r
2031   dcb.fOutxCtsFlow = FALSE;\r
2032   dcb.fOutxDsrFlow = FALSE;\r
2033   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2034   dcb.fDsrSensitivity = FALSE;\r
2035   dcb.fTXContinueOnXoff = TRUE;\r
2036   dcb.fOutX = FALSE;\r
2037   dcb.fInX = FALSE;\r
2038   dcb.fNull = FALSE;\r
2039   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2040   dcb.fAbortOnError = FALSE;\r
2041   dcb.ByteSize = 7;\r
2042   dcb.Parity = SPACEPARITY;\r
2043   dcb.StopBits = ONESTOPBIT;\r
2044   settingsFileName = SETTINGS_FILE;\r
2045   saveSettingsOnExit = TRUE;\r
2046   boardX = CW_USEDEFAULT;\r
2047   boardY = CW_USEDEFAULT;\r
2048   analysisX = CW_USEDEFAULT; \r
2049   analysisY = CW_USEDEFAULT; \r
2050   analysisW = CW_USEDEFAULT;\r
2051   analysisH = CW_USEDEFAULT;\r
2052   commentX = CW_USEDEFAULT; \r
2053   commentY = CW_USEDEFAULT; \r
2054   commentW = CW_USEDEFAULT;\r
2055   commentH = CW_USEDEFAULT;\r
2056   editTagsX = CW_USEDEFAULT; \r
2057   editTagsY = CW_USEDEFAULT; \r
2058   editTagsW = CW_USEDEFAULT;\r
2059   editTagsH = CW_USEDEFAULT;\r
2060   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2061   icsNames = ICS_NAMES;\r
2062   firstChessProgramNames = FCP_NAMES;\r
2063   secondChessProgramNames = SCP_NAMES;\r
2064   appData.initialMode = "";\r
2065   appData.variant = "normal";\r
2066   appData.firstProtocolVersion = PROTOVER;\r
2067   appData.secondProtocolVersion = PROTOVER;\r
2068   appData.showButtonBar = TRUE;\r
2069 \r
2070    /* [AS] New properties (see comments in header file) */\r
2071   appData.firstScoreIsAbsolute = FALSE;\r
2072   appData.secondScoreIsAbsolute = FALSE;\r
2073   appData.saveExtendedInfoInPGN = FALSE;\r
2074   appData.hideThinkingFromHuman = FALSE;\r
2075   appData.liteBackTextureFile = "";\r
2076   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2077   appData.darkBackTextureFile = "";\r
2078   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2079   appData.renderPiecesWithFont = "";\r
2080   appData.fontToPieceTable = "";\r
2081   appData.fontBackColorWhite = 0;\r
2082   appData.fontForeColorWhite = 0;\r
2083   appData.fontBackColorBlack = 0;\r
2084   appData.fontForeColorBlack = 0;\r
2085   appData.fontPieceSize = 80;\r
2086   appData.overrideLineGap = 1;\r
2087   appData.adjudicateLossThreshold = 0;\r
2088   appData.delayBeforeQuit = 0;\r
2089   appData.delayAfterQuit = 0;\r
2090   appData.nameOfDebugFile = "winboard.debug";\r
2091   appData.pgnEventHeader = "Computer Chess Game";\r
2092   appData.defaultFrcPosition = -1;\r
2093   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2094   appData.saveOutOfBookInfo = TRUE;\r
2095   appData.showEvalInMoveHistory = TRUE;\r
2096   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2097   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2098   appData.highlightMoveWithArrow = FALSE;\r
2099   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2100   appData.useStickyWindows = TRUE;\r
2101   appData.adjudicateDrawMoves = 0;\r
2102   appData.autoDisplayComment = TRUE;\r
2103   appData.autoDisplayTags = TRUE;\r
2104   appData.firstIsUCI = FALSE;\r
2105   appData.secondIsUCI = FALSE;\r
2106   appData.firstHasOwnBookUCI = TRUE;\r
2107   appData.secondHasOwnBookUCI = TRUE;\r
2108   appData.polyglotDir = "";\r
2109   appData.usePolyglotBook = FALSE;\r
2110   appData.polyglotBook = "";\r
2111   appData.defaultHashSize = 64;\r
2112   appData.defaultCacheSizeEGTB = 4;\r
2113   appData.defaultPathEGTB = "c:\\egtb";\r
2114   appData.firstOptions = "";\r
2115   appData.secondOptions = "";\r
2116 \r
2117   InitWindowPlacement( &wpGameList );\r
2118   InitWindowPlacement( &wpMoveHistory );\r
2119   InitWindowPlacement( &wpEvalGraph );\r
2120   InitWindowPlacement( &wpEngineOutput );\r
2121   InitWindowPlacement( &wpConsole );\r
2122 \r
2123   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2124   appData.NrFiles      = -1;\r
2125   appData.NrRanks      = -1;\r
2126   appData.holdingsSize = -1;\r
2127   appData.testClaims   = FALSE;\r
2128   appData.checkMates   = FALSE;\r
2129   appData.materialDraws= FALSE;\r
2130   appData.trivialDraws = FALSE;\r
2131   appData.ruleMoves    = 51;\r
2132   appData.drawRepeats  = 6;\r
2133   appData.matchPause   = 10000;\r
2134   appData.alphaRank    = FALSE;\r
2135   appData.allWhite     = FALSE;\r
2136   appData.upsideDown   = FALSE;\r
2137   appData.serverPause  = 15;\r
2138   appData.serverMovesName   = NULL;\r
2139   appData.suppressLoadMoves = FALSE;\r
2140   appData.firstTimeOdds  = 1;\r
2141   appData.secondTimeOdds = 1;\r
2142   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2143   appData.secondAccumulateTC = 1;\r
2144   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2145   appData.secondNPS = -1;\r
2146   appData.engineComments = 1;\r
2147   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2148   appData.egtFormats = "";\r
2149 \r
2150 #ifdef ZIPPY\r
2151   appData.zippyTalk = ZIPPY_TALK;\r
2152   appData.zippyPlay = ZIPPY_PLAY;\r
2153   appData.zippyLines = ZIPPY_LINES;\r
2154   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2155   appData.zippyPassword = ZIPPY_PASSWORD;\r
2156   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2157   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2158   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2159   appData.zippyUseI = ZIPPY_USE_I;\r
2160   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2161   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2162   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2163   appData.zippyGameStart = ZIPPY_GAME_START;\r
2164   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2165   appData.zippyAbort = ZIPPY_ABORT;\r
2166   appData.zippyVariants = ZIPPY_VARIANTS;\r
2167   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2168   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2169 #endif\r
2170 \r
2171   /* Point font array elements to structures and\r
2172      parse default font names */\r
2173   for (i=0; i<NUM_FONTS; i++) {\r
2174     for (j=0; j<NUM_SIZES; j++) {\r
2175       font[j][i] = &fontRec[j][i];\r
2176       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2177     }\r
2178   }\r
2179   \r
2180   /* Parse default settings file if any */\r
2181   if (ParseSettingsFile(settingsFileName, buf)) {\r
2182     settingsFileName = strdup(buf);\r
2183   }\r
2184 \r
2185   /* Parse command line */\r
2186   ParseArgs(StringGet, &lpCmdLine);\r
2187 \r
2188   /* [HGM] make sure board size is acceptable */\r
2189   if(appData.NrFiles > BOARD_SIZE ||\r
2190      appData.NrRanks > BOARD_SIZE   )\r
2191       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2192 \r
2193   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2194    * with options from the command line, we now make an even higher priority\r
2195    * overrule by WB options attached to the engine command line. This so that\r
2196    * tournament managers can use WB options (such as /timeOdds) that follow\r
2197    * the engines.\r
2198    */\r
2199   if(appData.firstChessProgram != NULL) {\r
2200       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2201       static char *f = "first";\r
2202       char buf[MSG_SIZ], *q = buf;\r
2203       if(p != NULL) { // engine command line contains WinBoard options\r
2204           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2205           ParseArgs(StringGet, &q);\r
2206           p[-1] = 0; // cut them offengine command line\r
2207       }\r
2208   }\r
2209   // now do same for second chess program\r
2210   if(appData.secondChessProgram != NULL) {\r
2211       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2212       static char *s = "second";\r
2213       char buf[MSG_SIZ], *q = buf;\r
2214       if(p != NULL) { // engine command line contains WinBoard options\r
2215           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2216           ParseArgs(StringGet, &q);\r
2217           p[-1] = 0; // cut them offengine command line\r
2218       }\r
2219   }\r
2220 \r
2221 \r
2222   /* Propagate options that affect others */\r
2223   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2224   if (appData.icsActive || appData.noChessProgram) {\r
2225      chessProgram = FALSE;  /* not local chess program mode */\r
2226   }\r
2227 \r
2228   /* Open startup dialog if needed */\r
2229   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2230       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2231       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2232                         *appData.secondChessProgram == NULLCHAR))) {\r
2233     FARPROC lpProc;\r
2234     \r
2235     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2236     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2237     FreeProcInstance(lpProc);\r
2238   }\r
2239 \r
2240   /* Make sure save files land in the right (?) directory */\r
2241   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2242     appData.saveGameFile = strdup(buf);\r
2243   }\r
2244   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2245     appData.savePositionFile = strdup(buf);\r
2246   }\r
2247 \r
2248   /* Finish initialization for fonts and sounds */\r
2249   for (i=0; i<NUM_FONTS; i++) {\r
2250     for (j=0; j<NUM_SIZES; j++) {\r
2251       CreateFontInMF(font[j][i]);\r
2252     }\r
2253   }\r
2254   /* xboard, and older WinBoards, controlled the move sound with the\r
2255      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2256      always turn the option on (so that the backend will call us),\r
2257      then let the user turn the sound off by setting it to silence if\r
2258      desired.  To accommodate old winboard.ini files saved by old\r
2259      versions of WinBoard, we also turn off the sound if the option\r
2260      was initially set to false. */\r
2261   if (!appData.ringBellAfterMoves) {\r
2262     sounds[(int)SoundMove].name = strdup("");\r
2263     appData.ringBellAfterMoves = TRUE;\r
2264   }\r
2265   GetCurrentDirectory(MSG_SIZ, currDir);\r
2266   SetCurrentDirectory(installDir);\r
2267   LoadAllSounds();\r
2268   SetCurrentDirectory(currDir);\r
2269 \r
2270   p = icsTextMenuString;\r
2271   if (p[0] == '@') {\r
2272     FILE* f = fopen(p + 1, "r");\r
2273     if (f == NULL) {\r
2274       DisplayFatalError(p + 1, errno, 2);\r
2275       return;\r
2276     }\r
2277     i = fread(buf, 1, sizeof(buf)-1, f);\r
2278     fclose(f);\r
2279     buf[i] = NULLCHAR;\r
2280     p = buf;\r
2281   }\r
2282   ParseIcsTextMenu(strdup(p));\r
2283 }\r
2284 \r
2285 \r
2286 VOID\r
2287 InitMenuChecks()\r
2288 {\r
2289   HMENU hmenu = GetMenu(hwndMain);\r
2290 \r
2291   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2292                         MF_BYCOMMAND|((appData.icsActive &&\r
2293                                        *appData.icsCommPort != NULLCHAR) ?\r
2294                                       MF_ENABLED : MF_GRAYED));\r
2295   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2296                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2297                                      MF_CHECKED : MF_UNCHECKED));\r
2298 }\r
2299 \r
2300 \r
2301 VOID\r
2302 SaveSettings(char* name)\r
2303 {\r
2304   FILE *f;\r
2305   ArgDescriptor *ad;\r
2306   WINDOWPLACEMENT wp;\r
2307   char dir[MSG_SIZ];\r
2308 \r
2309   if (!hwndMain) return;\r
2310 \r
2311   GetCurrentDirectory(MSG_SIZ, dir);\r
2312   SetCurrentDirectory(installDir);\r
2313   f = fopen(name, "w");\r
2314   SetCurrentDirectory(dir);\r
2315   if (f == NULL) {\r
2316     DisplayError(name, errno);\r
2317     return;\r
2318   }\r
2319   fprintf(f, ";\n");\r
2320   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2321   fprintf(f, ";\n");\r
2322   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2323   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2324   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2325   fprintf(f, ";\n");\r
2326 \r
2327   wp.length = sizeof(WINDOWPLACEMENT);\r
2328   GetWindowPlacement(hwndMain, &wp);\r
2329   boardX = wp.rcNormalPosition.left;\r
2330   boardY = wp.rcNormalPosition.top;\r
2331 \r
2332   if (hwndConsole) {\r
2333     GetWindowPlacement(hwndConsole, &wp);\r
2334     wpConsole.x = wp.rcNormalPosition.left;\r
2335     wpConsole.y = wp.rcNormalPosition.top;\r
2336     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2337     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2338   }\r
2339 \r
2340   if (analysisDialog) {\r
2341     GetWindowPlacement(analysisDialog, &wp);\r
2342     analysisX = wp.rcNormalPosition.left;\r
2343     analysisY = wp.rcNormalPosition.top;\r
2344     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2345     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2346   }\r
2347 \r
2348   if (commentDialog) {\r
2349     GetWindowPlacement(commentDialog, &wp);\r
2350     commentX = wp.rcNormalPosition.left;\r
2351     commentY = wp.rcNormalPosition.top;\r
2352     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2353     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2354   }\r
2355 \r
2356   if (editTagsDialog) {\r
2357     GetWindowPlacement(editTagsDialog, &wp);\r
2358     editTagsX = wp.rcNormalPosition.left;\r
2359     editTagsY = wp.rcNormalPosition.top;\r
2360     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2361     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2362   }\r
2363 \r
2364   if (gameListDialog) {\r
2365     GetWindowPlacement(gameListDialog, &wp);\r
2366     wpGameList.x = wp.rcNormalPosition.left;\r
2367     wpGameList.y = wp.rcNormalPosition.top;\r
2368     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2369     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2370   }\r
2371 \r
2372   /* [AS] Move history */\r
2373   wpMoveHistory.visible = MoveHistoryIsUp();\r
2374   \r
2375   if( moveHistoryDialog ) {\r
2376     GetWindowPlacement(moveHistoryDialog, &wp);\r
2377     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2378     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2379     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2380     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2381   }\r
2382 \r
2383   /* [AS] Eval graph */\r
2384   wpEvalGraph.visible = EvalGraphIsUp();\r
2385 \r
2386   if( evalGraphDialog ) {\r
2387     GetWindowPlacement(evalGraphDialog, &wp);\r
2388     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2389     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2390     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2391     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2392   }\r
2393 \r
2394   /* [AS] Engine output */\r
2395   wpEngineOutput.visible = EngineOutputIsUp();\r
2396 \r
2397   if( engineOutputDialog ) {\r
2398     GetWindowPlacement(engineOutputDialog, &wp);\r
2399     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2400     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2401     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2402     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2403   }\r
2404 \r
2405   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2406     if (!ad->save) continue;\r
2407     switch (ad->argType) {\r
2408     case ArgString:\r
2409       {\r
2410         char *p = *(char **)ad->argLoc;\r
2411         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2412           /* Quote multiline values or \-containing values\r
2413              with { } if possible */\r
2414           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2415         } else {\r
2416           /* Else quote with " " */\r
2417           fprintf(f, "/%s=\"", ad->argName);\r
2418           while (*p) {\r
2419             if (*p == '\n') fprintf(f, "\n");\r
2420             else if (*p == '\r') fprintf(f, "\\r");\r
2421             else if (*p == '\t') fprintf(f, "\\t");\r
2422             else if (*p == '\b') fprintf(f, "\\b");\r
2423             else if (*p == '\f') fprintf(f, "\\f");\r
2424             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2425             else if (*p == '\"') fprintf(f, "\\\"");\r
2426             else if (*p == '\\') fprintf(f, "\\\\");\r
2427             else putc(*p, f);\r
2428             p++;\r
2429           }\r
2430           fprintf(f, "\"\n");\r
2431         }\r
2432       }\r
2433       break;\r
2434     case ArgInt:\r
2435     case ArgZ:\r
2436       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2437       break;\r
2438     case ArgX:\r
2439       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2440       break;\r
2441     case ArgY:\r
2442       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2443       break;\r
2444     case ArgFloat:\r
2445       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2446       break;\r
2447     case ArgBoolean:\r
2448       fprintf(f, "/%s=%s\n", ad->argName, \r
2449         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2450       break;\r
2451     case ArgTrue:\r
2452       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2453       break;\r
2454     case ArgFalse:\r
2455       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2456       break;\r
2457     case ArgColor:\r
2458       {\r
2459         COLORREF color = *(COLORREF *)ad->argLoc;\r
2460         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2461           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2462       }\r
2463       break;\r
2464     case ArgAttribs:\r
2465       {\r
2466         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2467         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2468           (ta->effects & CFE_BOLD) ? "b" : "",\r
2469           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2470           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2471           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2472           (ta->effects) ? " " : "",\r
2473           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2474       }\r
2475       break;\r
2476     case ArgFilename:\r
2477       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2478         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2479       } else {\r
2480         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2481       }\r
2482       break;\r
2483     case ArgBoardSize:\r
2484       fprintf(f, "/%s=%s\n", ad->argName,\r
2485               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2486       break;\r
2487     case ArgFont:\r
2488       {\r
2489         int bs;\r
2490         for (bs=0; bs<NUM_SIZES; bs++) {\r
2491           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2492           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2493           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2494             ad->argName, mfp->faceName, mfp->pointSize,\r
2495             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2496             mfp->bold ? "b" : "",\r
2497             mfp->italic ? "i" : "",\r
2498             mfp->underline ? "u" : "",\r
2499             mfp->strikeout ? "s" : "");\r
2500         }\r
2501       }\r
2502       break;\r
2503     case ArgCommSettings:\r
2504       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2505     case ArgNone:\r
2506     case ArgSettingsFilename: ;\r
2507     }\r
2508   }\r
2509   fclose(f);\r
2510 }\r
2511 \r
2512 \r
2513 \r
2514 /*---------------------------------------------------------------------------*\\r
2515  *\r
2516  * GDI board drawing routines\r
2517  *\r
2518 \*---------------------------------------------------------------------------*/\r
2519 \r
2520 /* [AS] Draw square using background texture */\r
2521 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2522 {\r
2523     XFORM   x;\r
2524 \r
2525     if( mode == 0 ) {\r
2526         return; /* Should never happen! */\r
2527     }\r
2528 \r
2529     SetGraphicsMode( dst, GM_ADVANCED );\r
2530 \r
2531     switch( mode ) {\r
2532     case 1:\r
2533         /* Identity */\r
2534         break;\r
2535     case 2:\r
2536         /* X reflection */\r
2537         x.eM11 = -1.0;\r
2538         x.eM12 = 0;\r
2539         x.eM21 = 0;\r
2540         x.eM22 = 1.0;\r
2541         x.eDx = (FLOAT) dw + dx - 1;\r
2542         x.eDy = 0;\r
2543         dx = 0;\r
2544         SetWorldTransform( dst, &x );\r
2545         break;\r
2546     case 3:\r
2547         /* Y reflection */\r
2548         x.eM11 = 1.0;\r
2549         x.eM12 = 0;\r
2550         x.eM21 = 0;\r
2551         x.eM22 = -1.0;\r
2552         x.eDx = 0;\r
2553         x.eDy = (FLOAT) dh + dy - 1;\r
2554         dy = 0;\r
2555         SetWorldTransform( dst, &x );\r
2556         break;\r
2557     case 4:\r
2558         /* X/Y flip */\r
2559         x.eM11 = 0;\r
2560         x.eM12 = 1.0;\r
2561         x.eM21 = 1.0;\r
2562         x.eM22 = 0;\r
2563         x.eDx = (FLOAT) dx;\r
2564         x.eDy = (FLOAT) dy;\r
2565         dx = 0;\r
2566         dy = 0;\r
2567         SetWorldTransform( dst, &x );\r
2568         break;\r
2569     }\r
2570 \r
2571     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2572 \r
2573     x.eM11 = 1.0;\r
2574     x.eM12 = 0;\r
2575     x.eM21 = 0;\r
2576     x.eM22 = 1.0;\r
2577     x.eDx = 0;\r
2578     x.eDy = 0;\r
2579     SetWorldTransform( dst, &x );\r
2580 \r
2581     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2582 }\r
2583 \r
2584 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2585 enum {\r
2586     PM_WP = (int) WhitePawn, \r
2587     PM_WN = (int) WhiteKnight, \r
2588     PM_WB = (int) WhiteBishop, \r
2589     PM_WR = (int) WhiteRook, \r
2590     PM_WQ = (int) WhiteQueen, \r
2591     PM_WF = (int) WhiteFerz, \r
2592     PM_WW = (int) WhiteWazir, \r
2593     PM_WE = (int) WhiteAlfil, \r
2594     PM_WM = (int) WhiteMan, \r
2595     PM_WO = (int) WhiteCannon, \r
2596     PM_WU = (int) WhiteUnicorn, \r
2597     PM_WH = (int) WhiteNightrider, \r
2598     PM_WA = (int) WhiteAngel, \r
2599     PM_WC = (int) WhiteMarshall, \r
2600     PM_WAB = (int) WhiteCardinal, \r
2601     PM_WD = (int) WhiteDragon, \r
2602     PM_WL = (int) WhiteLance, \r
2603     PM_WS = (int) WhiteCobra, \r
2604     PM_WV = (int) WhiteFalcon, \r
2605     PM_WSG = (int) WhiteSilver, \r
2606     PM_WG = (int) WhiteGrasshopper, \r
2607     PM_WK = (int) WhiteKing,\r
2608     PM_BP = (int) BlackPawn, \r
2609     PM_BN = (int) BlackKnight, \r
2610     PM_BB = (int) BlackBishop, \r
2611     PM_BR = (int) BlackRook, \r
2612     PM_BQ = (int) BlackQueen, \r
2613     PM_BF = (int) BlackFerz, \r
2614     PM_BW = (int) BlackWazir, \r
2615     PM_BE = (int) BlackAlfil, \r
2616     PM_BM = (int) BlackMan,\r
2617     PM_BO = (int) BlackCannon, \r
2618     PM_BU = (int) BlackUnicorn, \r
2619     PM_BH = (int) BlackNightrider, \r
2620     PM_BA = (int) BlackAngel, \r
2621     PM_BC = (int) BlackMarshall, \r
2622     PM_BG = (int) BlackGrasshopper, \r
2623     PM_BAB = (int) BlackCardinal,\r
2624     PM_BD = (int) BlackDragon,\r
2625     PM_BL = (int) BlackLance,\r
2626     PM_BS = (int) BlackCobra,\r
2627     PM_BV = (int) BlackFalcon,\r
2628     PM_BSG = (int) BlackSilver,\r
2629     PM_BK = (int) BlackKing\r
2630 };\r
2631 \r
2632 static HFONT hPieceFont = NULL;\r
2633 static HBITMAP hPieceMask[(int) EmptySquare];\r
2634 static HBITMAP hPieceFace[(int) EmptySquare];\r
2635 static int fontBitmapSquareSize = 0;\r
2636 static char pieceToFontChar[(int) EmptySquare] =\r
2637                               { 'p', 'n', 'b', 'r', 'q', \r
2638                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2639                       'k', 'o', 'm', 'v', 't', 'w', \r
2640                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2641                                                               'l' };\r
2642 \r
2643 extern BOOL SetCharTable( char *table, const char * map );\r
2644 /* [HGM] moved to backend.c */\r
2645 \r
2646 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2647 {\r
2648     HBRUSH hbrush;\r
2649     BYTE r1 = GetRValue( color );\r
2650     BYTE g1 = GetGValue( color );\r
2651     BYTE b1 = GetBValue( color );\r
2652     BYTE r2 = r1 / 2;\r
2653     BYTE g2 = g1 / 2;\r
2654     BYTE b2 = b1 / 2;\r
2655     RECT rc;\r
2656 \r
2657     /* Create a uniform background first */\r
2658     hbrush = CreateSolidBrush( color );\r
2659     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2660     FillRect( hdc, &rc, hbrush );\r
2661     DeleteObject( hbrush );\r
2662     \r
2663     if( mode == 1 ) {\r
2664         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2665         int steps = squareSize / 2;\r
2666         int i;\r
2667 \r
2668         for( i=0; i<steps; i++ ) {\r
2669             BYTE r = r1 - (r1-r2) * i / steps;\r
2670             BYTE g = g1 - (g1-g2) * i / steps;\r
2671             BYTE b = b1 - (b1-b2) * i / steps;\r
2672 \r
2673             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2674             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2675             FillRect( hdc, &rc, hbrush );\r
2676             DeleteObject(hbrush);\r
2677         }\r
2678     }\r
2679     else if( mode == 2 ) {\r
2680         /* Diagonal gradient, good more or less for every piece */\r
2681         POINT triangle[3];\r
2682         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2683         HBRUSH hbrush_old;\r
2684         int steps = squareSize;\r
2685         int i;\r
2686 \r
2687         triangle[0].x = squareSize - steps;\r
2688         triangle[0].y = squareSize;\r
2689         triangle[1].x = squareSize;\r
2690         triangle[1].y = squareSize;\r
2691         triangle[2].x = squareSize;\r
2692         triangle[2].y = squareSize - steps;\r
2693 \r
2694         for( i=0; i<steps; i++ ) {\r
2695             BYTE r = r1 - (r1-r2) * i / steps;\r
2696             BYTE g = g1 - (g1-g2) * i / steps;\r
2697             BYTE b = b1 - (b1-b2) * i / steps;\r
2698 \r
2699             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2700             hbrush_old = SelectObject( hdc, hbrush );\r
2701             Polygon( hdc, triangle, 3 );\r
2702             SelectObject( hdc, hbrush_old );\r
2703             DeleteObject(hbrush);\r
2704             triangle[0].x++;\r
2705             triangle[2].y++;\r
2706         }\r
2707 \r
2708         SelectObject( hdc, hpen );\r
2709     }\r
2710 }\r
2711 \r
2712 /*\r
2713     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2714     seems to work ok. The main problem here is to find the "inside" of a chess\r
2715     piece: follow the steps as explained below.\r
2716 */\r
2717 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2718 {\r
2719     HBITMAP hbm;\r
2720     HBITMAP hbm_old;\r
2721     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2722     RECT rc;\r
2723     SIZE sz;\r
2724     POINT pt;\r
2725     int backColor = whitePieceColor; \r
2726     int foreColor = blackPieceColor;\r
2727     \r
2728     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2729         backColor = appData.fontBackColorWhite;\r
2730         foreColor = appData.fontForeColorWhite;\r
2731     }\r
2732     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2733         backColor = appData.fontBackColorBlack;\r
2734         foreColor = appData.fontForeColorBlack;\r
2735     }\r
2736 \r
2737     /* Mask */\r
2738     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2739 \r
2740     hbm_old = SelectObject( hdc, hbm );\r
2741 \r
2742     rc.left = 0;\r
2743     rc.top = 0;\r
2744     rc.right = squareSize;\r
2745     rc.bottom = squareSize;\r
2746 \r
2747     /* Step 1: background is now black */\r
2748     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2749 \r
2750     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2751 \r
2752     pt.x = (squareSize - sz.cx) / 2;\r
2753     pt.y = (squareSize - sz.cy) / 2;\r
2754 \r
2755     SetBkMode( hdc, TRANSPARENT );\r
2756     SetTextColor( hdc, chroma );\r
2757     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2758     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2759 \r
2760     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2761     /* Step 3: the area outside the piece is filled with white */\r
2762 //    FloodFill( hdc, 0, 0, chroma );\r
2763     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2764     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2765     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2766     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2767     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2768     /* \r
2769         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2770         but if the start point is not inside the piece we're lost!\r
2771         There should be a better way to do this... if we could create a region or path\r
2772         from the fill operation we would be fine for example.\r
2773     */\r
2774 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2775     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2776 \r
2777     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2778         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2779         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2780 \r
2781         SelectObject( dc2, bm2 );\r
2782         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2783         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2784         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2785         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2786         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2787 \r
2788         DeleteDC( dc2 );\r
2789         DeleteObject( bm2 );\r
2790     }\r
2791 \r
2792     SetTextColor( hdc, 0 );\r
2793     /* \r
2794         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2795         draw the piece again in black for safety.\r
2796     */\r
2797     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2798 \r
2799     SelectObject( hdc, hbm_old );\r
2800 \r
2801     if( hPieceMask[index] != NULL ) {\r
2802         DeleteObject( hPieceMask[index] );\r
2803     }\r
2804 \r
2805     hPieceMask[index] = hbm;\r
2806 \r
2807     /* Face */\r
2808     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2809 \r
2810     SelectObject( hdc, hbm );\r
2811 \r
2812     {\r
2813         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2814         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2815         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2816 \r
2817         SelectObject( dc1, hPieceMask[index] );\r
2818         SelectObject( dc2, bm2 );\r
2819         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2820         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2821         \r
2822         /* \r
2823             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2824             the piece background and deletes (makes transparent) the rest.\r
2825             Thanks to that mask, we are free to paint the background with the greates\r
2826             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2827             We use this, to make gradients and give the pieces a "roundish" look.\r
2828         */\r
2829         SetPieceBackground( hdc, backColor, 2 );\r
2830         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2831 \r
2832         DeleteDC( dc2 );\r
2833         DeleteDC( dc1 );\r
2834         DeleteObject( bm2 );\r
2835     }\r
2836 \r
2837     SetTextColor( hdc, foreColor );\r
2838     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2839 \r
2840     SelectObject( hdc, hbm_old );\r
2841 \r
2842     if( hPieceFace[index] != NULL ) {\r
2843         DeleteObject( hPieceFace[index] );\r
2844     }\r
2845 \r
2846     hPieceFace[index] = hbm;\r
2847 }\r
2848 \r
2849 static int TranslatePieceToFontPiece( int piece )\r
2850 {\r
2851     switch( piece ) {\r
2852     case BlackPawn:\r
2853         return PM_BP;\r
2854     case BlackKnight:\r
2855         return PM_BN;\r
2856     case BlackBishop:\r
2857         return PM_BB;\r
2858     case BlackRook:\r
2859         return PM_BR;\r
2860     case BlackQueen:\r
2861         return PM_BQ;\r
2862     case BlackKing:\r
2863         return PM_BK;\r
2864     case WhitePawn:\r
2865         return PM_WP;\r
2866     case WhiteKnight:\r
2867         return PM_WN;\r
2868     case WhiteBishop:\r
2869         return PM_WB;\r
2870     case WhiteRook:\r
2871         return PM_WR;\r
2872     case WhiteQueen:\r
2873         return PM_WQ;\r
2874     case WhiteKing:\r
2875         return PM_WK;\r
2876 \r
2877     case BlackAngel:\r
2878         return PM_BA;\r
2879     case BlackMarshall:\r
2880         return PM_BC;\r
2881     case BlackFerz:\r
2882         return PM_BF;\r
2883     case BlackNightrider:\r
2884         return PM_BH;\r
2885     case BlackAlfil:\r
2886         return PM_BE;\r
2887     case BlackWazir:\r
2888         return PM_BW;\r
2889     case BlackUnicorn:\r
2890         return PM_BU;\r
2891     case BlackCannon:\r
2892         return PM_BO;\r
2893     case BlackGrasshopper:\r
2894         return PM_BG;\r
2895     case BlackMan:\r
2896         return PM_BM;\r
2897     case BlackSilver:\r
2898         return PM_BSG;\r
2899     case BlackLance:\r
2900         return PM_BL;\r
2901     case BlackFalcon:\r
2902         return PM_BV;\r
2903     case BlackCobra:\r
2904         return PM_BS;\r
2905     case BlackCardinal:\r
2906         return PM_BAB;\r
2907     case BlackDragon:\r
2908         return PM_BD;\r
2909 \r
2910     case WhiteAngel:\r
2911         return PM_WA;\r
2912     case WhiteMarshall:\r
2913         return PM_WC;\r
2914     case WhiteFerz:\r
2915         return PM_WF;\r
2916     case WhiteNightrider:\r
2917         return PM_WH;\r
2918     case WhiteAlfil:\r
2919         return PM_WE;\r
2920     case WhiteWazir:\r
2921         return PM_WW;\r
2922     case WhiteUnicorn:\r
2923         return PM_WU;\r
2924     case WhiteCannon:\r
2925         return PM_WO;\r
2926     case WhiteGrasshopper:\r
2927         return PM_WG;\r
2928     case WhiteMan:\r
2929         return PM_WM;\r
2930     case WhiteSilver:\r
2931         return PM_WSG;\r
2932     case WhiteLance:\r
2933         return PM_WL;\r
2934     case WhiteFalcon:\r
2935         return PM_WV;\r
2936     case WhiteCobra:\r
2937         return PM_WS;\r
2938     case WhiteCardinal:\r
2939         return PM_WAB;\r
2940     case WhiteDragon:\r
2941         return PM_WD;\r
2942     }\r
2943 \r
2944     return 0;\r
2945 }\r
2946 \r
2947 void CreatePiecesFromFont()\r
2948 {\r
2949     LOGFONT lf;\r
2950     HDC hdc_window = NULL;\r
2951     HDC hdc = NULL;\r
2952     HFONT hfont_old;\r
2953     int fontHeight;\r
2954     int i;\r
2955 \r
2956     if( fontBitmapSquareSize < 0 ) {\r
2957         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2958         return;\r
2959     }\r
2960 \r
2961     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2962         fontBitmapSquareSize = -1;\r
2963         return;\r
2964     }\r
2965 \r
2966     if( fontBitmapSquareSize != squareSize ) {\r
2967         hdc_window = GetDC( hwndMain );\r
2968         hdc = CreateCompatibleDC( hdc_window );\r
2969 \r
2970         if( hPieceFont != NULL ) {\r
2971             DeleteObject( hPieceFont );\r
2972         }\r
2973         else {\r
2974             for( i=0; i<=(int)BlackKing; i++ ) {\r
2975                 hPieceMask[i] = NULL;\r
2976                 hPieceFace[i] = NULL;\r
2977             }\r
2978         }\r
2979 \r
2980         fontHeight = 75;\r
2981 \r
2982         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2983             fontHeight = appData.fontPieceSize;\r
2984         }\r
2985 \r
2986         fontHeight = (fontHeight * squareSize) / 100;\r
2987 \r
2988         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2989         lf.lfWidth = 0;\r
2990         lf.lfEscapement = 0;\r
2991         lf.lfOrientation = 0;\r
2992         lf.lfWeight = FW_NORMAL;\r
2993         lf.lfItalic = 0;\r
2994         lf.lfUnderline = 0;\r
2995         lf.lfStrikeOut = 0;\r
2996         lf.lfCharSet = DEFAULT_CHARSET;\r
2997         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2998         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2999         lf.lfQuality = PROOF_QUALITY;\r
3000         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
3001         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
3002         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
3003 \r
3004         hPieceFont = CreateFontIndirect( &lf );\r
3005 \r
3006         if( hPieceFont == NULL ) {\r
3007             fontBitmapSquareSize = -2;\r
3008         }\r
3009         else {\r
3010             /* Setup font-to-piece character table */\r
3011             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
3012                 /* No (or wrong) global settings, try to detect the font */\r
3013                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
3014                     /* Alpha */\r
3015                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
3016                 }\r
3017                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3018                     /* DiagramTT* family */\r
3019                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3020                 }\r
3021                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3022                     /* Fairy symbols */\r
3023                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3024                 }\r
3025                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3026                     /* Good Companion (Some characters get warped as literal :-( */\r
3027                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3028                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3029                     SetCharTable(pieceToFontChar, s);\r
3030                 }\r
3031                 else {\r
3032                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3033                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3034                 }\r
3035             }\r
3036 \r
3037             /* Create bitmaps */\r
3038             hfont_old = SelectObject( hdc, hPieceFont );\r
3039             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3040                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3041                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3042 \r
3043             SelectObject( hdc, hfont_old );\r
3044 \r
3045             fontBitmapSquareSize = squareSize;\r
3046         }\r
3047     }\r
3048 \r
3049     if( hdc != NULL ) {\r
3050         DeleteDC( hdc );\r
3051     }\r
3052 \r
3053     if( hdc_window != NULL ) {\r
3054         ReleaseDC( hwndMain, hdc_window );\r
3055     }\r
3056 }\r
3057 \r
3058 HBITMAP\r
3059 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3060 {\r
3061   char name[128];\r
3062 \r
3063   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3064   if (gameInfo.event &&\r
3065       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3066       strcmp(name, "k80s") == 0) {\r
3067     strcpy(name, "tim");\r
3068   }\r
3069   return LoadBitmap(hinst, name);\r
3070 }\r
3071 \r
3072 \r
3073 /* Insert a color into the program's logical palette\r
3074    structure.  This code assumes the given color is\r
3075    the result of the RGB or PALETTERGB macro, and it\r
3076    knows how those macros work (which is documented).\r
3077 */\r
3078 VOID\r
3079 InsertInPalette(COLORREF color)\r
3080 {\r
3081   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3082 \r
3083   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3084     DisplayFatalError("Too many colors", 0, 1);\r
3085     pLogPal->palNumEntries--;\r
3086     return;\r
3087   }\r
3088 \r
3089   pe->peFlags = (char) 0;\r
3090   pe->peRed = (char) (0xFF & color);\r
3091   pe->peGreen = (char) (0xFF & (color >> 8));\r
3092   pe->peBlue = (char) (0xFF & (color >> 16));\r
3093   return;\r
3094 }\r
3095 \r
3096 \r
3097 VOID\r
3098 InitDrawingColors()\r
3099 {\r
3100   if (pLogPal == NULL) {\r
3101     /* Allocate enough memory for a logical palette with\r
3102      * PALETTESIZE entries and set the size and version fields\r
3103      * of the logical palette structure.\r
3104      */\r
3105     pLogPal = (NPLOGPALETTE)\r
3106       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3107                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3108     pLogPal->palVersion    = 0x300;\r
3109   }\r
3110   pLogPal->palNumEntries = 0;\r
3111 \r
3112   InsertInPalette(lightSquareColor);\r
3113   InsertInPalette(darkSquareColor);\r
3114   InsertInPalette(whitePieceColor);\r
3115   InsertInPalette(blackPieceColor);\r
3116   InsertInPalette(highlightSquareColor);\r
3117   InsertInPalette(premoveHighlightColor);\r
3118 \r
3119   /*  create a logical color palette according the information\r
3120    *  in the LOGPALETTE structure.\r
3121    */\r
3122   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3123 \r
3124   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3125   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3126   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3127   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3128   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3129   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3130   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3131   /* [AS] Force rendering of the font-based pieces */\r
3132   if( fontBitmapSquareSize > 0 ) {\r
3133     fontBitmapSquareSize = 0;\r
3134   }\r
3135 }\r
3136 \r
3137 \r
3138 int\r
3139 BoardWidth(int boardSize, int n)\r
3140 { /* [HGM] argument n added to allow different width and height */\r
3141   int lineGap = sizeInfo[boardSize].lineGap;\r
3142 \r
3143   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3144       lineGap = appData.overrideLineGap;\r
3145   }\r
3146 \r
3147   return (n + 1) * lineGap +\r
3148           n * sizeInfo[boardSize].squareSize;\r
3149 }\r
3150 \r
3151 /* Respond to board resize by dragging edge */\r
3152 VOID\r
3153 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3154 {\r
3155   BoardSize newSize = NUM_SIZES - 1;\r
3156   static int recurse = 0;\r
3157   if (IsIconic(hwndMain)) return;\r
3158   if (recurse > 0) return;\r
3159   recurse++;\r
3160   while (newSize > 0) {\r
3161         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3162         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3163            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3164     newSize--;\r
3165   } \r
3166   boardSize = newSize;\r
3167   InitDrawingSizes(boardSize, flags);\r
3168   recurse--;\r
3169 }\r
3170 \r
3171 \r
3172 \r
3173 VOID\r
3174 InitDrawingSizes(BoardSize boardSize, int flags)\r
3175 {\r
3176   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3177   ChessSquare piece;\r
3178   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3179   HDC hdc;\r
3180   SIZE clockSize, messageSize;\r
3181   HFONT oldFont;\r
3182   char buf[MSG_SIZ];\r
3183   char *str;\r
3184   HMENU hmenu = GetMenu(hwndMain);\r
3185   RECT crect, wrect, oldRect;\r
3186   int offby;\r
3187   LOGBRUSH logbrush;\r
3188 \r
3189   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3190   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3191 \r
3192   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3193   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3194 \r
3195   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3196   oldRect.top = boardY;\r
3197   oldRect.right = boardX + winWidth;\r
3198   oldRect.bottom = boardY + winHeight;\r
3199 \r
3200   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3201   smallLayout = sizeInfo[boardSize].smallLayout;\r
3202   squareSize = sizeInfo[boardSize].squareSize;\r
3203   lineGap = sizeInfo[boardSize].lineGap;\r
3204   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3205 \r
3206   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3207       lineGap = appData.overrideLineGap;\r
3208   }\r
3209 \r
3210   if (tinyLayout != oldTinyLayout) {\r
3211     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3212     if (tinyLayout) {\r
3213       style &= ~WS_SYSMENU;\r
3214       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3215                  "&Minimize\tCtrl+F4");\r
3216     } else {\r
3217       style |= WS_SYSMENU;\r
3218       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3219     }\r
3220     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3221 \r
3222     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3223       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3224         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3225     }\r
3226     DrawMenuBar(hwndMain);\r
3227   }\r
3228 \r
3229   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3230   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3231 \r
3232   /* Get text area sizes */\r
3233   hdc = GetDC(hwndMain);\r
3234   if (appData.clockMode) {\r
3235     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3236   } else {\r
3237     sprintf(buf, "White");\r
3238   }\r
3239   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3240   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3241   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3242   str = "We only care about the height here";\r
3243   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3244   SelectObject(hdc, oldFont);\r
3245   ReleaseDC(hwndMain, hdc);\r
3246 \r
3247   /* Compute where everything goes */\r
3248   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3249         /* [HGM] logo: if either logo is on, reserve space for it */\r
3250         logoHeight =  2*clockSize.cy;\r
3251         leftLogoRect.left   = OUTER_MARGIN;\r
3252         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3253         leftLogoRect.top    = OUTER_MARGIN;\r
3254         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3255 \r
3256         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3257         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3258         rightLogoRect.top    = OUTER_MARGIN;\r
3259         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3260 \r
3261 \r
3262     whiteRect.left = leftLogoRect.right;\r
3263     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3264     whiteRect.top = OUTER_MARGIN;\r
3265     whiteRect.bottom = whiteRect.top + logoHeight;\r
3266 \r
3267     blackRect.right = rightLogoRect.left;\r
3268     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3269     blackRect.top = whiteRect.top;\r
3270     blackRect.bottom = whiteRect.bottom;\r
3271   } else {\r
3272     whiteRect.left = OUTER_MARGIN;\r
3273     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3274     whiteRect.top = OUTER_MARGIN;\r
3275     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3276 \r
3277     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3278     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3279     blackRect.top = whiteRect.top;\r
3280     blackRect.bottom = whiteRect.bottom;\r
3281   }\r
3282 \r
3283   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3284   if (appData.showButtonBar) {\r
3285     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3286       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3287   } else {\r
3288     messageRect.right = OUTER_MARGIN + boardWidth;\r
3289   }\r
3290   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3291   messageRect.bottom = messageRect.top + messageSize.cy;\r
3292 \r
3293   boardRect.left = OUTER_MARGIN;\r
3294   boardRect.right = boardRect.left + boardWidth;\r
3295   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3296   boardRect.bottom = boardRect.top + boardHeight;\r
3297 \r
3298   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3299   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3300   oldBoardSize = boardSize;\r
3301   oldTinyLayout = tinyLayout;\r
3302   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3303   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3304     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3305   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3306   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3307   winHeight = winH; //       without disturbing window attachments\r
3308   GetWindowRect(hwndMain, &wrect);\r
3309   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3310                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3311 \r
3312   // [HGM] placement: let attached windows follow size change.\r
3313   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3314   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3315   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3316   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3317   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3318 \r
3319   /* compensate if menu bar wrapped */\r
3320   GetClientRect(hwndMain, &crect);\r
3321   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3322   winHeight += offby;\r
3323   switch (flags) {\r
3324   case WMSZ_TOPLEFT:\r
3325     SetWindowPos(hwndMain, NULL, \r
3326                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3327                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3328     break;\r
3329 \r
3330   case WMSZ_TOPRIGHT:\r
3331   case WMSZ_TOP:\r
3332     SetWindowPos(hwndMain, NULL, \r
3333                  wrect.left, wrect.bottom - winHeight, \r
3334                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3335     break;\r
3336 \r
3337   case WMSZ_BOTTOMLEFT:\r
3338   case WMSZ_LEFT:\r
3339     SetWindowPos(hwndMain, NULL, \r
3340                  wrect.right - winWidth, wrect.top, \r
3341                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3342     break;\r
3343 \r
3344   case WMSZ_BOTTOMRIGHT:\r
3345   case WMSZ_BOTTOM:\r
3346   case WMSZ_RIGHT:\r
3347   default:\r
3348     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3349                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3350     break;\r
3351   }\r
3352 \r
3353   hwndPause = NULL;\r
3354   for (i = 0; i < N_BUTTONS; i++) {\r
3355     if (buttonDesc[i].hwnd != NULL) {\r
3356       DestroyWindow(buttonDesc[i].hwnd);\r
3357       buttonDesc[i].hwnd = NULL;\r
3358     }\r
3359     if (appData.showButtonBar) {\r
3360       buttonDesc[i].hwnd =\r
3361         CreateWindow("BUTTON", buttonDesc[i].label,\r
3362                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3363                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3364                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3365                      (HMENU) buttonDesc[i].id,\r
3366                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3367       if (tinyLayout) {\r
3368         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3369                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3370                     MAKELPARAM(FALSE, 0));\r
3371       }\r
3372       if (buttonDesc[i].id == IDM_Pause)\r
3373         hwndPause = buttonDesc[i].hwnd;\r
3374       buttonDesc[i].wndproc = (WNDPROC)\r
3375         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3376     }\r
3377   }\r
3378   if (gridPen != NULL) DeleteObject(gridPen);\r
3379   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3380   if (premovePen != NULL) DeleteObject(premovePen);\r
3381   if (lineGap != 0) {\r
3382     logbrush.lbStyle = BS_SOLID;\r
3383     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3384     gridPen =\r
3385       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3386                    lineGap, &logbrush, 0, NULL);\r
3387     logbrush.lbColor = highlightSquareColor;\r
3388     highlightPen =\r
3389       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3390                    lineGap, &logbrush, 0, NULL);\r
3391 \r
3392     logbrush.lbColor = premoveHighlightColor; \r
3393     premovePen =\r
3394       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3395                    lineGap, &logbrush, 0, NULL);\r
3396 \r
3397     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3398     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3399       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3400       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3401         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3402       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3403         BOARD_WIDTH * (squareSize + lineGap);\r
3404       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3405     }\r
3406     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3407       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3408       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3409         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3410         lineGap / 2 + (i * (squareSize + lineGap));\r
3411       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3412         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3413       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3414     }\r
3415   }\r
3416 \r
3417   /* [HGM] Licensing requirement */\r
3418 #ifdef GOTHIC\r
3419   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3420 #endif\r
3421 #ifdef FALCON\r
3422   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3423 #endif\r
3424   GothicPopUp( "", VariantNormal);\r
3425 \r
3426 \r
3427 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3428 \r
3429   /* Load piece bitmaps for this board size */\r
3430   for (i=0; i<=2; i++) {\r
3431     for (piece = WhitePawn;\r
3432          (int) piece < (int) BlackPawn;\r
3433          piece = (ChessSquare) ((int) piece + 1)) {\r
3434       if (pieceBitmap[i][piece] != NULL)\r
3435         DeleteObject(pieceBitmap[i][piece]);\r
3436     }\r
3437   }\r
3438 \r
3439   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3440   // Orthodox Chess pieces\r
3441   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3442   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3443   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3444   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3445   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3446   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3447   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3448   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3449   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3450   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3451   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3452   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3453   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3454   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3455   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3456   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3457     // in Shogi, Hijack the unused Queen for Lance\r
3458     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3459     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3460     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3461   } else {\r
3462     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3463     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3464     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3465   }\r
3466 \r
3467   if(squareSize <= 72 && squareSize >= 33) { \r
3468     /* A & C are available in most sizes now */\r
3469     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3470       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3471       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3472       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3473       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3474       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3475       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3476       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3477       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3478       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3479       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3480       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3481       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3482     } else { // Smirf-like\r
3483       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3484       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3485       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3486     }\r
3487     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3488       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3489       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3490       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3491     } else { // WinBoard standard\r
3492       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3493       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3494       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3495     }\r
3496   }\r
3497 \r
3498 \r
3499   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3500     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3501     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3502     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3503     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3504     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3505     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3506     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3507     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3508     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3509     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3510     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3511     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3512     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3513     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3514     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3515     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3516     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3517     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3518     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3519     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3520     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3521     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3522     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3523     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3524     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3525     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3526     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3527     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3528     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3529     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3530 \r
3531     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3532       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3533       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3534       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3535       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3536       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3537       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3538       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3539       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3540       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3541       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3542       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3543       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3544     } else {\r
3545       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3546       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3547       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3548       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3549       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3550       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3551       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3552       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3553       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3554       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3555       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3556       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3557     }\r
3558 \r
3559   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3560     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3561     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3562     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3563     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3564     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3565     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3566     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3567     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3568     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3569     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3570     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3571     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3572     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3573     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3574   }\r
3575 \r
3576 \r
3577   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3578   /* special Shogi support in this size */\r
3579   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3580       for (piece = WhitePawn;\r
3581            (int) piece < (int) BlackPawn;\r
3582            piece = (ChessSquare) ((int) piece + 1)) {\r
3583         if (pieceBitmap[i][piece] != NULL)\r
3584           DeleteObject(pieceBitmap[i][piece]);\r
3585       }\r
3586     }\r
3587   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3588   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3589   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3590   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3591   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3592   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3593   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3594   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3595   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3596   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3597   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3598   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3599   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3600   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3601   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3602   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3603   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3604   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3605   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3606   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3607   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3608   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3609   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3610   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3611   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3612   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3613   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3614   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3615   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3616   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3617   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3618   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3619   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3620   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3621   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3622   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3623   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3624   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3625   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3626   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3627   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3628   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3629   minorSize = 0;\r
3630   }\r
3631 }\r
3632 \r
3633 HBITMAP\r
3634 PieceBitmap(ChessSquare p, int kind)\r
3635 {\r
3636   if ((int) p >= (int) BlackPawn)\r
3637     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3638 \r
3639   return pieceBitmap[kind][(int) p];\r
3640 }\r
3641 \r
3642 /***************************************************************/\r
3643 \r
3644 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3645 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3646 /*\r
3647 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3648 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3649 */\r
3650 \r
3651 VOID\r
3652 SquareToPos(int row, int column, int * x, int * y)\r
3653 {\r
3654   if (flipView) {\r
3655     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3656     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3657   } else {\r
3658     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3659     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3660   }\r
3661 }\r
3662 \r
3663 VOID\r
3664 DrawCoordsOnDC(HDC hdc)\r
3665 {\r
3666   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
3667   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
3668   char str[2] = { NULLCHAR, NULLCHAR };\r
3669   int oldMode, oldAlign, x, y, start, i;\r
3670   HFONT oldFont;\r
3671   HBRUSH oldBrush;\r
3672 \r
3673   if (!appData.showCoords)\r
3674     return;\r
3675 \r
3676   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3677 \r
3678   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3679   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3680   oldAlign = GetTextAlign(hdc);\r
3681   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3682 \r
3683   y = boardRect.top + lineGap;\r
3684   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3685 \r
3686   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3687   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3688     str[0] = files[start + i];\r
3689     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3690     y += squareSize + lineGap;\r
3691   }\r
3692 \r
3693   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3694 \r
3695   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3696   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3697     str[0] = ranks[start + i];\r
3698     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3699     x += squareSize + lineGap;\r
3700   }    \r
3701 \r
3702   SelectObject(hdc, oldBrush);\r
3703   SetBkMode(hdc, oldMode);\r
3704   SetTextAlign(hdc, oldAlign);\r
3705   SelectObject(hdc, oldFont);\r
3706 }\r
3707 \r
3708 VOID\r
3709 DrawGridOnDC(HDC hdc)\r
3710 {\r
3711   HPEN oldPen;\r
3712  \r
3713   if (lineGap != 0) {\r
3714     oldPen = SelectObject(hdc, gridPen);\r
3715     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3716     SelectObject(hdc, oldPen);\r
3717   }\r
3718 }\r
3719 \r
3720 #define HIGHLIGHT_PEN 0\r
3721 #define PREMOVE_PEN   1\r
3722 \r
3723 VOID\r
3724 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3725 {\r
3726   int x1, y1;\r
3727   HPEN oldPen, hPen;\r
3728   if (lineGap == 0) return;\r
3729   if (flipView) {\r
3730     x1 = boardRect.left +\r
3731       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3732     y1 = boardRect.top +\r
3733       lineGap/2 + y * (squareSize + lineGap);\r
3734   } else {\r
3735     x1 = boardRect.left +\r
3736       lineGap/2 + x * (squareSize + lineGap);\r
3737     y1 = boardRect.top +\r
3738       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3739   }\r
3740   hPen = pen ? premovePen : highlightPen;\r
3741   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3742   MoveToEx(hdc, x1, y1, NULL);\r
3743   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3744   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3745   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3746   LineTo(hdc, x1, y1);\r
3747   SelectObject(hdc, oldPen);\r
3748 }\r
3749 \r
3750 VOID\r
3751 DrawHighlightsOnDC(HDC hdc)\r
3752 {\r
3753   int i;\r
3754   for (i=0; i<2; i++) {\r
3755     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3756       DrawHighlightOnDC(hdc, TRUE,\r
3757                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3758                         HIGHLIGHT_PEN);\r
3759   }\r
3760   for (i=0; i<2; i++) {\r
3761     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3762         premoveHighlightInfo.sq[i].y >= 0) {\r
3763         DrawHighlightOnDC(hdc, TRUE,\r
3764                           premoveHighlightInfo.sq[i].x, \r
3765                           premoveHighlightInfo.sq[i].y,\r
3766                           PREMOVE_PEN);\r
3767     }\r
3768   }\r
3769 }\r
3770 \r
3771 /* Note: sqcolor is used only in monoMode */\r
3772 /* Note that this code is largely duplicated in woptions.c,\r
3773    function DrawSampleSquare, so that needs to be updated too */\r
3774 VOID\r
3775 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3776 {\r
3777   HBITMAP oldBitmap;\r
3778   HBRUSH oldBrush;\r
3779   int tmpSize;\r
3780 \r
3781   if (appData.blindfold) return;\r
3782 \r
3783   /* [AS] Use font-based pieces if needed */\r
3784   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3785     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3786     CreatePiecesFromFont();\r
3787 \r
3788     if( fontBitmapSquareSize == squareSize ) {\r
3789         int index = TranslatePieceToFontPiece(piece);\r
3790 \r
3791         SelectObject( tmphdc, hPieceMask[ index ] );\r
3792 \r
3793         BitBlt( hdc,\r
3794             x, y,\r
3795             squareSize, squareSize,\r
3796             tmphdc,\r
3797             0, 0,\r
3798             SRCAND );\r
3799 \r
3800         SelectObject( tmphdc, hPieceFace[ index ] );\r
3801 \r
3802         BitBlt( hdc,\r
3803             x, y,\r
3804             squareSize, squareSize,\r
3805             tmphdc,\r
3806             0, 0,\r
3807             SRCPAINT );\r
3808 \r
3809         return;\r
3810     }\r
3811   }\r
3812 \r
3813   if (appData.monoMode) {\r
3814     SelectObject(tmphdc, PieceBitmap(piece, \r
3815       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3816     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3817            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3818   } else {\r
3819     tmpSize = squareSize;\r
3820     if(minorSize &&\r
3821         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3822          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3823       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3824       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3825       x += (squareSize - minorSize)>>1;\r
3826       y += squareSize - minorSize - 2;\r
3827       tmpSize = minorSize;\r
3828     }\r
3829     if (color || appData.allWhite ) {\r
3830       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3831       if( color )\r
3832               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3833       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3834       if(appData.upsideDown && color==flipView)\r
3835         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3836       else\r
3837         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3838       /* Use black for outline of white pieces */\r
3839       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3840       if(appData.upsideDown && color==flipView)\r
3841         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3842       else\r
3843         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3844     } else {\r
3845       /* Use square color for details of black pieces */\r
3846       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3847       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3848       if(appData.upsideDown && !flipView)\r
3849         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3850       else\r
3851         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3852     }\r
3853     SelectObject(hdc, oldBrush);\r
3854     SelectObject(tmphdc, oldBitmap);\r
3855   }\r
3856 }\r
3857 \r
3858 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3859 int GetBackTextureMode( int algo )\r
3860 {\r
3861     int result = BACK_TEXTURE_MODE_DISABLED;\r
3862 \r
3863     switch( algo ) \r
3864     {\r
3865         case BACK_TEXTURE_MODE_PLAIN:\r
3866             result = 1; /* Always use identity map */\r
3867             break;\r
3868         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3869             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3870             break;\r
3871     }\r
3872 \r
3873     return result;\r
3874 }\r
3875 \r
3876 /* \r
3877     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3878     to handle redraws cleanly (as random numbers would always be different).\r
3879 */\r
3880 VOID RebuildTextureSquareInfo()\r
3881 {\r
3882     BITMAP bi;\r
3883     int lite_w = 0;\r
3884     int lite_h = 0;\r
3885     int dark_w = 0;\r
3886     int dark_h = 0;\r
3887     int row;\r
3888     int col;\r
3889 \r
3890     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3891 \r
3892     if( liteBackTexture != NULL ) {\r
3893         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3894             lite_w = bi.bmWidth;\r
3895             lite_h = bi.bmHeight;\r
3896         }\r
3897     }\r
3898 \r
3899     if( darkBackTexture != NULL ) {\r
3900         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3901             dark_w = bi.bmWidth;\r
3902             dark_h = bi.bmHeight;\r
3903         }\r
3904     }\r
3905 \r
3906     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3907         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3908             if( (col + row) & 1 ) {\r
3909                 /* Lite square */\r
3910                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3911                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3912                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3913                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3914                 }\r
3915             }\r
3916             else {\r
3917                 /* Dark square */\r
3918                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3919                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3920                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3921                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3922                 }\r
3923             }\r
3924         }\r
3925     }\r
3926 }\r
3927 \r
3928 /* [AS] Arrow highlighting support */\r
3929 \r
3930 static int A_WIDTH = 5; /* Width of arrow body */\r
3931 \r
3932 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3933 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3934 \r
3935 static double Sqr( double x )\r
3936 {\r
3937     return x*x;\r
3938 }\r
3939 \r
3940 static int Round( double x )\r
3941 {\r
3942     return (int) (x + 0.5);\r
3943 }\r
3944 \r
3945 /* Draw an arrow between two points using current settings */\r
3946 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3947 {\r
3948     POINT arrow[7];\r
3949     double dx, dy, j, k, x, y;\r
3950 \r
3951     if( d_x == s_x ) {\r
3952         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3953 \r
3954         arrow[0].x = s_x + A_WIDTH;\r
3955         arrow[0].y = s_y;\r
3956 \r
3957         arrow[1].x = s_x + A_WIDTH;\r
3958         arrow[1].y = d_y - h;\r
3959 \r
3960         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3961         arrow[2].y = d_y - h;\r
3962 \r
3963         arrow[3].x = d_x;\r
3964         arrow[3].y = d_y;\r
3965 \r
3966         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3967         arrow[4].y = d_y - h;\r
3968 \r
3969         arrow[5].x = s_x - A_WIDTH;\r
3970         arrow[5].y = d_y - h;\r
3971 \r
3972         arrow[6].x = s_x - A_WIDTH;\r
3973         arrow[6].y = s_y;\r
3974     }\r
3975     else if( d_y == s_y ) {\r
3976         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3977 \r
3978         arrow[0].x = s_x;\r
3979         arrow[0].y = s_y + A_WIDTH;\r
3980 \r
3981         arrow[1].x = d_x - w;\r
3982         arrow[1].y = s_y + A_WIDTH;\r
3983 \r
3984         arrow[2].x = d_x - w;\r
3985         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3986 \r
3987         arrow[3].x = d_x;\r
3988         arrow[3].y = d_y;\r
3989 \r
3990         arrow[4].x = d_x - w;\r
3991         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3992 \r
3993         arrow[5].x = d_x - w;\r
3994         arrow[5].y = s_y - A_WIDTH;\r
3995 \r
3996         arrow[6].x = s_x;\r
3997         arrow[6].y = s_y - A_WIDTH;\r
3998     }\r
3999     else {\r
4000         /* [AS] Needed a lot of paper for this! :-) */\r
4001         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4002         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4003   \r
4004         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4005 \r
4006         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4007 \r
4008         x = s_x;\r
4009         y = s_y;\r
4010 \r
4011         arrow[0].x = Round(x - j);\r
4012         arrow[0].y = Round(y + j*dx);\r
4013 \r
4014         arrow[1].x = Round(x + j);\r
4015         arrow[1].y = Round(y - j*dx);\r
4016 \r
4017         if( d_x > s_x ) {\r
4018             x = (double) d_x - k;\r
4019             y = (double) d_y - k*dy;\r
4020         }\r
4021         else {\r
4022             x = (double) d_x + k;\r
4023             y = (double) d_y + k*dy;\r
4024         }\r
4025 \r
4026         arrow[2].x = Round(x + j);\r
4027         arrow[2].y = Round(y - j*dx);\r
4028 \r
4029         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4030         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4031 \r
4032         arrow[4].x = d_x;\r
4033         arrow[4].y = d_y;\r
4034 \r
4035         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4036         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4037 \r
4038         arrow[6].x = Round(x - j);\r
4039         arrow[6].y = Round(y + j*dx);\r
4040     }\r
4041 \r
4042     Polygon( hdc, arrow, 7 );\r
4043 }\r
4044 \r
4045 /* [AS] Draw an arrow between two squares */\r
4046 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4047 {\r
4048     int s_x, s_y, d_x, d_y;\r
4049     HPEN hpen;\r
4050     HPEN holdpen;\r
4051     HBRUSH hbrush;\r
4052     HBRUSH holdbrush;\r
4053     LOGBRUSH stLB;\r
4054 \r
4055     if( s_col == d_col && s_row == d_row ) {\r
4056         return;\r
4057     }\r
4058 \r
4059     /* Get source and destination points */\r
4060     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4061     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4062 \r
4063     if( d_y > s_y ) {\r
4064         d_y += squareSize / 4;\r
4065     }\r
4066     else if( d_y < s_y ) {\r
4067         d_y += 3 * squareSize / 4;\r
4068     }\r
4069     else {\r
4070         d_y += squareSize / 2;\r
4071     }\r
4072 \r
4073     if( d_x > s_x ) {\r
4074         d_x += squareSize / 4;\r
4075     }\r
4076     else if( d_x < s_x ) {\r
4077         d_x += 3 * squareSize / 4;\r
4078     }\r
4079     else {\r
4080         d_x += squareSize / 2;\r
4081     }\r
4082 \r
4083     s_x += squareSize / 2;\r
4084     s_y += squareSize / 2;\r
4085 \r
4086     /* Adjust width */\r
4087     A_WIDTH = squareSize / 14;\r
4088 \r
4089     /* Draw */\r
4090     stLB.lbStyle = BS_SOLID;\r
4091     stLB.lbColor = appData.highlightArrowColor;\r
4092     stLB.lbHatch = 0;\r
4093 \r
4094     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4095     holdpen = SelectObject( hdc, hpen );\r
4096     hbrush = CreateBrushIndirect( &stLB );\r
4097     holdbrush = SelectObject( hdc, hbrush );\r
4098 \r
4099     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4100 \r
4101     SelectObject( hdc, holdpen );\r
4102     SelectObject( hdc, holdbrush );\r
4103     DeleteObject( hpen );\r
4104     DeleteObject( hbrush );\r
4105 }\r
4106 \r
4107 BOOL HasHighlightInfo()\r
4108 {\r
4109     BOOL result = FALSE;\r
4110 \r
4111     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4112         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4113     {\r
4114         result = TRUE;\r
4115     }\r
4116 \r
4117     return result;\r
4118 }\r
4119 \r
4120 BOOL IsDrawArrowEnabled()\r
4121 {\r
4122     BOOL result = FALSE;\r
4123 \r
4124     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4125         result = TRUE;\r
4126     }\r
4127 \r
4128     return result;\r
4129 }\r
4130 \r
4131 VOID DrawArrowHighlight( HDC hdc )\r
4132 {\r
4133     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4134         DrawArrowBetweenSquares( hdc,\r
4135             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4136             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4137     }\r
4138 }\r
4139 \r
4140 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4141 {\r
4142     HRGN result = NULL;\r
4143 \r
4144     if( HasHighlightInfo() ) {\r
4145         int x1, y1, x2, y2;\r
4146         int sx, sy, dx, dy;\r
4147 \r
4148         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4149         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4150 \r
4151         sx = MIN( x1, x2 );\r
4152         sy = MIN( y1, y2 );\r
4153         dx = MAX( x1, x2 ) + squareSize;\r
4154         dy = MAX( y1, y2 ) + squareSize;\r
4155 \r
4156         result = CreateRectRgn( sx, sy, dx, dy );\r
4157     }\r
4158 \r
4159     return result;\r
4160 }\r
4161 \r
4162 /*\r
4163     Warning: this function modifies the behavior of several other functions. \r
4164     \r
4165     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4166     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4167     repaint is scattered all over the place, which is not good for features such as\r
4168     "arrow highlighting" that require a full repaint of the board.\r
4169 \r
4170     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4171     user interaction, when speed is not so important) but especially to avoid errors\r
4172     in the displayed graphics.\r
4173 \r
4174     In such patched places, I always try refer to this function so there is a single\r
4175     place to maintain knowledge.\r
4176     \r
4177     To restore the original behavior, just return FALSE unconditionally.\r
4178 */\r
4179 BOOL IsFullRepaintPreferrable()\r
4180 {\r
4181     BOOL result = FALSE;\r
4182 \r
4183     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4184         /* Arrow may appear on the board */\r
4185         result = TRUE;\r
4186     }\r
4187 \r
4188     return result;\r
4189 }\r
4190 \r
4191 /* \r
4192     This function is called by DrawPosition to know whether a full repaint must\r
4193     be forced or not.\r
4194 \r
4195     Only DrawPosition may directly call this function, which makes use of \r
4196     some state information. Other function should call DrawPosition specifying \r
4197     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4198 */\r
4199 BOOL DrawPositionNeedsFullRepaint()\r
4200 {\r
4201     BOOL result = FALSE;\r
4202 \r
4203     /* \r
4204         Probably a slightly better policy would be to trigger a full repaint\r
4205         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4206         but animation is fast enough that it's difficult to notice.\r
4207     */\r
4208     if( animInfo.piece == EmptySquare ) {\r
4209         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4210             result = TRUE;\r
4211         }\r
4212     }\r
4213 \r
4214     return result;\r
4215 }\r
4216 \r
4217 VOID\r
4218 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4219 {\r
4220   int row, column, x, y, square_color, piece_color;\r
4221   ChessSquare piece;\r
4222   HBRUSH oldBrush;\r
4223   HDC texture_hdc = NULL;\r
4224 \r
4225   /* [AS] Initialize background textures if needed */\r
4226   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4227       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4228       if( backTextureSquareSize != squareSize \r
4229        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4230           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4231           backTextureSquareSize = squareSize;\r
4232           RebuildTextureSquareInfo();\r
4233       }\r
4234 \r
4235       texture_hdc = CreateCompatibleDC( hdc );\r
4236   }\r
4237 \r
4238   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4239     for (column = 0; column < BOARD_WIDTH; column++) {\r
4240   \r
4241       SquareToPos(row, column, &x, &y);\r
4242 \r
4243       piece = board[row][column];\r
4244 \r
4245       square_color = ((column + row) % 2) == 1;\r
4246       if( gameInfo.variant == VariantXiangqi ) {\r
4247           square_color = !InPalace(row, column);\r
4248           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4249           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4250       }\r
4251       piece_color = (int) piece < (int) BlackPawn;\r
4252 \r
4253 \r
4254       /* [HGM] holdings file: light square or black */\r
4255       if(column == BOARD_LEFT-2) {\r
4256             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4257                 square_color = 1;\r
4258             else {\r
4259                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4260                 continue;\r
4261             }\r
4262       } else\r
4263       if(column == BOARD_RGHT + 1 ) {\r
4264             if( row < gameInfo.holdingsSize )\r
4265                 square_color = 1;\r
4266             else {\r
4267                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4268                 continue;\r
4269             }\r
4270       }\r
4271       if(column == BOARD_LEFT-1 ) /* left align */\r
4272             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4273       else if( column == BOARD_RGHT) /* right align */\r
4274             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4275       else\r
4276       if (appData.monoMode) {\r
4277         if (piece == EmptySquare) {\r
4278           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4279                  square_color ? WHITENESS : BLACKNESS);\r
4280         } else {\r
4281           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4282         }\r
4283       } \r
4284       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4285           /* [AS] Draw the square using a texture bitmap */\r
4286           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4287           int r = row, c = column; // [HGM] do not flip board in flipView\r
4288           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4289 \r
4290           DrawTile( x, y, \r
4291               squareSize, squareSize, \r
4292               hdc, \r
4293               texture_hdc,\r
4294               backTextureSquareInfo[r][c].mode,\r
4295               backTextureSquareInfo[r][c].x,\r
4296               backTextureSquareInfo[r][c].y );\r
4297 \r
4298           SelectObject( texture_hdc, hbm );\r
4299 \r
4300           if (piece != EmptySquare) {\r
4301               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4302           }\r
4303       }\r
4304       else {\r
4305         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4306 \r
4307         oldBrush = SelectObject(hdc, brush );\r
4308         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4309         SelectObject(hdc, oldBrush);\r
4310         if (piece != EmptySquare)\r
4311           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4312       }\r
4313     }\r
4314   }\r
4315 \r
4316   if( texture_hdc != NULL ) {\r
4317     DeleteDC( texture_hdc );\r
4318   }\r
4319 }\r
4320 \r
4321 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4322 void fputDW(FILE *f, int x)\r
4323 {\r
4324         fputc(x     & 255, f);\r
4325         fputc(x>>8  & 255, f);\r
4326         fputc(x>>16 & 255, f);\r
4327         fputc(x>>24 & 255, f);\r
4328 }\r
4329 \r
4330 #define MAX_CLIPS 200   /* more than enough */\r
4331 \r
4332 VOID\r
4333 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4334 {\r
4335 //  HBITMAP bufferBitmap;\r
4336   BITMAP bi;\r
4337 //  RECT Rect;\r
4338   HDC tmphdc;\r
4339   HBITMAP hbm;\r
4340   int w = 100, h = 50;\r
4341 \r
4342   if(logo == NULL) return;\r
4343 //  GetClientRect(hwndMain, &Rect);\r
4344 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4345 //                                      Rect.bottom-Rect.top+1);\r
4346   tmphdc = CreateCompatibleDC(hdc);\r
4347   hbm = SelectObject(tmphdc, logo);\r
4348   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4349             w = bi.bmWidth;\r
4350             h = bi.bmHeight;\r
4351   }\r
4352   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4353                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4354   SelectObject(tmphdc, hbm);\r
4355   DeleteDC(tmphdc);\r
4356 }\r
4357 \r
4358 VOID\r
4359 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4360 {\r
4361   static Board lastReq, lastDrawn;\r
4362   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4363   static int lastDrawnFlipView = 0;\r
4364   static int lastReqValid = 0, lastDrawnValid = 0;\r
4365   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4366   HDC tmphdc;\r
4367   HDC hdcmem;\r
4368   HBITMAP bufferBitmap;\r
4369   HBITMAP oldBitmap;\r
4370   RECT Rect;\r
4371   HRGN clips[MAX_CLIPS];\r
4372   ChessSquare dragged_piece = EmptySquare;\r
4373 \r
4374   /* I'm undecided on this - this function figures out whether a full\r
4375    * repaint is necessary on its own, so there's no real reason to have the\r
4376    * caller tell it that.  I think this can safely be set to FALSE - but\r
4377    * if we trust the callers not to request full repaints unnessesarily, then\r
4378    * we could skip some clipping work.  In other words, only request a full\r
4379    * redraw when the majority of pieces have changed positions (ie. flip, \r
4380    * gamestart and similar)  --Hawk\r
4381    */\r
4382   Boolean fullrepaint = repaint;\r
4383 \r
4384   if( DrawPositionNeedsFullRepaint() ) {\r
4385       fullrepaint = TRUE;\r
4386   }\r
4387 \r
4388   if (board == NULL) {\r
4389     if (!lastReqValid) {\r
4390       return;\r
4391     }\r
4392     board = lastReq;\r
4393   } else {\r
4394     CopyBoard(lastReq, board);\r
4395     lastReqValid = 1;\r
4396   }\r
4397 \r
4398   if (doingSizing) {\r
4399     return;\r
4400   }\r
4401 \r
4402   if (IsIconic(hwndMain)) {\r
4403     return;\r
4404   }\r
4405 \r
4406   if (hdc == NULL) {\r
4407     hdc = GetDC(hwndMain);\r
4408     if (!appData.monoMode) {\r
4409       SelectPalette(hdc, hPal, FALSE);\r
4410       RealizePalette(hdc);\r
4411     }\r
4412     releaseDC = TRUE;\r
4413   } else {\r
4414     releaseDC = FALSE;\r
4415   }\r
4416 \r
4417   /* Create some work-DCs */\r
4418   hdcmem = CreateCompatibleDC(hdc);\r
4419   tmphdc = CreateCompatibleDC(hdc);\r
4420 \r
4421   /* If dragging is in progress, we temporarely remove the piece */\r
4422   /* [HGM] or temporarily decrease count if stacked              */\r
4423   /*       !! Moved to before board compare !!                   */\r
4424   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4425     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4426     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4427             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4428         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4429     } else \r
4430     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4431             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4432         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4433     } else \r
4434         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4435   }\r
4436 \r
4437   /* Figure out which squares need updating by comparing the \r
4438    * newest board with the last drawn board and checking if\r
4439    * flipping has changed.\r
4440    */\r
4441   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4442     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4443       for (column = 0; column < BOARD_WIDTH; column++) {\r
4444         if (lastDrawn[row][column] != board[row][column]) {\r
4445           SquareToPos(row, column, &x, &y);\r
4446           clips[num_clips++] =\r
4447             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4448         }\r
4449       }\r
4450     }\r
4451     for (i=0; i<2; i++) {\r
4452       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4453           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4454         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4455             lastDrawnHighlight.sq[i].y >= 0) {\r
4456           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4457                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4458           clips[num_clips++] =\r
4459             CreateRectRgn(x - lineGap, y - lineGap, \r
4460                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4461         }\r
4462         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4463           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4464           clips[num_clips++] =\r
4465             CreateRectRgn(x - lineGap, y - lineGap, \r
4466                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4467         }\r
4468       }\r
4469     }\r
4470     for (i=0; i<2; i++) {\r
4471       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4472           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4473         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4474             lastDrawnPremove.sq[i].y >= 0) {\r
4475           SquareToPos(lastDrawnPremove.sq[i].y,\r
4476                       lastDrawnPremove.sq[i].x, &x, &y);\r
4477           clips[num_clips++] =\r
4478             CreateRectRgn(x - lineGap, y - lineGap, \r
4479                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4480         }\r
4481         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4482             premoveHighlightInfo.sq[i].y >= 0) {\r
4483           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4484                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4485           clips[num_clips++] =\r
4486             CreateRectRgn(x - lineGap, y - lineGap, \r
4487                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4488         }\r
4489       }\r
4490     }\r
4491   } else {\r
4492     fullrepaint = TRUE;\r
4493   }\r
4494 \r
4495   /* Create a buffer bitmap - this is the actual bitmap\r
4496    * being written to.  When all the work is done, we can\r
4497    * copy it to the real DC (the screen).  This avoids\r
4498    * the problems with flickering.\r
4499    */\r
4500   GetClientRect(hwndMain, &Rect);\r
4501   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4502                                         Rect.bottom-Rect.top+1);\r
4503   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4504   if (!appData.monoMode) {\r
4505     SelectPalette(hdcmem, hPal, FALSE);\r
4506   }\r
4507 \r
4508   /* Create clips for dragging */\r
4509   if (!fullrepaint) {\r
4510     if (dragInfo.from.x >= 0) {\r
4511       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4512       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4513     }\r
4514     if (dragInfo.start.x >= 0) {\r
4515       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4516       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4517     }\r
4518     if (dragInfo.pos.x >= 0) {\r
4519       x = dragInfo.pos.x - squareSize / 2;\r
4520       y = dragInfo.pos.y - squareSize / 2;\r
4521       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4522     }\r
4523     if (dragInfo.lastpos.x >= 0) {\r
4524       x = dragInfo.lastpos.x - squareSize / 2;\r
4525       y = dragInfo.lastpos.y - squareSize / 2;\r
4526       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4527     }\r
4528   }\r
4529 \r
4530   /* Are we animating a move?  \r
4531    * If so, \r
4532    *   - remove the piece from the board (temporarely)\r
4533    *   - calculate the clipping region\r
4534    */\r
4535   if (!fullrepaint) {\r
4536     if (animInfo.piece != EmptySquare) {\r
4537       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4538       x = boardRect.left + animInfo.lastpos.x;\r
4539       y = boardRect.top + animInfo.lastpos.y;\r
4540       x2 = boardRect.left + animInfo.pos.x;\r
4541       y2 = boardRect.top + animInfo.pos.y;\r
4542       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4543       /* Slight kludge.  The real problem is that after AnimateMove is\r
4544          done, the position on the screen does not match lastDrawn.\r
4545          This currently causes trouble only on e.p. captures in\r
4546          atomic, where the piece moves to an empty square and then\r
4547          explodes.  The old and new positions both had an empty square\r
4548          at the destination, but animation has drawn a piece there and\r
4549          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4550       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4551     }\r
4552   }\r
4553 \r
4554   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4555   if (num_clips == 0)\r
4556     fullrepaint = TRUE;\r
4557 \r
4558   /* Set clipping on the memory DC */\r
4559   if (!fullrepaint) {\r
4560     SelectClipRgn(hdcmem, clips[0]);\r
4561     for (x = 1; x < num_clips; x++) {\r
4562       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4563         abort();  // this should never ever happen!\r
4564     }\r
4565   }\r
4566 \r
4567   /* Do all the drawing to the memory DC */\r
4568   if(explodeInfo.radius) { // [HGM] atomic\r
4569         HBRUSH oldBrush;\r
4570         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4571         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4572         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4573         x += squareSize/2;\r
4574         y += squareSize/2;\r
4575         if(!fullrepaint) {\r
4576           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4577           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4578         }\r
4579         DrawGridOnDC(hdcmem);\r
4580         DrawHighlightsOnDC(hdcmem);\r
4581         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4582         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4583         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4584         SelectObject(hdcmem, oldBrush);\r
4585   } else {\r
4586     DrawGridOnDC(hdcmem);\r
4587     DrawHighlightsOnDC(hdcmem);\r
4588     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4589   }\r
4590   if(logoHeight) {\r
4591         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4592         if(appData.autoLogo) {\r
4593           \r
4594           switch(gameMode) { // pick logos based on game mode\r
4595             case IcsObserving:\r
4596                 whiteLogo = second.programLogo; // ICS logo\r
4597                 blackLogo = second.programLogo;\r
4598             default:\r
4599                 break;\r
4600             case IcsPlayingWhite:\r
4601                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4602                 blackLogo = second.programLogo; // ICS logo\r
4603                 break;\r
4604             case IcsPlayingBlack:\r
4605                 whiteLogo = second.programLogo; // ICS logo\r
4606                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4607                 break;\r
4608             case TwoMachinesPlay:\r
4609                 if(first.twoMachinesColor[0] == 'b') {\r
4610                     whiteLogo = second.programLogo;\r
4611                     blackLogo = first.programLogo;\r
4612                 }\r
4613                 break;\r
4614             case MachinePlaysWhite:\r
4615                 blackLogo = userLogo;\r
4616                 break;\r
4617             case MachinePlaysBlack:\r
4618                 whiteLogo = userLogo;\r
4619                 blackLogo = first.programLogo;\r
4620           }\r
4621         }\r
4622         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4623         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4624   }\r
4625 \r
4626   if( appData.highlightMoveWithArrow ) {\r
4627     DrawArrowHighlight(hdcmem);\r
4628   }\r
4629 \r
4630   DrawCoordsOnDC(hdcmem);\r
4631 \r
4632   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4633                  /* to make sure lastDrawn contains what is actually drawn */\r
4634 \r
4635   /* Put the dragged piece back into place and draw it (out of place!) */\r
4636     if (dragged_piece != EmptySquare) {\r
4637     /* [HGM] or restack */\r
4638     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4639                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4640     else\r
4641     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4642                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4643     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4644     x = dragInfo.pos.x - squareSize / 2;\r
4645     y = dragInfo.pos.y - squareSize / 2;\r
4646     DrawPieceOnDC(hdcmem, dragged_piece,\r
4647                   ((int) dragged_piece < (int) BlackPawn), \r
4648                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4649   }   \r
4650   \r
4651   /* Put the animated piece back into place and draw it */\r
4652   if (animInfo.piece != EmptySquare) {\r
4653     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4654     x = boardRect.left + animInfo.pos.x;\r
4655     y = boardRect.top + animInfo.pos.y;\r
4656     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4657                   ((int) animInfo.piece < (int) BlackPawn),\r
4658                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4659   }\r
4660 \r
4661   /* Release the bufferBitmap by selecting in the old bitmap \r
4662    * and delete the memory DC\r
4663    */\r
4664   SelectObject(hdcmem, oldBitmap);\r
4665   DeleteDC(hdcmem);\r
4666 \r
4667   /* Set clipping on the target DC */\r
4668   if (!fullrepaint) {\r
4669     SelectClipRgn(hdc, clips[0]);\r
4670     for (x = 1; x < num_clips; x++) {\r
4671       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4672         abort();   // this should never ever happen!\r
4673     } \r
4674   }\r
4675 \r
4676   /* Copy the new bitmap onto the screen in one go.\r
4677    * This way we avoid any flickering\r
4678    */\r
4679   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4680   BitBlt(hdc, boardRect.left, boardRect.top,\r
4681          boardRect.right - boardRect.left,\r
4682          boardRect.bottom - boardRect.top,\r
4683          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4684   if(saveDiagFlag) { \r
4685     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4686     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4687 \r
4688     GetObject(bufferBitmap, sizeof(b), &b);\r
4689     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4690         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4691         bih.biWidth = b.bmWidth;\r
4692         bih.biHeight = b.bmHeight;\r
4693         bih.biPlanes = 1;\r
4694         bih.biBitCount = b.bmBitsPixel;\r
4695         bih.biCompression = 0;\r
4696         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4697         bih.biXPelsPerMeter = 0;\r
4698         bih.biYPelsPerMeter = 0;\r
4699         bih.biClrUsed = 0;\r
4700         bih.biClrImportant = 0;\r
4701 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4702 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4703         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4704 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4705 \r
4706         wb = b.bmWidthBytes;\r
4707         // count colors\r
4708         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4709                 int k = ((int*) pData)[i];\r
4710                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4711                 if(j >= 16) break;\r
4712                 color[j] = k;\r
4713                 if(j >= nrColors) nrColors = j+1;\r
4714         }\r
4715         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4716                 INT p = 0;\r
4717                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4718                     for(w=0; w<(wb>>2); w+=2) {\r
4719                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4720                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4721                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4722                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4723                         pData[p++] = m | j<<4;\r
4724                     }\r
4725                     while(p&3) pData[p++] = 0;\r
4726                 }\r
4727                 fac = 3;\r
4728                 wb = ((wb+31)>>5)<<2;\r
4729         }\r
4730         // write BITMAPFILEHEADER\r
4731         fprintf(diagFile, "BM");\r
4732         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4733         fputDW(diagFile, 0);\r
4734         fputDW(diagFile, 0x36 + (fac?64:0));\r
4735         // write BITMAPINFOHEADER\r
4736         fputDW(diagFile, 40);\r
4737         fputDW(diagFile, b.bmWidth);\r
4738         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4739         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4740         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4741         fputDW(diagFile, 0);\r
4742         fputDW(diagFile, 0);\r
4743         fputDW(diagFile, 0);\r
4744         fputDW(diagFile, 0);\r
4745         fputDW(diagFile, 0);\r
4746         fputDW(diagFile, 0);\r
4747         // write color table\r
4748         if(fac)\r
4749         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4750         // write bitmap data\r
4751         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4752                 fputc(pData[i], diagFile);\r
4753      }\r
4754   }\r
4755 \r
4756   SelectObject(tmphdc, oldBitmap);\r
4757 \r
4758   /* Massive cleanup */\r
4759   for (x = 0; x < num_clips; x++)\r
4760     DeleteObject(clips[x]);\r
4761 \r
4762   DeleteDC(tmphdc);\r
4763   DeleteObject(bufferBitmap);\r
4764 \r
4765   if (releaseDC) \r
4766     ReleaseDC(hwndMain, hdc);\r
4767   \r
4768   if (lastDrawnFlipView != flipView) {\r
4769     if (flipView)\r
4770       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4771     else\r
4772       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4773   }\r
4774 \r
4775 /*  CopyBoard(lastDrawn, board);*/\r
4776   lastDrawnHighlight = highlightInfo;\r
4777   lastDrawnPremove   = premoveHighlightInfo;\r
4778   lastDrawnFlipView = flipView;\r
4779   lastDrawnValid = 1;\r
4780 }\r
4781 \r
4782 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4783 int\r
4784 SaveDiagram(f)\r
4785      FILE *f;\r
4786 {\r
4787     saveDiagFlag = 1; diagFile = f;\r
4788     HDCDrawPosition(NULL, TRUE, NULL);\r
4789 \r
4790     saveDiagFlag = 0;\r
4791 \r
4792 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4793     \r
4794     fclose(f);\r
4795     return TRUE;\r
4796 }\r
4797 \r
4798 \r
4799 /*---------------------------------------------------------------------------*\\r
4800 | CLIENT PAINT PROCEDURE\r
4801 |   This is the main event-handler for the WM_PAINT message.\r
4802 |\r
4803 \*---------------------------------------------------------------------------*/\r
4804 VOID\r
4805 PaintProc(HWND hwnd)\r
4806 {\r
4807   HDC         hdc;\r
4808   PAINTSTRUCT ps;\r
4809   HFONT       oldFont;\r
4810 \r
4811   if((hdc = BeginPaint(hwnd, &ps))) {\r
4812     if (IsIconic(hwnd)) {\r
4813       DrawIcon(hdc, 2, 2, iconCurrent);\r
4814     } else {\r
4815       if (!appData.monoMode) {\r
4816         SelectPalette(hdc, hPal, FALSE);\r
4817         RealizePalette(hdc);\r
4818       }\r
4819       HDCDrawPosition(hdc, 1, NULL);\r
4820       oldFont =\r
4821         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4822       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4823                  ETO_CLIPPED|ETO_OPAQUE,\r
4824                  &messageRect, messageText, strlen(messageText), NULL);\r
4825       SelectObject(hdc, oldFont);\r
4826       DisplayBothClocks();\r
4827     }\r
4828     EndPaint(hwnd,&ps);\r
4829   }\r
4830 \r
4831   return;\r
4832 }\r
4833 \r
4834 \r
4835 /*\r
4836  * If the user selects on a border boundary, return -1; if off the board,\r
4837  *   return -2.  Otherwise map the event coordinate to the square.\r
4838  * The offset boardRect.left or boardRect.top must already have been\r
4839  *   subtracted from x.\r
4840  */\r
4841 int\r
4842 EventToSquare(int x)\r
4843 {\r
4844   if (x <= 0)\r
4845     return -2;\r
4846   if (x < lineGap)\r
4847     return -1;\r
4848   x -= lineGap;\r
4849   if ((x % (squareSize + lineGap)) >= squareSize)\r
4850     return -1;\r
4851   x /= (squareSize + lineGap);\r
4852   if (x >= BOARD_SIZE)\r
4853     return -2;\r
4854   return x;\r
4855 }\r
4856 \r
4857 typedef struct {\r
4858   char piece;\r
4859   int command;\r
4860   char* name;\r
4861 } DropEnable;\r
4862 \r
4863 DropEnable dropEnables[] = {\r
4864   { 'P', DP_Pawn, "Pawn" },\r
4865   { 'N', DP_Knight, "Knight" },\r
4866   { 'B', DP_Bishop, "Bishop" },\r
4867   { 'R', DP_Rook, "Rook" },\r
4868   { 'Q', DP_Queen, "Queen" },\r
4869 };\r
4870 \r
4871 VOID\r
4872 SetupDropMenu(HMENU hmenu)\r
4873 {\r
4874   int i, count, enable;\r
4875   char *p;\r
4876   extern char white_holding[], black_holding[];\r
4877   char item[MSG_SIZ];\r
4878 \r
4879   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4880     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4881                dropEnables[i].piece);\r
4882     count = 0;\r
4883     while (p && *p++ == dropEnables[i].piece) count++;\r
4884     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4885     enable = count > 0 || !appData.testLegality\r
4886       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4887                       && !appData.icsActive);\r
4888     ModifyMenu(hmenu, dropEnables[i].command,\r
4889                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4890                dropEnables[i].command, item);\r
4891   }\r
4892 }\r
4893 \r
4894 /* Event handler for mouse messages */\r
4895 VOID\r
4896 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4897 {\r
4898   int x, y;\r
4899   POINT pt;\r
4900   static int recursive = 0;\r
4901   HMENU hmenu;\r
4902 //  BOOLEAN needsRedraw = FALSE;\r
4903   BOOLEAN saveAnimate;\r
4904   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4905   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
4906   ChessMove moveType;\r
4907 \r
4908   if (recursive) {\r
4909     if (message == WM_MBUTTONUP) {\r
4910       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4911          to the middle button: we simulate pressing the left button too!\r
4912          */\r
4913       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4914       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4915     }\r
4916     return;\r
4917   }\r
4918   recursive++;\r
4919   \r
4920   pt.x = LOWORD(lParam);\r
4921   pt.y = HIWORD(lParam);\r
4922   x = EventToSquare(pt.x - boardRect.left);\r
4923   y = EventToSquare(pt.y - boardRect.top);\r
4924   if (!flipView && y >= 0) {\r
4925     y = BOARD_HEIGHT - 1 - y;\r
4926   }\r
4927   if (flipView && x >= 0) {\r
4928     x = BOARD_WIDTH - 1 - x;\r
4929   }\r
4930 \r
4931   switch (message) {\r
4932   case WM_LBUTTONDOWN:\r
4933     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
4934         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
4935         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
4936         if(gameInfo.holdingsWidth && \r
4937                 (WhiteOnMove(currentMove) \r
4938                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
4939                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
4940             // click in right holdings, for determining promotion piece\r
4941             ChessSquare p = boards[currentMove][y][x];\r
4942             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
4943             if(p != EmptySquare) {\r
4944                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
4945                 fromX = fromY = -1;\r
4946                 break;\r
4947             }\r
4948         }\r
4949         DrawPosition(FALSE, boards[currentMove]);\r
4950         break;\r
4951     }\r
4952     ErrorPopDown();\r
4953     sameAgain = FALSE;\r
4954     if (y == -2) {\r
4955       /* Downclick vertically off board; check if on clock */\r
4956       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4957         if (gameMode == EditPosition) {\r
4958           SetWhiteToPlayEvent();\r
4959         } else if (gameMode == IcsPlayingBlack ||\r
4960                    gameMode == MachinePlaysWhite) {\r
4961           CallFlagEvent();\r
4962         } else if (gameMode == EditGame) {\r
4963           AdjustClock(flipClock, -1);\r
4964         }\r
4965       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4966         if (gameMode == EditPosition) {\r
4967           SetBlackToPlayEvent();\r
4968         } else if (gameMode == IcsPlayingWhite ||\r
4969                    gameMode == MachinePlaysBlack) {\r
4970           CallFlagEvent();\r
4971         } else if (gameMode == EditGame) {\r
4972           AdjustClock(!flipClock, -1);\r
4973         }\r
4974       }\r
4975       if (!appData.highlightLastMove) {\r
4976         ClearHighlights();\r
4977         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
4978       }\r
4979       fromX = fromY = -1;\r
4980       dragInfo.start.x = dragInfo.start.y = -1;\r
4981       dragInfo.from = dragInfo.start;\r
4982       break;\r
4983     } else if (x < 0 || y < 0\r
4984       /* [HGM] block clicks between board and holdings */\r
4985               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
4986               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
4987               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
4988         /* EditPosition, empty square, or different color piece;\r
4989            click-click move is possible */\r
4990                                ) {\r
4991       break;\r
4992     } else if (fromX == x && fromY == y) {\r
4993       /* Downclick on same square again */\r
4994       ClearHighlights();\r
4995       DrawPosition(forceFullRepaint || FALSE, NULL);\r
4996       sameAgain = TRUE;  \r
4997     } else if (fromX != -1 &&\r
4998                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
4999                                                                         ) {\r
5000       /* Downclick on different square. */\r
5001       /* [HGM] if on holdings file, should count as new first click ! */\r
5002       /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5003         toX = x;\r
5004         toY = y;\r
5005         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5006            to make sure move is legal before showing promotion popup */\r
5007         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR, FALSE);\r
5008         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5009                 fromX = fromY = -1; \r
5010                 ClearHighlights();\r
5011                 DrawPosition(FALSE, boards[currentMove]);\r
5012                 break; \r
5013         } else \r
5014         if(moveType != ImpossibleMove && moveType != Comment) {\r
5015           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5016           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5017             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5018               appData.alwaysPromoteToQueen)) {\r
5019                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5020                   if (!appData.highlightLastMove) {\r
5021                       ClearHighlights();\r
5022                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5023                   }\r
5024           } else\r
5025           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5026                   SetHighlights(fromX, fromY, toX, toY);\r
5027                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5028                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5029                      If promotion to Q is legal, all are legal! */\r
5030                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5031                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5032                     // kludge to temporarily execute move on display, without promoting yet\r
5033                     promotionChoice = TRUE;\r
5034                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5035                     boards[currentMove][toY][toX] = p;\r
5036                     DrawPosition(FALSE, boards[currentMove]);\r
5037                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5038                     boards[currentMove][toY][toX] = q;\r
5039                     DisplayMessage("Select piece from holdings", "");\r
5040                   } else\r
5041                   PromotionPopup(hwnd);\r
5042                   goto noClear;\r
5043           } else { // not a promotion. Move can be illegal if testLegality off, and should be made then.\r
5044              if (appData.animate || appData.highlightLastMove) {\r
5045                  SetHighlights(fromX, fromY, toX, toY);\r
5046              } else {\r
5047                  ClearHighlights();\r
5048              }\r
5049              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5050              if (appData.animate && !appData.highlightLastMove) {\r
5051                   ClearHighlights();\r
5052                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5053              }\r
5054           }\r
5055           fromX = fromY = -1;\r
5056         noClear:\r
5057           break;\r
5058         }\r
5059         if (gotPremove && moveType != Comment) {\r
5060             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5061 //            DrawPosition(forceFullRepaint || FALSE, NULL);\r
5062         } else ClearHighlights();\r
5063         fromX = fromY = -1;\r
5064         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5065         if(moveType != Comment) break;\r
5066     }\r
5067     /* First downclick, or restart on a square with same color piece */\r
5068     if (!frozen && OKToStartUserMove(x, y)) {\r
5069       fromX = x;\r
5070       fromY = y;\r
5071       dragInfo.lastpos = pt;\r
5072       dragInfo.from.x = fromX;\r
5073       dragInfo.from.y = fromY;\r
5074       dragInfo.start = dragInfo.from;\r
5075       SetCapture(hwndMain);\r
5076     } else {\r
5077       fromX = fromY = -1;\r
5078       dragInfo.start.x = dragInfo.start.y = -1;\r
5079       dragInfo.from = dragInfo.start;\r
5080       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5081     }\r
5082     break;\r
5083 \r
5084   case WM_LBUTTONUP:\r
5085     ReleaseCapture();\r
5086     if (fromX == -1) break;\r
5087     if (x == fromX && y == fromY) {\r
5088       dragInfo.from.x = dragInfo.from.y = -1;\r
5089       /* Upclick on same square */\r
5090       if (sameAgain) {\r
5091         /* Clicked same square twice: abort click-click move */\r
5092         fromX = fromY = -1;\r
5093         gotPremove = 0;\r
5094         ClearPremoveHighlights();\r
5095       } else {\r
5096         /* First square clicked: start click-click move */\r
5097         SetHighlights(fromX, fromY, -1, -1);\r
5098       }\r
5099       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5100     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5101       /* Errant click; ignore */\r
5102       break;\r
5103     } else {\r
5104       /* Finish drag move. */\r
5105     if (appData.debugMode) {\r
5106         fprintf(debugFP, "release\n");\r
5107     }\r
5108       dragInfo.from.x = dragInfo.from.y = -1;\r
5109       toX = x;\r
5110       toY = y;\r
5111       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5112       appData.animate = appData.animate && !appData.animateDragging;\r
5113       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR, TRUE);\r
5114       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5115                 fromX = fromY = -1; \r
5116                 ClearHighlights();\r
5117                 DrawPosition(FALSE, boards[currentMove]);\r
5118                 appData.animate = saveAnimate;\r
5119                 break; \r
5120       } else \r
5121       if(moveType != ImpossibleMove) {\r
5122           /* [HGM] use move type to determine if move is promotion.\r
5123              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5124           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5125             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5126               appData.alwaysPromoteToQueen)) \r
5127                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5128           else \r
5129           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5130                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5131                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5132                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5133                     // kludge to temporarily execute move on display, wthout promotng yet\r
5134                     promotionChoice = TRUE;\r
5135                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5136                     boards[currentMove][toY][toX] = p;\r
5137                     DrawPosition(FALSE, boards[currentMove]);\r
5138                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5139                     boards[currentMove][toY][toX] = q;\r
5140                     appData.animate = saveAnimate;\r
5141                     DisplayMessage("Select piece from holdings", "");\r
5142                     break;\r
5143                   } else\r
5144                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5145           } else {\r
5146             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5147                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5148                                         moveType == WhiteCapturesEnPassant || \r
5149                                         moveType == BlackCapturesEnPassant   ) )\r
5150                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5151             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5152           }\r
5153       }\r
5154       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5155       appData.animate = saveAnimate;\r
5156       fromX = fromY = -1;\r
5157       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5158         ClearHighlights();\r
5159       }\r
5160       if (appData.animate || appData.animateDragging ||\r
5161           appData.highlightDragging || gotPremove) {\r
5162         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5163       }\r
5164     }\r
5165     dragInfo.start.x = dragInfo.start.y = -1; \r
5166     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5167     break;\r
5168 \r
5169   case WM_MOUSEMOVE:\r
5170     if ((appData.animateDragging || appData.highlightDragging)\r
5171         && (wParam & MK_LBUTTON)\r
5172         && dragInfo.from.x >= 0) \r
5173     {\r
5174       BOOL full_repaint = FALSE;\r
5175 \r
5176       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5177       if (appData.animateDragging) {\r
5178         dragInfo.pos = pt;\r
5179       }\r
5180       if (appData.highlightDragging) {\r
5181         SetHighlights(fromX, fromY, x, y);\r
5182         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5183             full_repaint = TRUE;\r
5184         }\r
5185       }\r
5186       \r
5187       DrawPosition( full_repaint, NULL);\r
5188       \r
5189       dragInfo.lastpos = dragInfo.pos;\r
5190     }\r
5191     break;\r
5192 \r
5193   case WM_MOUSEWHEEL: // [DM]\r
5194     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5195        /* Mouse Wheel is being rolled forward\r
5196         * Play moves forward\r
5197         */\r
5198        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5199                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5200        /* Mouse Wheel is being rolled backward\r
5201         * Play moves backward\r
5202         */\r
5203        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5204                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5205     }\r
5206     break;\r
5207 \r
5208   case WM_MBUTTONDOWN:\r
5209   case WM_RBUTTONDOWN:\r
5210     ErrorPopDown();\r
5211     ReleaseCapture();\r
5212     fromX = fromY = -1;\r
5213     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5214     dragInfo.start.x = dragInfo.start.y = -1;\r
5215     dragInfo.from = dragInfo.start;\r
5216     dragInfo.lastpos = dragInfo.pos;\r
5217     if (appData.highlightDragging) {\r
5218       ClearHighlights();\r
5219     }\r
5220     if(y == -2) {\r
5221       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5222       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5223           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5224       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5225           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5226       }\r
5227     }\r
5228     DrawPosition(TRUE, NULL);\r
5229 \r
5230     switch (gameMode) {\r
5231     case EditPosition:\r
5232     case IcsExamining:\r
5233       if (x < 0 || y < 0) break;\r
5234       fromX = x;\r
5235       fromY = y;\r
5236       if (message == WM_MBUTTONDOWN) {\r
5237         buttonCount = 3;  /* even if system didn't think so */\r
5238         if (wParam & MK_SHIFT) \r
5239           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5240         else\r
5241           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5242       } else { /* message == WM_RBUTTONDOWN */\r
5243         /* Just have one menu, on the right button.  Windows users don't\r
5244            think to try the middle one, and sometimes other software steals\r
5245            it, or it doesn't really exist. */\r
5246         if(gameInfo.variant != VariantShogi)\r
5247             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5248         else\r
5249             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5250       }\r
5251       break;\r
5252     case IcsPlayingWhite:\r
5253     case IcsPlayingBlack:\r
5254     case EditGame:\r
5255     case MachinePlaysWhite:\r
5256     case MachinePlaysBlack:\r
5257       if (appData.testLegality &&\r
5258           gameInfo.variant != VariantBughouse &&\r
5259           gameInfo.variant != VariantCrazyhouse) break;\r
5260       if (x < 0 || y < 0) break;\r
5261       fromX = x;\r
5262       fromY = y;\r
5263       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5264       SetupDropMenu(hmenu);\r
5265       MenuPopup(hwnd, pt, hmenu, -1);\r
5266       break;\r
5267     default:\r
5268       break;\r
5269     }\r
5270     break;\r
5271   }\r
5272 \r
5273   recursive--;\r
5274 }\r
5275 \r
5276 /* Preprocess messages for buttons in main window */\r
5277 LRESULT CALLBACK\r
5278 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5279 {\r
5280   int id = GetWindowLong(hwnd, GWL_ID);\r
5281   int i, dir;\r
5282 \r
5283   for (i=0; i<N_BUTTONS; i++) {\r
5284     if (buttonDesc[i].id == id) break;\r
5285   }\r
5286   if (i == N_BUTTONS) return 0;\r
5287   switch (message) {\r
5288   case WM_KEYDOWN:\r
5289     switch (wParam) {\r
5290     case VK_LEFT:\r
5291     case VK_RIGHT:\r
5292       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5293       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5294       return TRUE;\r
5295     }\r
5296     break;\r
5297   case WM_CHAR:\r
5298     switch (wParam) {\r
5299     case '\r':\r
5300       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5301       return TRUE;\r
5302     default:\r
5303       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5304         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5305         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5306         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5307         SetFocus(h);\r
5308         SendMessage(h, WM_CHAR, wParam, lParam);\r
5309         return TRUE;\r
5310       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5311         PopUpMoveDialog((char)wParam);\r
5312       }\r
5313       break;\r
5314     }\r
5315     break;\r
5316   }\r
5317   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5318 }\r
5319 \r
5320 /* Process messages for Promotion dialog box */\r
5321 LRESULT CALLBACK\r
5322 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5323 {\r
5324   char promoChar;\r
5325 \r
5326   switch (message) {\r
5327   case WM_INITDIALOG: /* message: initialize dialog box */\r
5328     /* Center the dialog over the application window */\r
5329     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5330     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5331       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5332        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5333                SW_SHOW : SW_HIDE);\r
5334     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5335     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5336        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5337          PieceToChar(WhiteAngel) != '~') ||\r
5338         (PieceToChar(BlackAngel) >= 'A' &&\r
5339          PieceToChar(BlackAngel) != '~')   ) ?\r
5340                SW_SHOW : SW_HIDE);\r
5341     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5342        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5343          PieceToChar(WhiteMarshall) != '~') ||\r
5344         (PieceToChar(BlackMarshall) >= 'A' &&\r
5345          PieceToChar(BlackMarshall) != '~')   ) ?\r
5346                SW_SHOW : SW_HIDE);\r
5347     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5348     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5349        gameInfo.variant != VariantShogi ?\r
5350                SW_SHOW : SW_HIDE);\r
5351     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5352        gameInfo.variant != VariantShogi ?\r
5353                SW_SHOW : SW_HIDE);\r
5354     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5355        gameInfo.variant == VariantShogi ?\r
5356                SW_SHOW : SW_HIDE);\r
5357     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5358        gameInfo.variant == VariantShogi ?\r
5359                SW_SHOW : SW_HIDE);\r
5360     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5361        gameInfo.variant == VariantSuper ?\r
5362                SW_SHOW : SW_HIDE);\r
5363     return TRUE;\r
5364 \r
5365   case WM_COMMAND: /* message: received a command */\r
5366     switch (LOWORD(wParam)) {\r
5367     case IDCANCEL:\r
5368       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5369       ClearHighlights();\r
5370       DrawPosition(FALSE, NULL);\r
5371       return TRUE;\r
5372     case PB_King:\r
5373       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5374       break;\r
5375     case PB_Queen:\r
5376       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5377       break;\r
5378     case PB_Rook:\r
5379       promoChar = PieceToChar(BlackRook);\r
5380       break;\r
5381     case PB_Bishop:\r
5382       promoChar = PieceToChar(BlackBishop);\r
5383       break;\r
5384     case PB_Chancellor:\r
5385       promoChar = PieceToChar(BlackMarshall);\r
5386       break;\r
5387     case PB_Archbishop:\r
5388       promoChar = PieceToChar(BlackAngel);\r
5389       break;\r
5390     case PB_Knight:\r
5391       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5392       break;\r
5393     default:\r
5394       return FALSE;\r
5395     }\r
5396     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5397     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5398        only show the popup when we are already sure the move is valid or\r
5399        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5400        will figure out it is a promotion from the promoChar. */\r
5401     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5402     if (!appData.highlightLastMove) {\r
5403       ClearHighlights();\r
5404       DrawPosition(FALSE, NULL);\r
5405     }\r
5406     return TRUE;\r
5407   }\r
5408   return FALSE;\r
5409 }\r
5410 \r
5411 /* Pop up promotion dialog */\r
5412 VOID\r
5413 PromotionPopup(HWND hwnd)\r
5414 {\r
5415   FARPROC lpProc;\r
5416 \r
5417   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5418   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5419     hwnd, (DLGPROC)lpProc);\r
5420   FreeProcInstance(lpProc);\r
5421 }\r
5422 \r
5423 /* Toggle ShowThinking */\r
5424 VOID\r
5425 ToggleShowThinking()\r
5426 {\r
5427   appData.showThinking = !appData.showThinking;\r
5428   ShowThinkingEvent();\r
5429 }\r
5430 \r
5431 VOID\r
5432 LoadGameDialog(HWND hwnd, char* title)\r
5433 {\r
5434   UINT number = 0;\r
5435   FILE *f;\r
5436   char fileTitle[MSG_SIZ];\r
5437   f = OpenFileDialog(hwnd, "rb", "",\r
5438                      appData.oldSaveStyle ? "gam" : "pgn",\r
5439                      GAME_FILT,\r
5440                      title, &number, fileTitle, NULL);\r
5441   if (f != NULL) {\r
5442     cmailMsgLoaded = FALSE;\r
5443     if (number == 0) {\r
5444       int error = GameListBuild(f);\r
5445       if (error) {\r
5446         DisplayError("Cannot build game list", error);\r
5447       } else if (!ListEmpty(&gameList) &&\r
5448                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5449         GameListPopUp(f, fileTitle);\r
5450         return;\r
5451       }\r
5452       GameListDestroy();\r
5453       number = 1;\r
5454     }\r
5455     LoadGame(f, number, fileTitle, FALSE);\r
5456   }\r
5457 }\r
5458 \r
5459 void UpdateICSWidth(HWND hText)\r
5460 {\r
5461         HDC hdc;\r
5462         TEXTMETRIC tm;\r
5463         RECT rc;\r
5464         HFONT hfont, hold_font;\r
5465         \r
5466         // get the text metrics\r
5467         hdc = GetDC(hText);\r
5468         hfont = CreateFontIndirect(&font[boardSize][CONSOLE_FONT]->lf);\r
5469         hold_font = SelectObject(hdc, hfont);\r
5470         GetTextMetrics(hdc, &tm);\r
5471         SelectObject(hdc, hold_font);\r
5472         DeleteObject(hfont);\r
5473         ReleaseDC(hText, hdc);\r
5474 \r
5475         // get the rectangle\r
5476         SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
5477 \r
5478         // update the width\r
5479         ics_update_width((rc.right-rc.left) / tm.tmAveCharWidth);\r
5480 }\r
5481 \r
5482 VOID\r
5483 ChangedConsoleFont()\r
5484 {\r
5485   CHARFORMAT cfmt;\r
5486   CHARRANGE tmpsel, sel;\r
5487   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5488   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5489   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5490   PARAFORMAT paraf;\r
5491 \r
5492   cfmt.cbSize = sizeof(CHARFORMAT);\r
5493   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5494   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5495   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5496    * size.  This was undocumented in the version of MSVC++ that I had\r
5497    * when I wrote the code, but is apparently documented now.\r
5498    */\r
5499   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5500   cfmt.bCharSet = f->lf.lfCharSet;\r
5501   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5502   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5503   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5504   /* Why are the following seemingly needed too? */\r
5505   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5506   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5507   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5508   tmpsel.cpMin = 0;\r
5509   tmpsel.cpMax = -1; /*999999?*/\r
5510   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5511   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5512   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5513    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5514    */\r
5515   paraf.cbSize = sizeof(paraf);\r
5516   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5517   paraf.dxStartIndent = 0;\r
5518   paraf.dxOffset = WRAP_INDENT;\r
5519   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5520   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5521   UpdateICSWidth(hText);\r
5522 }\r
5523 \r
5524 /*---------------------------------------------------------------------------*\\r
5525  *\r
5526  * Window Proc for main window\r
5527  *\r
5528 \*---------------------------------------------------------------------------*/\r
5529 \r
5530 /* Process messages for main window, etc. */\r
5531 LRESULT CALLBACK\r
5532 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5533 {\r
5534   FARPROC lpProc;\r
5535   int wmId, wmEvent;\r
5536   char *defName;\r
5537   FILE *f;\r
5538   UINT number;\r
5539   char fileTitle[MSG_SIZ];\r
5540   char buf[MSG_SIZ];\r
5541   static SnapData sd;\r
5542 \r
5543   switch (message) {\r
5544 \r
5545   case WM_PAINT: /* message: repaint portion of window */\r
5546     PaintProc(hwnd);\r
5547     break;\r
5548 \r
5549   case WM_ERASEBKGND:\r
5550     if (IsIconic(hwnd)) {\r
5551       /* Cheat; change the message */\r
5552       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5553     } else {\r
5554       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5555     }\r
5556     break;\r
5557 \r
5558   case WM_LBUTTONDOWN:\r
5559   case WM_MBUTTONDOWN:\r
5560   case WM_RBUTTONDOWN:\r
5561   case WM_LBUTTONUP:\r
5562   case WM_MBUTTONUP:\r
5563   case WM_RBUTTONUP:\r
5564   case WM_MOUSEMOVE:\r
5565   case WM_MOUSEWHEEL:\r
5566     MouseEvent(hwnd, message, wParam, lParam);\r
5567     break;\r
5568 \r
5569   JAWS_KB_NAVIGATION\r
5570 \r
5571   case WM_CHAR:\r
5572     \r
5573     JAWS_ALT_INTERCEPT\r
5574 \r
5575     if (appData.icsActive && (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9')) { \r
5576         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5577         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5578         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5579         SetFocus(h);\r
5580         SendMessage(h, message, wParam, lParam);\r
5581     } else if(lParam != KF_REPEAT) {\r
5582         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5583                 PopUpMoveDialog((char)wParam);\r
5584         } else if((char)wParam == 003) CopyGameToClipboard();\r
5585          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5586     }\r
5587 \r
5588     break;\r
5589 \r
5590   case WM_PALETTECHANGED:\r
5591     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5592       int nnew;\r
5593       HDC hdc = GetDC(hwndMain);\r
5594       SelectPalette(hdc, hPal, TRUE);\r
5595       nnew = RealizePalette(hdc);\r
5596       if (nnew > 0) {\r
5597         paletteChanged = TRUE;\r
5598         InvalidateRect(hwnd, &boardRect, FALSE);\r
5599       }\r
5600       ReleaseDC(hwnd, hdc);\r
5601     }\r
5602     break;\r
5603 \r
5604   case WM_QUERYNEWPALETTE:\r
5605     if (!appData.monoMode /*&& paletteChanged*/) {\r
5606       int nnew;\r
5607       HDC hdc = GetDC(hwndMain);\r
5608       paletteChanged = FALSE;\r
5609       SelectPalette(hdc, hPal, FALSE);\r
5610       nnew = RealizePalette(hdc);\r
5611       if (nnew > 0) {\r
5612         InvalidateRect(hwnd, &boardRect, FALSE);\r
5613       }\r
5614       ReleaseDC(hwnd, hdc);\r
5615       return TRUE;\r
5616     }\r
5617     return FALSE;\r
5618 \r
5619   case WM_COMMAND: /* message: command from application menu */\r
5620     wmId    = LOWORD(wParam);\r
5621     wmEvent = HIWORD(wParam);\r
5622 \r
5623     switch (wmId) {\r
5624     case IDM_NewGame:\r
5625       ResetGameEvent();\r
5626       AnalysisPopDown();\r
5627       SAY("new game enter a move to play against the computer with white");\r
5628       break;\r
5629 \r
5630     case IDM_NewGameFRC:\r
5631       if( NewGameFRC() == 0 ) {\r
5632         ResetGameEvent();\r
5633         AnalysisPopDown();\r
5634       }\r
5635       break;\r
5636 \r
5637     case IDM_NewVariant:\r
5638       NewVariantPopup(hwnd);\r
5639       break;\r
5640 \r
5641     case IDM_LoadGame:\r
5642       LoadGameDialog(hwnd, "Load Game from File");\r
5643       break;\r
5644 \r
5645     case IDM_LoadNextGame:\r
5646       ReloadGame(1);\r
5647       break;\r
5648 \r
5649     case IDM_LoadPrevGame:\r
5650       ReloadGame(-1);\r
5651       break;\r
5652 \r
5653     case IDM_ReloadGame:\r
5654       ReloadGame(0);\r
5655       break;\r
5656 \r
5657     case IDM_LoadPosition:\r
5658       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5659         Reset(FALSE, TRUE);\r
5660       }\r
5661       number = 1;\r
5662       f = OpenFileDialog(hwnd, "rb", "",\r
5663                          appData.oldSaveStyle ? "pos" : "fen",\r
5664                          POSITION_FILT,\r
5665                          "Load Position from File", &number, fileTitle, NULL);\r
5666       if (f != NULL) {\r
5667         LoadPosition(f, number, fileTitle);\r
5668       }\r
5669       break;\r
5670 \r
5671     case IDM_LoadNextPosition:\r
5672       ReloadPosition(1);\r
5673       break;\r
5674 \r
5675     case IDM_LoadPrevPosition:\r
5676       ReloadPosition(-1);\r
5677       break;\r
5678 \r
5679     case IDM_ReloadPosition:\r
5680       ReloadPosition(0);\r
5681       break;\r
5682 \r
5683     case IDM_SaveGame:\r
5684       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5685       f = OpenFileDialog(hwnd, "a", defName,\r
5686                          appData.oldSaveStyle ? "gam" : "pgn",\r
5687                          GAME_FILT,\r
5688                          "Save Game to File", NULL, fileTitle, NULL);\r
5689       if (f != NULL) {\r
5690         SaveGame(f, 0, "");\r
5691       }\r
5692       break;\r
5693 \r
5694     case IDM_SavePosition:\r
5695       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5696       f = OpenFileDialog(hwnd, "a", defName,\r
5697                          appData.oldSaveStyle ? "pos" : "fen",\r
5698                          POSITION_FILT,\r
5699                          "Save Position to File", NULL, fileTitle, NULL);\r
5700       if (f != NULL) {\r
5701         SavePosition(f, 0, "");\r
5702       }\r
5703       break;\r
5704 \r
5705     case IDM_SaveDiagram:\r
5706       defName = "diagram";\r
5707       f = OpenFileDialog(hwnd, "wb", defName,\r
5708                          "bmp",\r
5709                          DIAGRAM_FILT,\r
5710                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5711       if (f != NULL) {\r
5712         SaveDiagram(f);\r
5713       }\r
5714       break;\r
5715 \r
5716     case IDM_CopyGame:\r
5717       CopyGameToClipboard();\r
5718       break;\r
5719 \r
5720     case IDM_PasteGame:\r
5721       PasteGameFromClipboard();\r
5722       break;\r
5723 \r
5724     case IDM_CopyGameListToClipboard:\r
5725       CopyGameListToClipboard();\r
5726       break;\r
5727 \r
5728     /* [AS] Autodetect FEN or PGN data */\r
5729     case IDM_PasteAny:\r
5730       PasteGameOrFENFromClipboard();\r
5731       break;\r
5732 \r
5733     /* [AS] Move history */\r
5734     case IDM_ShowMoveHistory:\r
5735         if( MoveHistoryIsUp() ) {\r
5736             MoveHistoryPopDown();\r
5737         }\r
5738         else {\r
5739             MoveHistoryPopUp();\r
5740         }\r
5741         break;\r
5742 \r
5743     /* [AS] Eval graph */\r
5744     case IDM_ShowEvalGraph:\r
5745         if( EvalGraphIsUp() ) {\r
5746             EvalGraphPopDown();\r
5747         }\r
5748         else {\r
5749             EvalGraphPopUp();\r
5750             SetFocus(hwndMain);\r
5751         }\r
5752         break;\r
5753 \r
5754     /* [AS] Engine output */\r
5755     case IDM_ShowEngineOutput:\r
5756         if( EngineOutputIsUp() ) {\r
5757             EngineOutputPopDown();\r
5758         }\r
5759         else {\r
5760             EngineOutputPopUp();\r
5761         }\r
5762         break;\r
5763 \r
5764     /* [AS] User adjudication */\r
5765     case IDM_UserAdjudication_White:\r
5766         UserAdjudicationEvent( +1 );\r
5767         break;\r
5768 \r
5769     case IDM_UserAdjudication_Black:\r
5770         UserAdjudicationEvent( -1 );\r
5771         break;\r
5772 \r
5773     case IDM_UserAdjudication_Draw:\r
5774         UserAdjudicationEvent( 0 );\r
5775         break;\r
5776 \r
5777     /* [AS] Game list options dialog */\r
5778     case IDM_GameListOptions:\r
5779       GameListOptions();\r
5780       break;\r
5781 \r
5782     case IDM_NewChat:\r
5783       ChatPopUp();\r
5784       break;\r
5785 \r
5786     case IDM_CopyPosition:\r
5787       CopyFENToClipboard();\r
5788       break;\r
5789 \r
5790     case IDM_PastePosition:\r
5791       PasteFENFromClipboard();\r
5792       break;\r
5793 \r
5794     case IDM_MailMove:\r
5795       MailMoveEvent();\r
5796       break;\r
5797 \r
5798     case IDM_ReloadCMailMsg:\r
5799       Reset(TRUE, TRUE);\r
5800       ReloadCmailMsgEvent(FALSE);\r
5801       break;\r
5802 \r
5803     case IDM_Minimize:\r
5804       ShowWindow(hwnd, SW_MINIMIZE);\r
5805       break;\r
5806 \r
5807     case IDM_Exit:\r
5808       ExitEvent(0);\r
5809       break;\r
5810 \r
5811     case IDM_MachineWhite:\r
5812       MachineWhiteEvent();\r
5813       /*\r
5814        * refresh the tags dialog only if it's visible\r
5815        */\r
5816       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5817           char *tags;\r
5818           tags = PGNTags(&gameInfo);\r
5819           TagsPopUp(tags, CmailMsg());\r
5820           free(tags);\r
5821       }\r
5822       SAY("computer starts playing white");\r
5823       break;\r
5824 \r
5825     case IDM_MachineBlack:\r
5826       MachineBlackEvent();\r
5827       /*\r
5828        * refresh the tags dialog only if it's visible\r
5829        */\r
5830       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5831           char *tags;\r
5832           tags = PGNTags(&gameInfo);\r
5833           TagsPopUp(tags, CmailMsg());\r
5834           free(tags);\r
5835       }\r
5836       SAY("computer starts playing black");\r
5837       break;\r
5838 \r
5839     case IDM_TwoMachines:\r
5840       TwoMachinesEvent();\r
5841       /*\r
5842        * refresh the tags dialog only if it's visible\r
5843        */\r
5844       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5845           char *tags;\r
5846           tags = PGNTags(&gameInfo);\r
5847           TagsPopUp(tags, CmailMsg());\r
5848           free(tags);\r
5849       }\r
5850       SAY("programs start playing each other");\r
5851       break;\r
5852 \r
5853     case IDM_AnalysisMode:\r
5854       if (!first.analysisSupport) {\r
5855         sprintf(buf, "%s does not support analysis", first.tidy);\r
5856         DisplayError(buf, 0);\r
5857       } else {\r
5858         SAY("analyzing current position");\r
5859         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5860         if (appData.icsActive) {\r
5861                if (gameMode != IcsObserving) {\r
5862                        sprintf(buf, "You are not observing a game");\r
5863                        DisplayError(buf, 0);\r
5864                        /* secure check */\r
5865                        if (appData.icsEngineAnalyze) {\r
5866                                if (appData.debugMode) \r
5867                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5868                                ExitAnalyzeMode();\r
5869                                ModeHighlight();\r
5870                                break;\r
5871                        }\r
5872                        break;\r
5873                } else {\r
5874                        /* if enable, user want disable icsEngineAnalyze */\r
5875                        if (appData.icsEngineAnalyze) {\r
5876                                ExitAnalyzeMode();\r
5877                                ModeHighlight();\r
5878                                break;\r
5879                        }\r
5880                        appData.icsEngineAnalyze = TRUE;\r
5881                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5882                }\r
5883         } \r
5884         if (!appData.showThinking) ToggleShowThinking();\r
5885         AnalyzeModeEvent();\r
5886       }\r
5887       break;\r
5888 \r
5889     case IDM_AnalyzeFile:\r
5890       if (!first.analysisSupport) {\r
5891         char buf[MSG_SIZ];\r
5892         sprintf(buf, "%s does not support analysis", first.tidy);\r
5893         DisplayError(buf, 0);\r
5894       } else {\r
5895         if (!appData.showThinking) ToggleShowThinking();\r
5896         AnalyzeFileEvent();\r
5897         LoadGameDialog(hwnd, "Analyze Game from File");\r
5898         AnalysisPeriodicEvent(1);\r
5899       }\r
5900       break;\r
5901 \r
5902     case IDM_IcsClient:\r
5903       IcsClientEvent();\r
5904       break;\r
5905 \r
5906     case IDM_EditGame:\r
5907       EditGameEvent();\r
5908       SAY("edit game");\r
5909       break;\r
5910 \r
5911     case IDM_EditPosition:\r
5912       EditPositionEvent();\r
5913       SAY("to set up a position type a FEN");\r
5914       break;\r
5915 \r
5916     case IDM_Training:\r
5917       TrainingEvent();\r
5918       break;\r
5919 \r
5920     case IDM_ShowGameList:\r
5921       ShowGameListProc();\r
5922       break;\r
5923 \r
5924     case IDM_EditTags:\r
5925       EditTagsProc();\r
5926       break;\r
5927 \r
5928     case IDM_EditComment:\r
5929       if (commentDialogUp && editComment) {\r
5930         CommentPopDown();\r
5931       } else {\r
5932         EditCommentEvent();\r
5933       }\r
5934       break;\r
5935 \r
5936     case IDM_Pause:\r
5937       PauseEvent();\r
5938       break;\r
5939 \r
5940     case IDM_Accept:\r
5941       AcceptEvent();\r
5942       break;\r
5943 \r
5944     case IDM_Decline:\r
5945       DeclineEvent();\r
5946       break;\r
5947 \r
5948     case IDM_Rematch:\r
5949       RematchEvent();\r
5950       break;\r
5951 \r
5952     case IDM_CallFlag:\r
5953       CallFlagEvent();\r
5954       break;\r
5955 \r
5956     case IDM_Draw:\r
5957       DrawEvent();\r
5958       break;\r
5959 \r
5960     case IDM_Adjourn:\r
5961       AdjournEvent();\r
5962       break;\r
5963 \r
5964     case IDM_Abort:\r
5965       AbortEvent();\r
5966       break;\r
5967 \r
5968     case IDM_Resign:\r
5969       ResignEvent();\r
5970       break;\r
5971 \r
5972     case IDM_StopObserving:\r
5973       StopObservingEvent();\r
5974       break;\r
5975 \r
5976     case IDM_StopExamining:\r
5977       StopExaminingEvent();\r
5978       break;\r
5979 \r
5980     case IDM_TypeInMove:\r
5981       PopUpMoveDialog('\000');\r
5982       break;\r
5983 \r
5984     case IDM_TypeInName:\r
5985       PopUpNameDialog('\000');\r
5986       break;\r
5987 \r
5988     case IDM_Backward:\r
5989       BackwardEvent();\r
5990       SetFocus(hwndMain);\r
5991       break;\r
5992 \r
5993     JAWS_MENU_ITEMS\r
5994 \r
5995     case IDM_Forward:\r
5996       ForwardEvent();\r
5997       SetFocus(hwndMain);\r
5998       break;\r
5999 \r
6000     case IDM_ToStart:\r
6001       ToStartEvent();\r
6002       SetFocus(hwndMain);\r
6003       break;\r
6004 \r
6005     case IDM_ToEnd:\r
6006       ToEndEvent();\r
6007       SetFocus(hwndMain);\r
6008       break;\r
6009 \r
6010     case IDM_Revert:\r
6011       RevertEvent();\r
6012       break;\r
6013 \r
6014     case IDM_TruncateGame:\r
6015       TruncateGameEvent();\r
6016       break;\r
6017 \r
6018     case IDM_MoveNow:\r
6019       MoveNowEvent();\r
6020       break;\r
6021 \r
6022     case IDM_RetractMove:\r
6023       RetractMoveEvent();\r
6024       break;\r
6025 \r
6026     case IDM_FlipView:\r
6027       flipView = !flipView;\r
6028       DrawPosition(FALSE, NULL);\r
6029       break;\r
6030 \r
6031     case IDM_FlipClock:\r
6032       flipClock = !flipClock;\r
6033       DisplayBothClocks();\r
6034       DrawPosition(FALSE, NULL);\r
6035       break;\r
6036 \r
6037     case IDM_MuteSounds:\r
6038       mute = !mute; // [HGM] mute: keep track of global muting variable\r
6039       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
6040                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
6041       break;\r
6042 \r
6043     case IDM_GeneralOptions:\r
6044       GeneralOptionsPopup(hwnd);\r
6045       DrawPosition(TRUE, NULL);\r
6046       break;\r
6047 \r
6048     case IDM_BoardOptions:\r
6049       BoardOptionsPopup(hwnd);\r
6050       break;\r
6051 \r
6052     case IDM_EnginePlayOptions:\r
6053       EnginePlayOptionsPopup(hwnd);\r
6054       break;\r
6055 \r
6056     case IDM_Engine1Options:\r
6057       EngineOptionsPopup(hwnd, &first);\r
6058       break;\r
6059 \r
6060     case IDM_Engine2Options:\r
6061       EngineOptionsPopup(hwnd, &second);\r
6062       break;\r
6063 \r
6064     case IDM_OptionsUCI:\r
6065       UciOptionsPopup(hwnd);\r
6066       break;\r
6067 \r
6068     case IDM_IcsOptions:\r
6069       IcsOptionsPopup(hwnd);\r
6070       break;\r
6071 \r
6072     case IDM_Fonts:\r
6073       FontsOptionsPopup(hwnd);\r
6074       break;\r
6075 \r
6076     case IDM_Sounds:\r
6077       SoundOptionsPopup(hwnd);\r
6078       break;\r
6079 \r
6080     case IDM_CommPort:\r
6081       CommPortOptionsPopup(hwnd);\r
6082       break;\r
6083 \r
6084     case IDM_LoadOptions:\r
6085       LoadOptionsPopup(hwnd);\r
6086       break;\r
6087 \r
6088     case IDM_SaveOptions:\r
6089       SaveOptionsPopup(hwnd);\r
6090       break;\r
6091 \r
6092     case IDM_TimeControl:\r
6093       TimeControlOptionsPopup(hwnd);\r
6094       break;\r
6095 \r
6096     case IDM_SaveSettings:\r
6097       SaveSettings(settingsFileName);\r
6098       break;\r
6099 \r
6100     case IDM_SaveSettingsOnExit:\r
6101       saveSettingsOnExit = !saveSettingsOnExit;\r
6102       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6103                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6104                                          MF_CHECKED : MF_UNCHECKED));\r
6105       break;\r
6106 \r
6107     case IDM_Hint:\r
6108       HintEvent();\r
6109       break;\r
6110 \r
6111     case IDM_Book:\r
6112       BookEvent();\r
6113       break;\r
6114 \r
6115     case IDM_AboutGame:\r
6116       AboutGameEvent();\r
6117       break;\r
6118 \r
6119     case IDM_Debug:\r
6120       appData.debugMode = !appData.debugMode;\r
6121       if (appData.debugMode) {\r
6122         char dir[MSG_SIZ];\r
6123         GetCurrentDirectory(MSG_SIZ, dir);\r
6124         SetCurrentDirectory(installDir);\r
6125         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6126         SetCurrentDirectory(dir);\r
6127         setbuf(debugFP, NULL);\r
6128       } else {\r
6129         fclose(debugFP);\r
6130         debugFP = NULL;\r
6131       }\r
6132       break;\r
6133 \r
6134     case IDM_HELPCONTENTS:\r
6135       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6136           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6137           MessageBox (GetFocus(),\r
6138                     "Unable to activate help",\r
6139                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6140       }\r
6141       break;\r
6142 \r
6143     case IDM_HELPSEARCH:\r
6144         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6145             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6146         MessageBox (GetFocus(),\r
6147                     "Unable to activate help",\r
6148                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6149       }\r
6150       break;\r
6151 \r
6152     case IDM_HELPHELP:\r
6153       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6154         MessageBox (GetFocus(),\r
6155                     "Unable to activate help",\r
6156                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6157       }\r
6158       break;\r
6159 \r
6160     case IDM_ABOUT:\r
6161       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6162       DialogBox(hInst, \r
6163         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6164         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6165       FreeProcInstance(lpProc);\r
6166       break;\r
6167 \r
6168     case IDM_DirectCommand1:\r
6169       AskQuestionEvent("Direct Command",\r
6170                        "Send to chess program:", "", "1");\r
6171       break;\r
6172     case IDM_DirectCommand2:\r
6173       AskQuestionEvent("Direct Command",\r
6174                        "Send to second chess program:", "", "2");\r
6175       break;\r
6176 \r
6177     case EP_WhitePawn:\r
6178       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6179       fromX = fromY = -1;\r
6180       break;\r
6181 \r
6182     case EP_WhiteKnight:\r
6183       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6184       fromX = fromY = -1;\r
6185       break;\r
6186 \r
6187     case EP_WhiteBishop:\r
6188       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6189       fromX = fromY = -1;\r
6190       break;\r
6191 \r
6192     case EP_WhiteRook:\r
6193       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6194       fromX = fromY = -1;\r
6195       break;\r
6196 \r
6197     case EP_WhiteQueen:\r
6198       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6199       fromX = fromY = -1;\r
6200       break;\r
6201 \r
6202     case EP_WhiteFerz:\r
6203       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6204       fromX = fromY = -1;\r
6205       break;\r
6206 \r
6207     case EP_WhiteWazir:\r
6208       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6209       fromX = fromY = -1;\r
6210       break;\r
6211 \r
6212     case EP_WhiteAlfil:\r
6213       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6214       fromX = fromY = -1;\r
6215       break;\r
6216 \r
6217     case EP_WhiteCannon:\r
6218       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6219       fromX = fromY = -1;\r
6220       break;\r
6221 \r
6222     case EP_WhiteCardinal:\r
6223       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6224       fromX = fromY = -1;\r
6225       break;\r
6226 \r
6227     case EP_WhiteMarshall:\r
6228       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6229       fromX = fromY = -1;\r
6230       break;\r
6231 \r
6232     case EP_WhiteKing:\r
6233       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6234       fromX = fromY = -1;\r
6235       break;\r
6236 \r
6237     case EP_BlackPawn:\r
6238       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6239       fromX = fromY = -1;\r
6240       break;\r
6241 \r
6242     case EP_BlackKnight:\r
6243       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6244       fromX = fromY = -1;\r
6245       break;\r
6246 \r
6247     case EP_BlackBishop:\r
6248       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6249       fromX = fromY = -1;\r
6250       break;\r
6251 \r
6252     case EP_BlackRook:\r
6253       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6254       fromX = fromY = -1;\r
6255       break;\r
6256 \r
6257     case EP_BlackQueen:\r
6258       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6259       fromX = fromY = -1;\r
6260       break;\r
6261 \r
6262     case EP_BlackFerz:\r
6263       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6264       fromX = fromY = -1;\r
6265       break;\r
6266 \r
6267     case EP_BlackWazir:\r
6268       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6269       fromX = fromY = -1;\r
6270       break;\r
6271 \r
6272     case EP_BlackAlfil:\r
6273       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6274       fromX = fromY = -1;\r
6275       break;\r
6276 \r
6277     case EP_BlackCannon:\r
6278       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6279       fromX = fromY = -1;\r
6280       break;\r
6281 \r
6282     case EP_BlackCardinal:\r
6283       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6284       fromX = fromY = -1;\r
6285       break;\r
6286 \r
6287     case EP_BlackMarshall:\r
6288       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6289       fromX = fromY = -1;\r
6290       break;\r
6291 \r
6292     case EP_BlackKing:\r
6293       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6294       fromX = fromY = -1;\r
6295       break;\r
6296 \r
6297     case EP_EmptySquare:\r
6298       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6299       fromX = fromY = -1;\r
6300       break;\r
6301 \r
6302     case EP_ClearBoard:\r
6303       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6304       fromX = fromY = -1;\r
6305       break;\r
6306 \r
6307     case EP_White:\r
6308       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6309       fromX = fromY = -1;\r
6310       break;\r
6311 \r
6312     case EP_Black:\r
6313       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6314       fromX = fromY = -1;\r
6315       break;\r
6316 \r
6317     case EP_Promote:\r
6318       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6319       fromX = fromY = -1;\r
6320       break;\r
6321 \r
6322     case EP_Demote:\r
6323       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6324       fromX = fromY = -1;\r
6325       break;\r
6326 \r
6327     case DP_Pawn:\r
6328       DropMenuEvent(WhitePawn, fromX, fromY);\r
6329       fromX = fromY = -1;\r
6330       break;\r
6331 \r
6332     case DP_Knight:\r
6333       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6334       fromX = fromY = -1;\r
6335       break;\r
6336 \r
6337     case DP_Bishop:\r
6338       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6339       fromX = fromY = -1;\r
6340       break;\r
6341 \r
6342     case DP_Rook:\r
6343       DropMenuEvent(WhiteRook, fromX, fromY);\r
6344       fromX = fromY = -1;\r
6345       break;\r
6346 \r
6347     case DP_Queen:\r
6348       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6349       fromX = fromY = -1;\r
6350       break;\r
6351 \r
6352     default:\r
6353       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6354     }\r
6355     break;\r
6356 \r
6357   case WM_TIMER:\r
6358     switch (wParam) {\r
6359     case CLOCK_TIMER_ID:\r
6360       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6361       clockTimerEvent = 0;\r
6362       DecrementClocks(); /* call into back end */\r
6363       break;\r
6364     case LOAD_GAME_TIMER_ID:\r
6365       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6366       loadGameTimerEvent = 0;\r
6367       AutoPlayGameLoop(); /* call into back end */\r
6368       break;\r
6369     case ANALYSIS_TIMER_ID:\r
6370       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6371                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6372         AnalysisPeriodicEvent(0);\r
6373       } else {\r
6374         KillTimer(hwnd, analysisTimerEvent);\r
6375         analysisTimerEvent = 0;\r
6376       }\r
6377       break;\r
6378     case DELAYED_TIMER_ID:\r
6379       KillTimer(hwnd, delayedTimerEvent);\r
6380       delayedTimerEvent = 0;\r
6381       delayedTimerCallback();\r
6382       break;\r
6383     }\r
6384     break;\r
6385 \r
6386   case WM_USER_Input:\r
6387     InputEvent(hwnd, message, wParam, lParam);\r
6388     break;\r
6389 \r
6390   /* [AS] Also move "attached" child windows */\r
6391   case WM_WINDOWPOSCHANGING:\r
6392 \r
6393     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6394         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6395 \r
6396         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6397             /* Window is moving */\r
6398             RECT rcMain;\r
6399 \r
6400 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6401             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6402             rcMain.right  = boardX + winWidth;\r
6403             rcMain.top    = boardY;\r
6404             rcMain.bottom = boardY + winHeight;\r
6405             \r
6406             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6407             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6408             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6409             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6410             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6411             boardX = lpwp->x;\r
6412             boardY = lpwp->y;\r
6413         }\r
6414     }\r
6415     break;\r
6416 \r
6417   /* [AS] Snapping */\r
6418   case WM_ENTERSIZEMOVE:\r
6419     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6420     if (hwnd == hwndMain) {\r
6421       doingSizing = TRUE;\r
6422       lastSizing = 0;\r
6423     }\r
6424     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6425     break;\r
6426 \r
6427   case WM_SIZING:\r
6428     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6429     if (hwnd == hwndMain) {\r
6430       lastSizing = wParam;\r
6431     }\r
6432     break;\r
6433 \r
6434   case WM_MOVING:\r
6435     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6436       return OnMoving( &sd, hwnd, wParam, lParam );\r
6437 \r
6438   case WM_EXITSIZEMOVE:\r
6439     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6440     if (hwnd == hwndMain) {\r
6441       RECT client;\r
6442       doingSizing = FALSE;\r
6443       InvalidateRect(hwnd, &boardRect, FALSE);\r
6444       GetClientRect(hwnd, &client);\r
6445       ResizeBoard(client.right, client.bottom, lastSizing);\r
6446       lastSizing = 0;\r
6447       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6448     }\r
6449     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6450     break;\r
6451 \r
6452   case WM_DESTROY: /* message: window being destroyed */\r
6453     PostQuitMessage(0);\r
6454     break;\r
6455 \r
6456   case WM_CLOSE:\r
6457     if (hwnd == hwndMain) {\r
6458       ExitEvent(0);\r
6459     }\r
6460     break;\r
6461 \r
6462   default:      /* Passes it on if unprocessed */\r
6463     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6464   }\r
6465   return 0;\r
6466 }\r
6467 \r
6468 /*---------------------------------------------------------------------------*\\r
6469  *\r
6470  * Misc utility routines\r
6471  *\r
6472 \*---------------------------------------------------------------------------*/\r
6473 \r
6474 /*\r
6475  * Decent random number generator, at least not as bad as Windows\r
6476  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6477  */\r
6478 unsigned int randstate;\r
6479 \r
6480 int\r
6481 myrandom(void)\r
6482 {\r
6483   randstate = randstate * 1664525 + 1013904223;\r
6484   return (int) randstate & 0x7fffffff;\r
6485 }\r
6486 \r
6487 void\r
6488 mysrandom(unsigned int seed)\r
6489 {\r
6490   randstate = seed;\r
6491 }\r
6492 \r
6493 \r
6494 /* \r
6495  * returns TRUE if user selects a different color, FALSE otherwise \r
6496  */\r
6497 \r
6498 BOOL\r
6499 ChangeColor(HWND hwnd, COLORREF *which)\r
6500 {\r
6501   static BOOL firstTime = TRUE;\r
6502   static DWORD customColors[16];\r
6503   CHOOSECOLOR cc;\r
6504   COLORREF newcolor;\r
6505   int i;\r
6506   ColorClass ccl;\r
6507 \r
6508   if (firstTime) {\r
6509     /* Make initial colors in use available as custom colors */\r
6510     /* Should we put the compiled-in defaults here instead? */\r
6511     i = 0;\r
6512     customColors[i++] = lightSquareColor & 0xffffff;\r
6513     customColors[i++] = darkSquareColor & 0xffffff;\r
6514     customColors[i++] = whitePieceColor & 0xffffff;\r
6515     customColors[i++] = blackPieceColor & 0xffffff;\r
6516     customColors[i++] = highlightSquareColor & 0xffffff;\r
6517     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6518 \r
6519     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6520       customColors[i++] = textAttribs[ccl].color;\r
6521     }\r
6522     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6523     firstTime = FALSE;\r
6524   }\r
6525 \r
6526   cc.lStructSize = sizeof(cc);\r
6527   cc.hwndOwner = hwnd;\r
6528   cc.hInstance = NULL;\r
6529   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6530   cc.lpCustColors = (LPDWORD) customColors;\r
6531   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6532 \r
6533   if (!ChooseColor(&cc)) return FALSE;\r
6534 \r
6535   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6536   if (newcolor == *which) return FALSE;\r
6537   *which = newcolor;\r
6538   return TRUE;\r
6539 \r
6540   /*\r
6541   InitDrawingColors();\r
6542   InvalidateRect(hwnd, &boardRect, FALSE);\r
6543   */\r
6544 }\r
6545 \r
6546 BOOLEAN\r
6547 MyLoadSound(MySound *ms)\r
6548 {\r
6549   BOOL ok = FALSE;\r
6550   struct stat st;\r
6551   FILE *f;\r
6552 \r
6553   if (ms->data) free(ms->data);\r
6554   ms->data = NULL;\r
6555 \r
6556   switch (ms->name[0]) {\r
6557   case NULLCHAR:\r
6558     /* Silence */\r
6559     ok = TRUE;\r
6560     break;\r
6561   case '$':\r
6562     /* System sound from Control Panel.  Don't preload here. */\r
6563     ok = TRUE;\r
6564     break;\r
6565   case '!':\r
6566     if (ms->name[1] == NULLCHAR) {\r
6567       /* "!" alone = silence */\r
6568       ok = TRUE;\r
6569     } else {\r
6570       /* Builtin wave resource.  Error if not found. */\r
6571       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6572       if (h == NULL) break;\r
6573       ms->data = (void *)LoadResource(hInst, h);\r
6574       if (h == NULL) break;\r
6575       ok = TRUE;\r
6576     }\r
6577     break;\r
6578   default:\r
6579     /* .wav file.  Error if not found. */\r
6580     f = fopen(ms->name, "rb");\r
6581     if (f == NULL) break;\r
6582     if (fstat(fileno(f), &st) < 0) break;\r
6583     ms->data = malloc(st.st_size);\r
6584     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6585     fclose(f);\r
6586     ok = TRUE;\r
6587     break;\r
6588   }\r
6589   if (!ok) {\r
6590     char buf[MSG_SIZ];\r
6591     sprintf(buf, "Error loading sound %s", ms->name);\r
6592     DisplayError(buf, GetLastError());\r
6593   }\r
6594   return ok;\r
6595 }\r
6596 \r
6597 BOOLEAN\r
6598 MyPlaySound(MySound *ms)\r
6599 {\r
6600   BOOLEAN ok = FALSE;\r
6601 \r
6602   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6603   switch (ms->name[0]) {\r
6604   case NULLCHAR:\r
6605         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6606     /* Silence */\r
6607     ok = TRUE;\r
6608     break;\r
6609   case '$':\r
6610     /* System sound from Control Panel (deprecated feature).\r
6611        "$" alone or an unset sound name gets default beep (still in use). */\r
6612     if (ms->name[1]) {\r
6613       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6614     }\r
6615     if (!ok) ok = MessageBeep(MB_OK);\r
6616     break; \r
6617   case '!':\r
6618     /* Builtin wave resource, or "!" alone for silence */\r
6619     if (ms->name[1]) {\r
6620       if (ms->data == NULL) return FALSE;\r
6621       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6622     } else {\r
6623       ok = TRUE;\r
6624     }\r
6625     break;\r
6626   default:\r
6627     /* .wav file.  Error if not found. */\r
6628     if (ms->data == NULL) return FALSE;\r
6629     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6630     break;\r
6631   }\r
6632   /* Don't print an error: this can happen innocently if the sound driver\r
6633      is busy; for instance, if another instance of WinBoard is playing\r
6634      a sound at about the same time. */\r
6635   return ok;\r
6636 }\r
6637 \r
6638 \r
6639 LRESULT CALLBACK\r
6640 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6641 {\r
6642   BOOL ok;\r
6643   OPENFILENAME *ofn;\r
6644   static UINT *number; /* gross that this is static */\r
6645 \r
6646   switch (message) {\r
6647   case WM_INITDIALOG: /* message: initialize dialog box */\r
6648     /* Center the dialog over the application window */\r
6649     ofn = (OPENFILENAME *) lParam;\r
6650     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6651       number = (UINT *) ofn->lCustData;\r
6652       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6653     } else {\r
6654       number = NULL;\r
6655     }\r
6656     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6657     return FALSE;  /* Allow for further processing */\r
6658 \r
6659   case WM_COMMAND:\r
6660     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6661       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6662     }\r
6663     return FALSE;  /* Allow for further processing */\r
6664   }\r
6665   return FALSE;\r
6666 }\r
6667 \r
6668 UINT APIENTRY\r
6669 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6670 {\r
6671   static UINT *number;\r
6672   OPENFILENAME *ofname;\r
6673   OFNOTIFY *ofnot;\r
6674   switch (uiMsg) {\r
6675   case WM_INITDIALOG:\r
6676     ofname = (OPENFILENAME *)lParam;\r
6677     number = (UINT *)(ofname->lCustData);\r
6678     break;\r
6679   case WM_NOTIFY:\r
6680     ofnot = (OFNOTIFY *)lParam;\r
6681     if (ofnot->hdr.code == CDN_FILEOK) {\r
6682       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6683     }\r
6684     break;\r
6685   }\r
6686   return 0;\r
6687 }\r
6688 \r
6689 \r
6690 FILE *\r
6691 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6692                char *nameFilt, char *dlgTitle, UINT *number,\r
6693                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6694 {\r
6695   OPENFILENAME openFileName;\r
6696   char buf1[MSG_SIZ];\r
6697   FILE *f;\r
6698 \r
6699   if (fileName == NULL) fileName = buf1;\r
6700   if (defName == NULL) {\r
6701     strcpy(fileName, "*.");\r
6702     strcat(fileName, defExt);\r
6703   } else {\r
6704     strcpy(fileName, defName);\r
6705   }\r
6706   if (fileTitle) strcpy(fileTitle, "");\r
6707   if (number) *number = 0;\r
6708 \r
6709   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6710   openFileName.hwndOwner         = hwnd;\r
6711   openFileName.hInstance         = (HANDLE) hInst;\r
6712   openFileName.lpstrFilter       = nameFilt;\r
6713   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6714   openFileName.nMaxCustFilter    = 0L;\r
6715   openFileName.nFilterIndex      = 1L;\r
6716   openFileName.lpstrFile         = fileName;\r
6717   openFileName.nMaxFile          = MSG_SIZ;\r
6718   openFileName.lpstrFileTitle    = fileTitle;\r
6719   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6720   openFileName.lpstrInitialDir   = NULL;\r
6721   openFileName.lpstrTitle        = dlgTitle;\r
6722   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6723     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6724     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6725     | (oldDialog ? 0 : OFN_EXPLORER);\r
6726   openFileName.nFileOffset       = 0;\r
6727   openFileName.nFileExtension    = 0;\r
6728   openFileName.lpstrDefExt       = defExt;\r
6729   openFileName.lCustData         = (LONG) number;\r
6730   openFileName.lpfnHook          = oldDialog ?\r
6731     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6732   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6733 \r
6734   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6735                         GetOpenFileName(&openFileName)) {\r
6736     /* open the file */\r
6737     f = fopen(openFileName.lpstrFile, write);\r
6738     if (f == NULL) {\r
6739       MessageBox(hwnd, "File open failed", NULL,\r
6740                  MB_OK|MB_ICONEXCLAMATION);\r
6741       return NULL;\r
6742     }\r
6743   } else {\r
6744     int err = CommDlgExtendedError();\r
6745     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6746     return FALSE;\r
6747   }\r
6748   return f;\r
6749 }\r
6750 \r
6751 \r
6752 \r
6753 VOID APIENTRY\r
6754 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6755 {\r
6756   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6757 \r
6758   /*\r
6759    * Get the first pop-up menu in the menu template. This is the\r
6760    * menu that TrackPopupMenu displays.\r
6761    */\r
6762   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6763 \r
6764   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6765 \r
6766   /*\r
6767    * TrackPopup uses screen coordinates, so convert the\r
6768    * coordinates of the mouse click to screen coordinates.\r
6769    */\r
6770   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6771 \r
6772   /* Draw and track the floating pop-up menu. */\r
6773   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6774                  pt.x, pt.y, 0, hwnd, NULL);\r
6775 \r
6776   /* Destroy the menu.*/\r
6777   DestroyMenu(hmenu);\r
6778 }\r
6779    \r
6780 typedef struct {\r
6781   HWND hDlg, hText;\r
6782   int sizeX, sizeY, newSizeX, newSizeY;\r
6783   HDWP hdwp;\r
6784 } ResizeEditPlusButtonsClosure;\r
6785 \r
6786 BOOL CALLBACK\r
6787 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6788 {\r
6789   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6790   RECT rect;\r
6791   POINT pt;\r
6792 \r
6793   if (hChild == cl->hText) return TRUE;\r
6794   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6795   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6796   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6797   ScreenToClient(cl->hDlg, &pt);\r
6798   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6799     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6800   return TRUE;\r
6801 }\r
6802 \r
6803 /* Resize a dialog that has a (rich) edit field filling most of\r
6804    the top, with a row of buttons below */\r
6805 VOID\r
6806 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6807 {\r
6808   RECT rectText;\r
6809   int newTextHeight, newTextWidth;\r
6810   ResizeEditPlusButtonsClosure cl;\r
6811   \r
6812   /*if (IsIconic(hDlg)) return;*/\r
6813   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6814   \r
6815   cl.hdwp = BeginDeferWindowPos(8);\r
6816 \r
6817   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6818   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6819   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6820   if (newTextHeight < 0) {\r
6821     newSizeY += -newTextHeight;\r
6822     newTextHeight = 0;\r
6823   }\r
6824   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6825     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6826 \r
6827   cl.hDlg = hDlg;\r
6828   cl.hText = hText;\r
6829   cl.sizeX = sizeX;\r
6830   cl.sizeY = sizeY;\r
6831   cl.newSizeX = newSizeX;\r
6832   cl.newSizeY = newSizeY;\r
6833   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6834 \r
6835   EndDeferWindowPos(cl.hdwp);\r
6836 }\r
6837 \r
6838 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6839 {\r
6840     RECT    rChild, rParent;\r
6841     int     wChild, hChild, wParent, hParent;\r
6842     int     wScreen, hScreen, xNew, yNew;\r
6843     HDC     hdc;\r
6844 \r
6845     /* Get the Height and Width of the child window */\r
6846     GetWindowRect (hwndChild, &rChild);\r
6847     wChild = rChild.right - rChild.left;\r
6848     hChild = rChild.bottom - rChild.top;\r
6849 \r
6850     /* Get the Height and Width of the parent window */\r
6851     GetWindowRect (hwndParent, &rParent);\r
6852     wParent = rParent.right - rParent.left;\r
6853     hParent = rParent.bottom - rParent.top;\r
6854 \r
6855     /* Get the display limits */\r
6856     hdc = GetDC (hwndChild);\r
6857     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6858     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6859     ReleaseDC(hwndChild, hdc);\r
6860 \r
6861     /* Calculate new X position, then adjust for screen */\r
6862     xNew = rParent.left + ((wParent - wChild) /2);\r
6863     if (xNew < 0) {\r
6864         xNew = 0;\r
6865     } else if ((xNew+wChild) > wScreen) {\r
6866         xNew = wScreen - wChild;\r
6867     }\r
6868 \r
6869     /* Calculate new Y position, then adjust for screen */\r
6870     if( mode == 0 ) {\r
6871         yNew = rParent.top  + ((hParent - hChild) /2);\r
6872     }\r
6873     else {\r
6874         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6875     }\r
6876 \r
6877     if (yNew < 0) {\r
6878         yNew = 0;\r
6879     } else if ((yNew+hChild) > hScreen) {\r
6880         yNew = hScreen - hChild;\r
6881     }\r
6882 \r
6883     /* Set it, and return */\r
6884     return SetWindowPos (hwndChild, NULL,\r
6885                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6886 }\r
6887 \r
6888 /* Center one window over another */\r
6889 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6890 {\r
6891     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6892 }\r
6893 \r
6894 /*---------------------------------------------------------------------------*\\r
6895  *\r
6896  * Startup Dialog functions\r
6897  *\r
6898 \*---------------------------------------------------------------------------*/\r
6899 void\r
6900 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6901 {\r
6902   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6903 \r
6904   while (*cd != NULL) {\r
6905     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6906     cd++;\r
6907   }\r
6908 }\r
6909 \r
6910 void\r
6911 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6912 {\r
6913   char buf1[ARG_MAX];\r
6914   int len;\r
6915 \r
6916   if (str[0] == '@') {\r
6917     FILE* f = fopen(str + 1, "r");\r
6918     if (f == NULL) {\r
6919       DisplayFatalError(str + 1, errno, 2);\r
6920       return;\r
6921     }\r
6922     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6923     fclose(f);\r
6924     buf1[len] = NULLCHAR;\r
6925     str = buf1;\r
6926   }\r
6927 \r
6928   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6929 \r
6930   for (;;) {\r
6931     char buf[MSG_SIZ];\r
6932     char *end = strchr(str, '\n');\r
6933     if (end == NULL) return;\r
6934     memcpy(buf, str, end - str);\r
6935     buf[end - str] = NULLCHAR;\r
6936     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6937     str = end + 1;\r
6938   }\r
6939 }\r
6940 \r
6941 void\r
6942 SetStartupDialogEnables(HWND hDlg)\r
6943 {\r
6944   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6945     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6946     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6947   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6948     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6949   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6950     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6951   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6952     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6953   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6954     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6955     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6956     IsDlgButtonChecked(hDlg, OPT_View));\r
6957 }\r
6958 \r
6959 char *\r
6960 QuoteForFilename(char *filename)\r
6961 {\r
6962   int dquote, space;\r
6963   dquote = strchr(filename, '"') != NULL;\r
6964   space = strchr(filename, ' ') != NULL;\r
6965   if (dquote || space) {\r
6966     if (dquote) {\r
6967       return "'";\r
6968     } else {\r
6969       return "\"";\r
6970     }\r
6971   } else {\r
6972     return "";\r
6973   }\r
6974 }\r
6975 \r
6976 VOID\r
6977 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6978 {\r
6979   char buf[MSG_SIZ];\r
6980   char *q;\r
6981 \r
6982   InitComboStringsFromOption(hwndCombo, nthnames);\r
6983   q = QuoteForFilename(nthcp);\r
6984   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6985   if (*nthdir != NULLCHAR) {\r
6986     q = QuoteForFilename(nthdir);\r
6987     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6988   }\r
6989   if (*nthcp == NULLCHAR) {\r
6990     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6991   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6992     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6993     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6994   }\r
6995 }\r
6996 \r
6997 LRESULT CALLBACK\r
6998 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6999 {\r
7000   char buf[MSG_SIZ];\r
7001   HANDLE hwndCombo;\r
7002   char *p;\r
7003 \r
7004   switch (message) {\r
7005   case WM_INITDIALOG:\r
7006     /* Center the dialog */\r
7007     CenterWindow (hDlg, GetDesktopWindow());\r
7008     /* Initialize the dialog items */\r
7009     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7010                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7011                   firstChessProgramNames);\r
7012     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7013                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7014                   secondChessProgramNames);\r
7015     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7016     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7017     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7018     if (*appData.icsHelper != NULLCHAR) {\r
7019       char *q = QuoteForFilename(appData.icsHelper);\r
7020       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7021     }\r
7022     if (*appData.icsHost == NULLCHAR) {\r
7023       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7024       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7025     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7026       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7027       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7028     }\r
7029 \r
7030     if (appData.icsActive) {\r
7031       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7032     }\r
7033     else if (appData.noChessProgram) {\r
7034       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7035     }\r
7036     else {\r
7037       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7038     }\r
7039 \r
7040     SetStartupDialogEnables(hDlg);\r
7041     return TRUE;\r
7042 \r
7043   case WM_COMMAND:\r
7044     switch (LOWORD(wParam)) {\r
7045     case IDOK:\r
7046       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7047         strcpy(buf, "/fcp=");\r
7048         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7049         p = buf;\r
7050         ParseArgs(StringGet, &p);\r
7051         strcpy(buf, "/scp=");\r
7052         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7053         p = buf;\r
7054         ParseArgs(StringGet, &p);\r
7055         appData.noChessProgram = FALSE;\r
7056         appData.icsActive = FALSE;\r
7057       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7058         strcpy(buf, "/ics /icshost=");\r
7059         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7060         p = buf;\r
7061         ParseArgs(StringGet, &p);\r
7062         if (appData.zippyPlay) {\r
7063           strcpy(buf, "/fcp=");\r
7064           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7065           p = buf;\r
7066           ParseArgs(StringGet, &p);\r
7067         }\r
7068       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7069         appData.noChessProgram = TRUE;\r
7070         appData.icsActive = FALSE;\r
7071       } else {\r
7072         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7073                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7074         return TRUE;\r
7075       }\r
7076       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7077         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7078         p = buf;\r
7079         ParseArgs(StringGet, &p);\r
7080       }\r
7081       EndDialog(hDlg, TRUE);\r
7082       return TRUE;\r
7083 \r
7084     case IDCANCEL:\r
7085       ExitEvent(0);\r
7086       return TRUE;\r
7087 \r
7088     case IDM_HELPCONTENTS:\r
7089       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7090         MessageBox (GetFocus(),\r
7091                     "Unable to activate help",\r
7092                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7093       }\r
7094       break;\r
7095 \r
7096     default:\r
7097       SetStartupDialogEnables(hDlg);\r
7098       break;\r
7099     }\r
7100     break;\r
7101   }\r
7102   return FALSE;\r
7103 }\r
7104 \r
7105 /*---------------------------------------------------------------------------*\\r
7106  *\r
7107  * About box dialog functions\r
7108  *\r
7109 \*---------------------------------------------------------------------------*/\r
7110 \r
7111 /* Process messages for "About" dialog box */\r
7112 LRESULT CALLBACK\r
7113 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7114 {\r
7115   switch (message) {\r
7116   case WM_INITDIALOG: /* message: initialize dialog box */\r
7117     /* Center the dialog over the application window */\r
7118     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7119     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7120     JAWS_COPYRIGHT\r
7121     return (TRUE);\r
7122 \r
7123   case WM_COMMAND: /* message: received a command */\r
7124     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7125         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7126       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7127       return (TRUE);\r
7128     }\r
7129     break;\r
7130   }\r
7131   return (FALSE);\r
7132 }\r
7133 \r
7134 /*---------------------------------------------------------------------------*\\r
7135  *\r
7136  * Comment Dialog functions\r
7137  *\r
7138 \*---------------------------------------------------------------------------*/\r
7139 \r
7140 LRESULT CALLBACK\r
7141 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7142 {\r
7143   static HANDLE hwndText = NULL;\r
7144   int len, newSizeX, newSizeY, flags;\r
7145   static int sizeX, sizeY;\r
7146   char *str;\r
7147   RECT rect;\r
7148   MINMAXINFO *mmi;\r
7149 \r
7150   switch (message) {\r
7151   case WM_INITDIALOG: /* message: initialize dialog box */\r
7152     /* Initialize the dialog items */\r
7153     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7154     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7155     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7156     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7157     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7158     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7159     SetWindowText(hDlg, commentTitle);\r
7160     if (editComment) {\r
7161       SetFocus(hwndText);\r
7162     } else {\r
7163       SetFocus(GetDlgItem(hDlg, IDOK));\r
7164     }\r
7165     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7166                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7167                 MAKELPARAM(FALSE, 0));\r
7168     /* Size and position the dialog */\r
7169     if (!commentDialog) {\r
7170       commentDialog = hDlg;\r
7171       flags = SWP_NOZORDER;\r
7172       GetClientRect(hDlg, &rect);\r
7173       sizeX = rect.right;\r
7174       sizeY = rect.bottom;\r
7175       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7176           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7177         WINDOWPLACEMENT wp;\r
7178         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7179         wp.length = sizeof(WINDOWPLACEMENT);\r
7180         wp.flags = 0;\r
7181         wp.showCmd = SW_SHOW;\r
7182         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7183         wp.rcNormalPosition.left = commentX;\r
7184         wp.rcNormalPosition.right = commentX + commentW;\r
7185         wp.rcNormalPosition.top = commentY;\r
7186         wp.rcNormalPosition.bottom = commentY + commentH;\r
7187         SetWindowPlacement(hDlg, &wp);\r
7188 \r
7189         GetClientRect(hDlg, &rect);\r
7190         newSizeX = rect.right;\r
7191         newSizeY = rect.bottom;\r
7192         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7193                               newSizeX, newSizeY);\r
7194         sizeX = newSizeX;\r
7195         sizeY = newSizeY;\r
7196       }\r
7197     }\r
7198     return FALSE;\r
7199 \r
7200   case WM_COMMAND: /* message: received a command */\r
7201     switch (LOWORD(wParam)) {\r
7202     case IDOK:\r
7203       if (editComment) {\r
7204         char *p, *q;\r
7205         /* Read changed options from the dialog box */\r
7206         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7207         len = GetWindowTextLength(hwndText);\r
7208         str = (char *) malloc(len + 1);\r
7209         GetWindowText(hwndText, str, len + 1);\r
7210         p = q = str;\r
7211         while (*q) {\r
7212           if (*q == '\r')\r
7213             q++;\r
7214           else\r
7215             *p++ = *q++;\r
7216         }\r
7217         *p = NULLCHAR;\r
7218         ReplaceComment(commentIndex, str);\r
7219         free(str);\r
7220       }\r
7221       CommentPopDown();\r
7222       return TRUE;\r
7223 \r
7224     case IDCANCEL:\r
7225     case OPT_CancelComment:\r
7226       CommentPopDown();\r
7227       return TRUE;\r
7228 \r
7229     case OPT_ClearComment:\r
7230       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7231       break;\r
7232 \r
7233     case OPT_EditComment:\r
7234       EditCommentEvent();\r
7235       return TRUE;\r
7236 \r
7237     default:\r
7238       break;\r
7239     }\r
7240     break;\r
7241 \r
7242   case WM_SIZE:\r
7243     newSizeX = LOWORD(lParam);\r
7244     newSizeY = HIWORD(lParam);\r
7245     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7246     sizeX = newSizeX;\r
7247     sizeY = newSizeY;\r
7248     break;\r
7249 \r
7250   case WM_GETMINMAXINFO:\r
7251     /* Prevent resizing window too small */\r
7252     mmi = (MINMAXINFO *) lParam;\r
7253     mmi->ptMinTrackSize.x = 100;\r
7254     mmi->ptMinTrackSize.y = 100;\r
7255     break;\r
7256   }\r
7257   return FALSE;\r
7258 }\r
7259 \r
7260 VOID\r
7261 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7262 {\r
7263   FARPROC lpProc;\r
7264   char *p, *q;\r
7265 \r
7266   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7267 \r
7268   if (str == NULL) str = "";\r
7269   p = (char *) malloc(2 * strlen(str) + 2);\r
7270   q = p;\r
7271   while (*str) {\r
7272     if (*str == '\n') *q++ = '\r';\r
7273     *q++ = *str++;\r
7274   }\r
7275   *q = NULLCHAR;\r
7276   if (commentText != NULL) free(commentText);\r
7277 \r
7278   commentIndex = index;\r
7279   commentTitle = title;\r
7280   commentText = p;\r
7281   editComment = edit;\r
7282 \r
7283   if (commentDialog) {\r
7284     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7285     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7286   } else {\r
7287     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7288     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7289                  hwndMain, (DLGPROC)lpProc);\r
7290     FreeProcInstance(lpProc);\r
7291   }\r
7292   commentDialogUp = TRUE;\r
7293 }\r
7294 \r
7295 \r
7296 /*---------------------------------------------------------------------------*\\r
7297  *\r
7298  * Type-in move dialog functions\r
7299  * \r
7300 \*---------------------------------------------------------------------------*/\r
7301 \r
7302 LRESULT CALLBACK\r
7303 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7304 {\r
7305   char move[MSG_SIZ];\r
7306   HWND hInput;\r
7307   ChessMove moveType;\r
7308   int fromX, fromY, toX, toY;\r
7309   char promoChar;\r
7310 \r
7311   switch (message) {\r
7312   case WM_INITDIALOG:\r
7313     move[0] = (char) lParam;\r
7314     move[1] = NULLCHAR;\r
7315     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7316     hInput = GetDlgItem(hDlg, OPT_Move);\r
7317     SetWindowText(hInput, move);\r
7318     SetFocus(hInput);\r
7319     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7320     return FALSE;\r
7321 \r
7322   case WM_COMMAND:\r
7323     switch (LOWORD(wParam)) {\r
7324     case IDOK:\r
7325       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7326       { int n; Board board;\r
7327         // [HGM] FENedit\r
7328         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7329                 EditPositionPasteFEN(move);\r
7330                 EndDialog(hDlg, TRUE);\r
7331                 return TRUE;\r
7332         }\r
7333         // [HGM] movenum: allow move number to be typed in any mode\r
7334         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7335           currentMove = 2*n-1;\r
7336           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7337           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7338           EndDialog(hDlg, TRUE);\r
7339           DrawPosition(TRUE, boards[currentMove]);\r
7340           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7341           else DisplayMessage("", "");\r
7342           return TRUE;\r
7343         }\r
7344       }\r
7345       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7346         gameMode != Training) {\r
7347         DisplayMoveError("Displayed move is not current");\r
7348       } else {\r
7349 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7350         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7351           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7352         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7353         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7354           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7355           if (gameMode != Training)\r
7356               forwardMostMove = currentMove;\r
7357           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7358         } else {\r
7359           DisplayMoveError("Could not parse move");\r
7360         }\r
7361       }\r
7362       EndDialog(hDlg, TRUE);\r
7363       return TRUE;\r
7364     case IDCANCEL:\r
7365       EndDialog(hDlg, FALSE);\r
7366       return TRUE;\r
7367     default:\r
7368       break;\r
7369     }\r
7370     break;\r
7371   }\r
7372   return FALSE;\r
7373 }\r
7374 \r
7375 VOID\r
7376 PopUpMoveDialog(char firstchar)\r
7377 {\r
7378     FARPROC lpProc;\r
7379     \r
7380     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7381         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7382         gameMode == AnalyzeMode || gameMode == EditGame || \r
7383         gameMode == EditPosition || gameMode == IcsExamining ||\r
7384         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7385         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7386                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7387                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7388         gameMode == Training) {\r
7389       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7390       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7391         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7392       FreeProcInstance(lpProc);\r
7393     }\r
7394 }\r
7395 \r
7396 /*---------------------------------------------------------------------------*\\r
7397  *\r
7398  * Type-in name dialog functions\r
7399  * \r
7400 \*---------------------------------------------------------------------------*/\r
7401 \r
7402 LRESULT CALLBACK\r
7403 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7404 {\r
7405   char move[MSG_SIZ];\r
7406   HWND hInput;\r
7407 \r
7408   switch (message) {\r
7409   case WM_INITDIALOG:\r
7410     move[0] = (char) lParam;\r
7411     move[1] = NULLCHAR;\r
7412     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7413     hInput = GetDlgItem(hDlg, OPT_Name);\r
7414     SetWindowText(hInput, move);\r
7415     SetFocus(hInput);\r
7416     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7417     return FALSE;\r
7418 \r
7419   case WM_COMMAND:\r
7420     switch (LOWORD(wParam)) {\r
7421     case IDOK:\r
7422       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7423       appData.userName = strdup(move);\r
7424       SetUserLogo();\r
7425 \r
7426       EndDialog(hDlg, TRUE);\r
7427       return TRUE;\r
7428     case IDCANCEL:\r
7429       EndDialog(hDlg, FALSE);\r
7430       return TRUE;\r
7431     default:\r
7432       break;\r
7433     }\r
7434     break;\r
7435   }\r
7436   return FALSE;\r
7437 }\r
7438 \r
7439 VOID\r
7440 PopUpNameDialog(char firstchar)\r
7441 {\r
7442     FARPROC lpProc;\r
7443     \r
7444       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7445       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7446         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7447       FreeProcInstance(lpProc);\r
7448 }\r
7449 \r
7450 /*---------------------------------------------------------------------------*\\r
7451  *\r
7452  *  Error dialogs\r
7453  * \r
7454 \*---------------------------------------------------------------------------*/\r
7455 \r
7456 /* Nonmodal error box */\r
7457 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7458                              WPARAM wParam, LPARAM lParam);\r
7459 \r
7460 VOID\r
7461 ErrorPopUp(char *title, char *content)\r
7462 {\r
7463   FARPROC lpProc;\r
7464   char *p, *q;\r
7465   BOOLEAN modal = hwndMain == NULL;\r
7466 \r
7467   p = content;\r
7468   q = errorMessage;\r
7469   while (*p) {\r
7470     if (*p == '\n') {\r
7471       if (modal) {\r
7472         *q++ = ' ';\r
7473         p++;\r
7474       } else {\r
7475         *q++ = '\r';\r
7476         *q++ = *p++;\r
7477       }\r
7478     } else {\r
7479       *q++ = *p++;\r
7480     }\r
7481   }\r
7482   *q = NULLCHAR;\r
7483   strncpy(errorTitle, title, sizeof(errorTitle));\r
7484   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7485   \r
7486   if (modal) {\r
7487     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7488   } else {\r
7489     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7490     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7491                  hwndMain, (DLGPROC)lpProc);\r
7492     FreeProcInstance(lpProc);\r
7493   }\r
7494 }\r
7495 \r
7496 VOID\r
7497 ErrorPopDown()\r
7498 {\r
7499   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7500   if (errorDialog == NULL) return;\r
7501   DestroyWindow(errorDialog);\r
7502   errorDialog = NULL;\r
7503 }\r
7504 \r
7505 LRESULT CALLBACK\r
7506 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7507 {\r
7508   HANDLE hwndText;\r
7509   RECT rChild;\r
7510 \r
7511   switch (message) {\r
7512   case WM_INITDIALOG:\r
7513     GetWindowRect(hDlg, &rChild);\r
7514 \r
7515     /*\r
7516     SetWindowPos(hDlg, NULL, rChild.left,\r
7517       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7518       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7519     */\r
7520 \r
7521     /* \r
7522         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7523         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7524         and it doesn't work when you resize the dialog.\r
7525         For now, just give it a default position.\r
7526     */\r
7527     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7528 \r
7529     errorDialog = hDlg;\r
7530     SetWindowText(hDlg, errorTitle);\r
7531     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7532     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7533     return FALSE;\r
7534 \r
7535   case WM_COMMAND:\r
7536     switch (LOWORD(wParam)) {\r
7537     case IDOK:\r
7538     case IDCANCEL:\r
7539       if (errorDialog == hDlg) errorDialog = NULL;\r
7540       DestroyWindow(hDlg);\r
7541       return TRUE;\r
7542 \r
7543     default:\r
7544       break;\r
7545     }\r
7546     break;\r
7547   }\r
7548   return FALSE;\r
7549 }\r
7550 \r
7551 #ifdef GOTHIC\r
7552 HWND gothicDialog = NULL;\r
7553 \r
7554 LRESULT CALLBACK\r
7555 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7556 {\r
7557   HANDLE hwndText;\r
7558   RECT rChild;\r
7559   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7560 \r
7561   switch (message) {\r
7562   case WM_INITDIALOG:\r
7563     GetWindowRect(hDlg, &rChild);\r
7564 \r
7565     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7566                                                              SWP_NOZORDER);\r
7567 \r
7568     /* \r
7569         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7570         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7571         and it doesn't work when you resize the dialog.\r
7572         For now, just give it a default position.\r
7573     */\r
7574     gothicDialog = hDlg;\r
7575     SetWindowText(hDlg, errorTitle);\r
7576     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7577     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7578     return FALSE;\r
7579 \r
7580   case WM_COMMAND:\r
7581     switch (LOWORD(wParam)) {\r
7582     case IDOK:\r
7583     case IDCANCEL:\r
7584       if (errorDialog == hDlg) errorDialog = NULL;\r
7585       DestroyWindow(hDlg);\r
7586       return TRUE;\r
7587 \r
7588     default:\r
7589       break;\r
7590     }\r
7591     break;\r
7592   }\r
7593   return FALSE;\r
7594 }\r
7595 \r
7596 VOID\r
7597 GothicPopUp(char *title, VariantClass variant)\r
7598 {\r
7599   FARPROC lpProc;\r
7600   static char *lastTitle;\r
7601 \r
7602   strncpy(errorTitle, title, sizeof(errorTitle));\r
7603   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7604 \r
7605   if(lastTitle != title && gothicDialog != NULL) {\r
7606     DestroyWindow(gothicDialog);\r
7607     gothicDialog = NULL;\r
7608   }\r
7609   if(variant != VariantNormal && gothicDialog == NULL) {\r
7610     title = lastTitle;\r
7611     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7612     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7613                  hwndMain, (DLGPROC)lpProc);\r
7614     FreeProcInstance(lpProc);\r
7615   }\r
7616 }\r
7617 #endif\r
7618 \r
7619 /*---------------------------------------------------------------------------*\\r
7620  *\r
7621  *  Ics Interaction console functions\r
7622  *\r
7623 \*---------------------------------------------------------------------------*/\r
7624 \r
7625 #define HISTORY_SIZE 64\r
7626 static char *history[HISTORY_SIZE];\r
7627 int histIn = 0, histP = 0;\r
7628 \r
7629 VOID\r
7630 SaveInHistory(char *cmd)\r
7631 {\r
7632   if (history[histIn] != NULL) {\r
7633     free(history[histIn]);\r
7634     history[histIn] = NULL;\r
7635   }\r
7636   if (*cmd == NULLCHAR) return;\r
7637   history[histIn] = StrSave(cmd);\r
7638   histIn = (histIn + 1) % HISTORY_SIZE;\r
7639   if (history[histIn] != NULL) {\r
7640     free(history[histIn]);\r
7641     history[histIn] = NULL;\r
7642   }\r
7643   histP = histIn;\r
7644 }\r
7645 \r
7646 char *\r
7647 PrevInHistory(char *cmd)\r
7648 {\r
7649   int newhp;\r
7650   if (histP == histIn) {\r
7651     if (history[histIn] != NULL) free(history[histIn]);\r
7652     history[histIn] = StrSave(cmd);\r
7653   }\r
7654   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7655   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7656   histP = newhp;\r
7657   return history[histP];\r
7658 }\r
7659 \r
7660 char *\r
7661 NextInHistory()\r
7662 {\r
7663   if (histP == histIn) return NULL;\r
7664   histP = (histP + 1) % HISTORY_SIZE;\r
7665   return history[histP];\r
7666 }\r
7667 \r
7668 typedef struct {\r
7669   char *item;\r
7670   char *command;\r
7671   BOOLEAN getname;\r
7672   BOOLEAN immediate;\r
7673 } IcsTextMenuEntry;\r
7674 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7675 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7676 \r
7677 void\r
7678 ParseIcsTextMenu(char *icsTextMenuString)\r
7679 {\r
7680 //  int flags = 0;\r
7681   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7682   char *p = icsTextMenuString;\r
7683   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7684     free(e->item);\r
7685     e->item = NULL;\r
7686     if (e->command != NULL) {\r
7687       free(e->command);\r
7688       e->command = NULL;\r
7689     }\r
7690     e++;\r
7691   }\r
7692   e = icsTextMenuEntry;\r
7693   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7694     if (*p == ';' || *p == '\n') {\r
7695       e->item = strdup("-");\r
7696       e->command = NULL;\r
7697       p++;\r
7698     } else if (*p == '-') {\r
7699       e->item = strdup("-");\r
7700       e->command = NULL;\r
7701       p++;\r
7702       if (*p) p++;\r
7703     } else {\r
7704       char *q, *r, *s, *t;\r
7705       char c;\r
7706       q = strchr(p, ',');\r
7707       if (q == NULL) break;\r
7708       *q = NULLCHAR;\r
7709       r = strchr(q + 1, ',');\r
7710       if (r == NULL) break;\r
7711       *r = NULLCHAR;\r
7712       s = strchr(r + 1, ',');\r
7713       if (s == NULL) break;\r
7714       *s = NULLCHAR;\r
7715       c = ';';\r
7716       t = strchr(s + 1, c);\r
7717       if (t == NULL) {\r
7718         c = '\n';\r
7719         t = strchr(s + 1, c);\r
7720       }\r
7721       if (t != NULL) *t = NULLCHAR;\r
7722       e->item = strdup(p);\r
7723       e->command = strdup(q + 1);\r
7724       e->getname = *(r + 1) != '0';\r
7725       e->immediate = *(s + 1) != '0';\r
7726       *q = ',';\r
7727       *r = ',';\r
7728       *s = ',';\r
7729       if (t == NULL) break;\r
7730       *t = c;\r
7731       p = t + 1;\r
7732     }\r
7733     e++;\r
7734   } \r
7735 }\r
7736 \r
7737 HMENU\r
7738 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7739 {\r
7740   HMENU hmenu, h;\r
7741   int i = 0;\r
7742   hmenu = LoadMenu(hInst, "TextMenu");\r
7743   h = GetSubMenu(hmenu, 0);\r
7744   while (e->item) {\r
7745     if (strcmp(e->item, "-") == 0) {\r
7746       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7747     } else {\r
7748       if (e->item[0] == '|') {\r
7749         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7750                    IDM_CommandX + i, &e->item[1]);\r
7751       } else {\r
7752         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7753       }\r
7754     }\r
7755     e++;\r
7756     i++;\r
7757   } \r
7758   return hmenu;\r
7759 }\r
7760 \r
7761 WNDPROC consoleTextWindowProc;\r
7762 \r
7763 void\r
7764 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7765 {\r
7766   char buf[MSG_SIZ], name[MSG_SIZ];\r
7767   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7768   CHARRANGE sel;\r
7769 \r
7770   if (!getname) {\r
7771     SetWindowText(hInput, command);\r
7772     if (immediate) {\r
7773       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7774     } else {\r
7775       sel.cpMin = 999999;\r
7776       sel.cpMax = 999999;\r
7777       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7778       SetFocus(hInput);\r
7779     }\r
7780     return;\r
7781   }    \r
7782   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7783   if (sel.cpMin == sel.cpMax) {\r
7784     /* Expand to surrounding word */\r
7785     TEXTRANGE tr;\r
7786     do {\r
7787       tr.chrg.cpMax = sel.cpMin;\r
7788       tr.chrg.cpMin = --sel.cpMin;\r
7789       if (sel.cpMin < 0) break;\r
7790       tr.lpstrText = name;\r
7791       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7792     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7793     sel.cpMin++;\r
7794 \r
7795     do {\r
7796       tr.chrg.cpMin = sel.cpMax;\r
7797       tr.chrg.cpMax = ++sel.cpMax;\r
7798       tr.lpstrText = name;\r
7799       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7800     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7801     sel.cpMax--;\r
7802 \r
7803     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7804       MessageBeep(MB_ICONEXCLAMATION);\r
7805       return;\r
7806     }\r
7807     tr.chrg = sel;\r
7808     tr.lpstrText = name;\r
7809     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7810   } else {\r
7811     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7812       MessageBeep(MB_ICONEXCLAMATION);\r
7813       return;\r
7814     }\r
7815     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7816   }\r
7817   if (immediate) {\r
7818     sprintf(buf, "%s %s", command, name);\r
7819     SetWindowText(hInput, buf);\r
7820     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7821   } else {\r
7822     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7823     SetWindowText(hInput, buf);\r
7824     sel.cpMin = 999999;\r
7825     sel.cpMax = 999999;\r
7826     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7827     SetFocus(hInput);\r
7828   }\r
7829 }\r
7830 \r
7831 LRESULT CALLBACK \r
7832 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7833 {\r
7834   HWND hInput;\r
7835   CHARRANGE sel;\r
7836 \r
7837   switch (message) {\r
7838   case WM_KEYDOWN:\r
7839     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7840     switch (wParam) {\r
7841     case VK_PRIOR:\r
7842       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7843       return 0;\r
7844     case VK_NEXT:\r
7845       sel.cpMin = 999999;\r
7846       sel.cpMax = 999999;\r
7847       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7848       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7849       return 0;\r
7850     }\r
7851     break;\r
7852   case WM_CHAR:\r
7853    if(wParam != '\022') {\r
7854     if (wParam == '\t') {\r
7855       if (GetKeyState(VK_SHIFT) < 0) {\r
7856         /* shifted */\r
7857         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7858         if (buttonDesc[0].hwnd) {\r
7859           SetFocus(buttonDesc[0].hwnd);\r
7860         } else {\r
7861           SetFocus(hwndMain);\r
7862         }\r
7863       } else {\r
7864         /* unshifted */\r
7865         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7866       }\r
7867     } else {\r
7868       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7869       JAWS_DELETE( SetFocus(hInput); )\r
7870       SendMessage(hInput, message, wParam, lParam);\r
7871     }\r
7872     return 0;\r
7873    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7874   case WM_RBUTTONUP:\r
7875     if (GetKeyState(VK_SHIFT) & ~1) {\r
7876       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7877         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7878     } else {\r
7879       POINT pt;\r
7880       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7881       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7882       if (sel.cpMin == sel.cpMax) {\r
7883         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7884         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7885       }\r
7886       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7887         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7888       }\r
7889       pt.x = LOWORD(lParam);\r
7890       pt.y = HIWORD(lParam);\r
7891       MenuPopup(hwnd, pt, hmenu, -1);\r
7892     }\r
7893     return 0;\r
7894   case WM_PASTE:\r
7895     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7896     SetFocus(hInput);\r
7897     return SendMessage(hInput, message, wParam, lParam);\r
7898   case WM_MBUTTONDOWN:\r
7899     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7900   case WM_RBUTTONDOWN:\r
7901     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7902       /* Move selection here if it was empty */\r
7903       POINT pt;\r
7904       pt.x = LOWORD(lParam);\r
7905       pt.y = HIWORD(lParam);\r
7906       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7907       if (sel.cpMin == sel.cpMax) {\r
7908         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7909         sel.cpMax = sel.cpMin;\r
7910         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7911       }\r
7912       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7913     }\r
7914     return 0;\r
7915   case WM_COMMAND:\r
7916     switch (LOWORD(wParam)) {\r
7917     case IDM_QuickPaste:\r
7918       {\r
7919         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7920         if (sel.cpMin == sel.cpMax) {\r
7921           MessageBeep(MB_ICONEXCLAMATION);\r
7922           return 0;\r
7923         }\r
7924         SendMessage(hwnd, WM_COPY, 0, 0);\r
7925         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7926         SendMessage(hInput, WM_PASTE, 0, 0);\r
7927         SetFocus(hInput);\r
7928         return 0;\r
7929       }\r
7930     case IDM_Cut:\r
7931       SendMessage(hwnd, WM_CUT, 0, 0);\r
7932       return 0;\r
7933     case IDM_Paste:\r
7934       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7935       return 0;\r
7936     case IDM_Copy:\r
7937       SendMessage(hwnd, WM_COPY, 0, 0);\r
7938       return 0;\r
7939     default:\r
7940       {\r
7941         int i = LOWORD(wParam) - IDM_CommandX;\r
7942         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7943             icsTextMenuEntry[i].command != NULL) {\r
7944           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7945                    icsTextMenuEntry[i].getname,\r
7946                    icsTextMenuEntry[i].immediate);\r
7947           return 0;\r
7948         }\r
7949       }\r
7950       break;\r
7951     }\r
7952     break;\r
7953   }\r
7954   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7955 }\r
7956 \r
7957 WNDPROC consoleInputWindowProc;\r
7958 \r
7959 LRESULT CALLBACK\r
7960 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7961 {\r
7962   char buf[MSG_SIZ];\r
7963   char *p;\r
7964   static BOOL sendNextChar = FALSE;\r
7965   static BOOL quoteNextChar = FALSE;\r
7966   InputSource *is = consoleInputSource;\r
7967   CHARFORMAT cf;\r
7968   CHARRANGE sel;\r
7969 \r
7970   switch (message) {\r
7971   case WM_CHAR:\r
7972     if (!appData.localLineEditing || sendNextChar) {\r
7973       is->buf[0] = (CHAR) wParam;\r
7974       is->count = 1;\r
7975       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7976       sendNextChar = FALSE;\r
7977       return 0;\r
7978     }\r
7979     if (quoteNextChar) {\r
7980       buf[0] = (char) wParam;\r
7981       buf[1] = NULLCHAR;\r
7982       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7983       quoteNextChar = FALSE;\r
7984       return 0;\r
7985     }\r
7986     switch (wParam) {\r
7987     case '\r':   /* Enter key */\r
7988       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7989       if (consoleEcho) SaveInHistory(is->buf);\r
7990       is->buf[is->count++] = '\n';\r
7991       is->buf[is->count] = NULLCHAR;\r
7992       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7993       if (consoleEcho) {\r
7994         ConsoleOutput(is->buf, is->count, TRUE);\r
7995       } else if (appData.localLineEditing) {\r
7996         ConsoleOutput("\n", 1, TRUE);\r
7997       }\r
7998       /* fall thru */\r
7999     case '\033': /* Escape key */\r
8000       SetWindowText(hwnd, "");\r
8001       cf.cbSize = sizeof(CHARFORMAT);\r
8002       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8003       if (consoleEcho) {\r
8004         cf.crTextColor = textAttribs[ColorNormal].color;\r
8005       } else {\r
8006         cf.crTextColor = COLOR_ECHOOFF;\r
8007       }\r
8008       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8009       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8010       return 0;\r
8011     case '\t':   /* Tab key */\r
8012       if (GetKeyState(VK_SHIFT) < 0) {\r
8013         /* shifted */\r
8014         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8015       } else {\r
8016         /* unshifted */\r
8017         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8018         if (buttonDesc[0].hwnd) {\r
8019           SetFocus(buttonDesc[0].hwnd);\r
8020         } else {\r
8021           SetFocus(hwndMain);\r
8022         }\r
8023       }\r
8024       return 0;\r
8025     case '\023': /* Ctrl+S */\r
8026       sendNextChar = TRUE;\r
8027       return 0;\r
8028     case '\021': /* Ctrl+Q */\r
8029       quoteNextChar = TRUE;\r
8030       return 0;\r
8031     JAWS_REPLAY\r
8032     default:\r
8033       break;\r
8034     }\r
8035     break;\r
8036   case WM_KEYDOWN:\r
8037     switch (wParam) {\r
8038     case VK_UP:\r
8039       GetWindowText(hwnd, buf, MSG_SIZ);\r
8040       p = PrevInHistory(buf);\r
8041       if (p != NULL) {\r
8042         SetWindowText(hwnd, p);\r
8043         sel.cpMin = 999999;\r
8044         sel.cpMax = 999999;\r
8045         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8046         return 0;\r
8047       }\r
8048       break;\r
8049     case VK_DOWN:\r
8050       p = NextInHistory();\r
8051       if (p != NULL) {\r
8052         SetWindowText(hwnd, p);\r
8053         sel.cpMin = 999999;\r
8054         sel.cpMax = 999999;\r
8055         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8056         return 0;\r
8057       }\r
8058       break;\r
8059     case VK_HOME:\r
8060     case VK_END:\r
8061       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8062       /* fall thru */\r
8063     case VK_PRIOR:\r
8064     case VK_NEXT:\r
8065       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8066       return 0;\r
8067     }\r
8068     break;\r
8069   case WM_MBUTTONDOWN:\r
8070     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8071       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8072     break;\r
8073   case WM_RBUTTONUP:\r
8074     if (GetKeyState(VK_SHIFT) & ~1) {\r
8075       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8076         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8077     } else {\r
8078       POINT pt;\r
8079       HMENU hmenu;\r
8080       hmenu = LoadMenu(hInst, "InputMenu");\r
8081       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8082       if (sel.cpMin == sel.cpMax) {\r
8083         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8084         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8085       }\r
8086       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8087         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8088       }\r
8089       pt.x = LOWORD(lParam);\r
8090       pt.y = HIWORD(lParam);\r
8091       MenuPopup(hwnd, pt, hmenu, -1);\r
8092     }\r
8093     return 0;\r
8094   case WM_COMMAND:\r
8095     switch (LOWORD(wParam)) { \r
8096     case IDM_Undo:\r
8097       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8098       return 0;\r
8099     case IDM_SelectAll:\r
8100       sel.cpMin = 0;\r
8101       sel.cpMax = -1; /*999999?*/\r
8102       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8103       return 0;\r
8104     case IDM_Cut:\r
8105       SendMessage(hwnd, WM_CUT, 0, 0);\r
8106       return 0;\r
8107     case IDM_Paste:\r
8108       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8109       return 0;\r
8110     case IDM_Copy:\r
8111       SendMessage(hwnd, WM_COPY, 0, 0);\r
8112       return 0;\r
8113     }\r
8114     break;\r
8115   }\r
8116   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8117 }\r
8118 \r
8119 #define CO_MAX  100000\r
8120 #define CO_TRIM   1000\r
8121 \r
8122 LRESULT CALLBACK\r
8123 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8124 {\r
8125   static SnapData sd;\r
8126   HWND hText, hInput;\r
8127   RECT rect;\r
8128   static int sizeX, sizeY;\r
8129   int newSizeX, newSizeY;\r
8130   MINMAXINFO *mmi;\r
8131   WORD wMask;\r
8132 \r
8133   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8134   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8135 \r
8136   switch (message) {\r
8137   case WM_NOTIFY:\r
8138     if (((NMHDR*)lParam)->code == EN_LINK)\r
8139     {\r
8140       ENLINK *pLink = (ENLINK*)lParam;\r
8141       if (pLink->msg == WM_LBUTTONUP)\r
8142       {\r
8143         TEXTRANGE tr;\r
8144 \r
8145         tr.chrg = pLink->chrg;\r
8146         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
8147         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
8148         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
8149         free(tr.lpstrText);\r
8150       }\r
8151     }\r
8152     break;\r
8153   case WM_INITDIALOG: /* message: initialize dialog box */\r
8154     hwndConsole = hDlg;\r
8155     SetFocus(hInput);\r
8156     consoleTextWindowProc = (WNDPROC)\r
8157       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8158     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8159     consoleInputWindowProc = (WNDPROC)\r
8160       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8161     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8162     Colorize(ColorNormal, TRUE);\r
8163     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8164     ChangedConsoleFont();\r
8165     GetClientRect(hDlg, &rect);\r
8166     sizeX = rect.right;\r
8167     sizeY = rect.bottom;\r
8168     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8169         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8170       WINDOWPLACEMENT wp;\r
8171       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8172       wp.length = sizeof(WINDOWPLACEMENT);\r
8173       wp.flags = 0;\r
8174       wp.showCmd = SW_SHOW;\r
8175       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8176       wp.rcNormalPosition.left = wpConsole.x;\r
8177       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8178       wp.rcNormalPosition.top = wpConsole.y;\r
8179       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8180       SetWindowPlacement(hDlg, &wp);\r
8181     }\r
8182 \r
8183    // [HGM] Chessknight's change 2004-07-13\r
8184    else { /* Determine Defaults */\r
8185        WINDOWPLACEMENT wp;\r
8186        wpConsole.x = winWidth + 1;\r
8187        wpConsole.y = boardY;\r
8188        wpConsole.width = screenWidth -  winWidth;\r
8189        wpConsole.height = winHeight;\r
8190        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8191        wp.length = sizeof(WINDOWPLACEMENT);\r
8192        wp.flags = 0;\r
8193        wp.showCmd = SW_SHOW;\r
8194        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8195        wp.rcNormalPosition.left = wpConsole.x;\r
8196        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8197        wp.rcNormalPosition.top = wpConsole.y;\r
8198        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8199        SetWindowPlacement(hDlg, &wp);\r
8200     }\r
8201 \r
8202    // Allow hText to highlight URLs and send notifications on them\r
8203    wMask = SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
8204    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
8205    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
8206 \r
8207     return FALSE;\r
8208 \r
8209   case WM_SETFOCUS:\r
8210     SetFocus(hInput);\r
8211     return 0;\r
8212 \r
8213   case WM_CLOSE:\r
8214     ExitEvent(0);\r
8215     /* not reached */\r
8216     break;\r
8217 \r
8218   case WM_SIZE:\r
8219     if (IsIconic(hDlg)) break;\r
8220     newSizeX = LOWORD(lParam);\r
8221     newSizeY = HIWORD(lParam);\r
8222     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8223       RECT rectText, rectInput;\r
8224       POINT pt;\r
8225       int newTextHeight, newTextWidth;\r
8226       GetWindowRect(hText, &rectText);\r
8227       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8228       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8229       if (newTextHeight < 0) {\r
8230         newSizeY += -newTextHeight;\r
8231         newTextHeight = 0;\r
8232       }\r
8233       SetWindowPos(hText, NULL, 0, 0,\r
8234         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8235       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8236       pt.x = rectInput.left;\r
8237       pt.y = rectInput.top + newSizeY - sizeY;\r
8238       ScreenToClient(hDlg, &pt);\r
8239       SetWindowPos(hInput, NULL, \r
8240         pt.x, pt.y, /* needs client coords */   \r
8241         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8242         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8243     }\r
8244     sizeX = newSizeX;\r
8245     sizeY = newSizeY;\r
8246     break;\r
8247 \r
8248   case WM_GETMINMAXINFO:\r
8249     /* Prevent resizing window too small */\r
8250     mmi = (MINMAXINFO *) lParam;\r
8251     mmi->ptMinTrackSize.x = 100;\r
8252     mmi->ptMinTrackSize.y = 100;\r
8253     break;\r
8254 \r
8255   /* [AS] Snapping */\r
8256   case WM_ENTERSIZEMOVE:\r
8257     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8258 \r
8259   case WM_SIZING:\r
8260     return OnSizing( &sd, hDlg, wParam, lParam );\r
8261 \r
8262   case WM_MOVING:\r
8263     return OnMoving( &sd, hDlg, wParam, lParam );\r
8264 \r
8265   case WM_EXITSIZEMOVE:\r
8266         UpdateICSWidth(hText);\r
8267     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8268   }\r
8269 \r
8270   return DefWindowProc(hDlg, message, wParam, lParam);\r
8271 }\r
8272 \r
8273 \r
8274 VOID\r
8275 ConsoleCreate()\r
8276 {\r
8277   HWND hCons;\r
8278   if (hwndConsole) return;\r
8279   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8280   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8281 }\r
8282 \r
8283 \r
8284 VOID\r
8285 ConsoleOutput(char* data, int length, int forceVisible)\r
8286 {\r
8287   HWND hText;\r
8288   int trim, exlen;\r
8289   char *p, *q;\r
8290   char buf[CO_MAX+1];\r
8291   POINT pEnd;\r
8292   RECT rect;\r
8293   static int delayLF = 0;\r
8294   CHARRANGE savesel, sel;\r
8295 \r
8296   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8297   p = data;\r
8298   q = buf;\r
8299   if (delayLF) {\r
8300     *q++ = '\r';\r
8301     *q++ = '\n';\r
8302     delayLF = 0;\r
8303   }\r
8304   while (length--) {\r
8305     if (*p == '\n') {\r
8306       if (*++p) {\r
8307         *q++ = '\r';\r
8308         *q++ = '\n';\r
8309       } else {\r
8310         delayLF = 1;\r
8311       }\r
8312     } else if (*p == '\007') {\r
8313        MyPlaySound(&sounds[(int)SoundBell]);\r
8314        p++;\r
8315     } else {\r
8316       *q++ = *p++;\r
8317     }\r
8318   }\r
8319   *q = NULLCHAR;\r
8320   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8321   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8322   /* Save current selection */\r
8323   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8324   exlen = GetWindowTextLength(hText);\r
8325   /* Find out whether current end of text is visible */\r
8326   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8327   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8328   /* Trim existing text if it's too long */\r
8329   if (exlen + (q - buf) > CO_MAX) {\r
8330     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8331     sel.cpMin = 0;\r
8332     sel.cpMax = trim;\r
8333     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8334     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8335     exlen -= trim;\r
8336     savesel.cpMin -= trim;\r
8337     savesel.cpMax -= trim;\r
8338     if (exlen < 0) exlen = 0;\r
8339     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8340     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8341   }\r
8342   /* Append the new text */\r
8343   sel.cpMin = exlen;\r
8344   sel.cpMax = exlen;\r
8345   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8346   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8347   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8348   if (forceVisible || exlen == 0 ||\r
8349       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8350        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8351     /* Scroll to make new end of text visible if old end of text\r
8352        was visible or new text is an echo of user typein */\r
8353     sel.cpMin = 9999999;\r
8354     sel.cpMax = 9999999;\r
8355     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8356     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8357     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8358     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8359   }\r
8360   if (savesel.cpMax == exlen || forceVisible) {\r
8361     /* Move insert point to new end of text if it was at the old\r
8362        end of text or if the new text is an echo of user typein */\r
8363     sel.cpMin = 9999999;\r
8364     sel.cpMax = 9999999;\r
8365     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8366   } else {\r
8367     /* Restore previous selection */\r
8368     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8369   }\r
8370   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8371 }\r
8372 \r
8373 /*---------*/\r
8374 \r
8375 \r
8376 void\r
8377 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8378 {\r
8379   char buf[100];\r
8380   char *str;\r
8381   COLORREF oldFg, oldBg;\r
8382   HFONT oldFont;\r
8383   RECT rect;\r
8384 \r
8385   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8386 \r
8387   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8388   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8389   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8390 \r
8391   rect.left = x;\r
8392   rect.right = x + squareSize;\r
8393   rect.top  = y;\r
8394   rect.bottom = y + squareSize;\r
8395   str = buf;\r
8396 \r
8397   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8398                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8399              y, ETO_CLIPPED|ETO_OPAQUE,\r
8400              &rect, str, strlen(str), NULL);\r
8401 \r
8402   (void) SetTextColor(hdc, oldFg);\r
8403   (void) SetBkColor(hdc, oldBg);\r
8404   (void) SelectObject(hdc, oldFont);\r
8405 }\r
8406 \r
8407 void\r
8408 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8409               RECT *rect, char *color, char *flagFell)\r
8410 {\r
8411   char buf[100];\r
8412   char *str;\r
8413   COLORREF oldFg, oldBg;\r
8414   HFONT oldFont;\r
8415 \r
8416   if (appData.clockMode) {\r
8417     if (tinyLayout)\r
8418       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8419     else\r
8420       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8421     str = buf;\r
8422   } else {\r
8423     str = color;\r
8424   }\r
8425 \r
8426   if (highlight) {\r
8427     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8428     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8429   } else {\r
8430     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8431     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8432   }\r
8433   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8434 \r
8435   JAWS_SILENCE\r
8436 \r
8437   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8438              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8439              rect, str, strlen(str), NULL);\r
8440   if(logoHeight > 0 && appData.clockMode) {\r
8441       RECT r;\r
8442       sprintf(buf, "%s %s", buf+7, flagFell);\r
8443       r.top = rect->top + logoHeight/2;\r
8444       r.left = rect->left;\r
8445       r.right = rect->right;\r
8446       r.bottom = rect->bottom;\r
8447       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8448                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8449                  &r, str, strlen(str), NULL);\r
8450   }\r
8451   (void) SetTextColor(hdc, oldFg);\r
8452   (void) SetBkColor(hdc, oldBg);\r
8453   (void) SelectObject(hdc, oldFont);\r
8454 }\r
8455 \r
8456 \r
8457 int\r
8458 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8459            OVERLAPPED *ovl)\r
8460 {\r
8461   int ok, err;\r
8462 \r
8463   /* [AS]  */\r
8464   if( count <= 0 ) {\r
8465     if (appData.debugMode) {\r
8466       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8467     }\r
8468 \r
8469     return ERROR_INVALID_USER_BUFFER;\r
8470   }\r
8471 \r
8472   ResetEvent(ovl->hEvent);\r
8473   ovl->Offset = ovl->OffsetHigh = 0;\r
8474   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8475   if (ok) {\r
8476     err = NO_ERROR;\r
8477   } else {\r
8478     err = GetLastError();\r
8479     if (err == ERROR_IO_PENDING) {\r
8480       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8481       if (ok)\r
8482         err = NO_ERROR;\r
8483       else\r
8484         err = GetLastError();\r
8485     }\r
8486   }\r
8487   return err;\r
8488 }\r
8489 \r
8490 int\r
8491 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8492             OVERLAPPED *ovl)\r
8493 {\r
8494   int ok, err;\r
8495 \r
8496   ResetEvent(ovl->hEvent);\r
8497   ovl->Offset = ovl->OffsetHigh = 0;\r
8498   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8499   if (ok) {\r
8500     err = NO_ERROR;\r
8501   } else {\r
8502     err = GetLastError();\r
8503     if (err == ERROR_IO_PENDING) {\r
8504       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8505       if (ok)\r
8506         err = NO_ERROR;\r
8507       else\r
8508         err = GetLastError();\r
8509     }\r
8510   }\r
8511   return err;\r
8512 }\r
8513 \r
8514 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8515 void CheckForInputBufferFull( InputSource * is )\r
8516 {\r
8517     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8518         /* Look for end of line */\r
8519         char * p = is->buf;\r
8520         \r
8521         while( p < is->next && *p != '\n' ) {\r
8522             p++;\r
8523         }\r
8524 \r
8525         if( p >= is->next ) {\r
8526             if (appData.debugMode) {\r
8527                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8528             }\r
8529 \r
8530             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8531             is->count = (DWORD) -1;\r
8532             is->next = is->buf;\r
8533         }\r
8534     }\r
8535 }\r
8536 \r
8537 DWORD\r
8538 InputThread(LPVOID arg)\r
8539 {\r
8540   InputSource *is;\r
8541   OVERLAPPED ovl;\r
8542 \r
8543   is = (InputSource *) arg;\r
8544   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8545   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8546   while (is->hThread != NULL) {\r
8547     is->error = DoReadFile(is->hFile, is->next,\r
8548                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8549                            &is->count, &ovl);\r
8550     if (is->error == NO_ERROR) {\r
8551       is->next += is->count;\r
8552     } else {\r
8553       if (is->error == ERROR_BROKEN_PIPE) {\r
8554         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8555         is->count = 0;\r
8556       } else {\r
8557         is->count = (DWORD) -1;\r
8558         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8559         break; \r
8560       }\r
8561     }\r
8562 \r
8563     CheckForInputBufferFull( is );\r
8564 \r
8565     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8566 \r
8567     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8568 \r
8569     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8570   }\r
8571 \r
8572   CloseHandle(ovl.hEvent);\r
8573   CloseHandle(is->hFile);\r
8574 \r
8575   if (appData.debugMode) {\r
8576     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8577   }\r
8578 \r
8579   return 0;\r
8580 }\r
8581 \r
8582 \r
8583 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8584 DWORD\r
8585 NonOvlInputThread(LPVOID arg)\r
8586 {\r
8587   InputSource *is;\r
8588   char *p, *q;\r
8589   int i;\r
8590   char prev;\r
8591 \r
8592   is = (InputSource *) arg;\r
8593   while (is->hThread != NULL) {\r
8594     is->error = ReadFile(is->hFile, is->next,\r
8595                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8596                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8597     if (is->error == NO_ERROR) {\r
8598       /* Change CRLF to LF */\r
8599       if (is->next > is->buf) {\r
8600         p = is->next - 1;\r
8601         i = is->count + 1;\r
8602       } else {\r
8603         p = is->next;\r
8604         i = is->count;\r
8605       }\r
8606       q = p;\r
8607       prev = NULLCHAR;\r
8608       while (i > 0) {\r
8609         if (prev == '\r' && *p == '\n') {\r
8610           *(q-1) = '\n';\r
8611           is->count--;\r
8612         } else { \r
8613           *q++ = *p;\r
8614         }\r
8615         prev = *p++;\r
8616         i--;\r
8617       }\r
8618       *q = NULLCHAR;\r
8619       is->next = q;\r
8620     } else {\r
8621       if (is->error == ERROR_BROKEN_PIPE) {\r
8622         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8623         is->count = 0; \r
8624       } else {\r
8625         is->count = (DWORD) -1;\r
8626       }\r
8627     }\r
8628 \r
8629     CheckForInputBufferFull( is );\r
8630 \r
8631     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8632 \r
8633     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8634 \r
8635     if (is->count < 0) break;  /* Quit on error */\r
8636   }\r
8637   CloseHandle(is->hFile);\r
8638   return 0;\r
8639 }\r
8640 \r
8641 DWORD\r
8642 SocketInputThread(LPVOID arg)\r
8643 {\r
8644   InputSource *is;\r
8645 \r
8646   is = (InputSource *) arg;\r
8647   while (is->hThread != NULL) {\r
8648     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8649     if ((int)is->count == SOCKET_ERROR) {\r
8650       is->count = (DWORD) -1;\r
8651       is->error = WSAGetLastError();\r
8652     } else {\r
8653       is->error = NO_ERROR;\r
8654       is->next += is->count;\r
8655       if (is->count == 0 && is->second == is) {\r
8656         /* End of file on stderr; quit with no message */\r
8657         break;\r
8658       }\r
8659     }\r
8660     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8661 \r
8662     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8663 \r
8664     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8665   }\r
8666   return 0;\r
8667 }\r
8668 \r
8669 VOID\r
8670 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8671 {\r
8672   InputSource *is;\r
8673 \r
8674   is = (InputSource *) lParam;\r
8675   if (is->lineByLine) {\r
8676     /* Feed in lines one by one */\r
8677     char *p = is->buf;\r
8678     char *q = p;\r
8679     while (q < is->next) {\r
8680       if (*q++ == '\n') {\r
8681         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8682         p = q;\r
8683       }\r
8684     }\r
8685     \r
8686     /* Move any partial line to the start of the buffer */\r
8687     q = is->buf;\r
8688     while (p < is->next) {\r
8689       *q++ = *p++;\r
8690     }\r
8691     is->next = q;\r
8692 \r
8693     if (is->error != NO_ERROR || is->count == 0) {\r
8694       /* Notify backend of the error.  Note: If there was a partial\r
8695          line at the end, it is not flushed through. */\r
8696       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8697     }\r
8698   } else {\r
8699     /* Feed in the whole chunk of input at once */\r
8700     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8701     is->next = is->buf;\r
8702   }\r
8703 }\r
8704 \r
8705 /*---------------------------------------------------------------------------*\\r
8706  *\r
8707  *  Menu enables. Used when setting various modes.\r
8708  *\r
8709 \*---------------------------------------------------------------------------*/\r
8710 \r
8711 typedef struct {\r
8712   int item;\r
8713   int flags;\r
8714 } Enables;\r
8715 \r
8716 VOID\r
8717 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8718 {\r
8719   while (enab->item > 0) {\r
8720     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8721     enab++;\r
8722   }\r
8723 }\r
8724 \r
8725 Enables gnuEnables[] = {\r
8726   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8727   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8728   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8729   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8730   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8731   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8732   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8733   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8734   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8735   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8736   { -1, -1 }\r
8737 };\r
8738 \r
8739 Enables icsEnables[] = {\r
8740   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8741   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8742   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8743   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8744   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8745   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8746   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8747   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8748   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8749   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8750   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8751   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8752   { -1, -1 }\r
8753 };\r
8754 \r
8755 #ifdef ZIPPY\r
8756 Enables zippyEnables[] = {\r
8757   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8758   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8759   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8760   { -1, -1 }\r
8761 };\r
8762 #endif\r
8763 \r
8764 Enables ncpEnables[] = {\r
8765   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8766   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8767   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8768   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8769   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8770   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8771   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8772   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8773   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8774   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8775   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8776   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8777   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8778   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8779   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8780   { -1, -1 }\r
8781 };\r
8782 \r
8783 Enables trainingOnEnables[] = {\r
8784   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8785   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8786   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8787   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8788   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8789   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8790   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8791   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8792   { -1, -1 }\r
8793 };\r
8794 \r
8795 Enables trainingOffEnables[] = {\r
8796   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8797   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8798   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8799   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8800   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8801   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8802   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8803   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8804   { -1, -1 }\r
8805 };\r
8806 \r
8807 /* These modify either ncpEnables or gnuEnables */\r
8808 Enables cmailEnables[] = {\r
8809   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8810   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8811   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8812   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8813   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8814   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8815   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8816   { -1, -1 }\r
8817 };\r
8818 \r
8819 Enables machineThinkingEnables[] = {\r
8820   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8821   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8822   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8823   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8824   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8825   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8826   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8827   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8828   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8829   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8830   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8831   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8832   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8833   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8834   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8835   { -1, -1 }\r
8836 };\r
8837 \r
8838 Enables userThinkingEnables[] = {\r
8839   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8840   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8841   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8842   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8843   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8844   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8845   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8846   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8847   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8848   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8849   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8850   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8851   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8852   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8853   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8854   { -1, -1 }\r
8855 };\r
8856 \r
8857 /*---------------------------------------------------------------------------*\\r
8858  *\r
8859  *  Front-end interface functions exported by XBoard.\r
8860  *  Functions appear in same order as prototypes in frontend.h.\r
8861  * \r
8862 \*---------------------------------------------------------------------------*/\r
8863 VOID\r
8864 ModeHighlight()\r
8865 {\r
8866   static UINT prevChecked = 0;\r
8867   static int prevPausing = 0;\r
8868   UINT nowChecked;\r
8869 \r
8870   if (pausing != prevPausing) {\r
8871     prevPausing = pausing;\r
8872     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8873                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8874     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8875   }\r
8876 \r
8877   switch (gameMode) {\r
8878   case BeginningOfGame:\r
8879     if (appData.icsActive)\r
8880       nowChecked = IDM_IcsClient;\r
8881     else if (appData.noChessProgram)\r
8882       nowChecked = IDM_EditGame;\r
8883     else\r
8884       nowChecked = IDM_MachineBlack;\r
8885     break;\r
8886   case MachinePlaysBlack:\r
8887     nowChecked = IDM_MachineBlack;\r
8888     break;\r
8889   case MachinePlaysWhite:\r
8890     nowChecked = IDM_MachineWhite;\r
8891     break;\r
8892   case TwoMachinesPlay:\r
8893     nowChecked = IDM_TwoMachines;\r
8894     break;\r
8895   case AnalyzeMode:\r
8896     nowChecked = IDM_AnalysisMode;\r
8897     break;\r
8898   case AnalyzeFile:\r
8899     nowChecked = IDM_AnalyzeFile;\r
8900     break;\r
8901   case EditGame:\r
8902     nowChecked = IDM_EditGame;\r
8903     break;\r
8904   case PlayFromGameFile:\r
8905     nowChecked = IDM_LoadGame;\r
8906     break;\r
8907   case EditPosition:\r
8908     nowChecked = IDM_EditPosition;\r
8909     break;\r
8910   case Training:\r
8911     nowChecked = IDM_Training;\r
8912     break;\r
8913   case IcsPlayingWhite:\r
8914   case IcsPlayingBlack:\r
8915   case IcsObserving:\r
8916   case IcsIdle:\r
8917     nowChecked = IDM_IcsClient;\r
8918     break;\r
8919   default:\r
8920   case EndOfGame:\r
8921     nowChecked = 0;\r
8922     break;\r
8923   }\r
8924   if (prevChecked != 0)\r
8925     (void) CheckMenuItem(GetMenu(hwndMain),\r
8926                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8927   if (nowChecked != 0)\r
8928     (void) CheckMenuItem(GetMenu(hwndMain),\r
8929                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8930 \r
8931   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8932     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8933                           MF_BYCOMMAND|MF_ENABLED);\r
8934   } else {\r
8935     (void) EnableMenuItem(GetMenu(hwndMain), \r
8936                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8937   }\r
8938 \r
8939   prevChecked = nowChecked;\r
8940 \r
8941   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8942   if (appData.icsActive) {\r
8943        if (appData.icsEngineAnalyze) {\r
8944                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8945                        MF_BYCOMMAND|MF_CHECKED);\r
8946        } else {\r
8947                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8948                        MF_BYCOMMAND|MF_UNCHECKED);\r
8949        }\r
8950   }\r
8951 }\r
8952 \r
8953 VOID\r
8954 SetICSMode()\r
8955 {\r
8956   HMENU hmenu = GetMenu(hwndMain);\r
8957   SetMenuEnables(hmenu, icsEnables);\r
8958   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8959     MF_BYPOSITION|MF_ENABLED);\r
8960 #ifdef ZIPPY\r
8961   if (appData.zippyPlay) {\r
8962     SetMenuEnables(hmenu, zippyEnables);\r
8963     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8964          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8965           MF_BYCOMMAND|MF_ENABLED);\r
8966   }\r
8967 #endif\r
8968 }\r
8969 \r
8970 VOID\r
8971 SetGNUMode()\r
8972 {\r
8973   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8974 }\r
8975 \r
8976 VOID\r
8977 SetNCPMode()\r
8978 {\r
8979   HMENU hmenu = GetMenu(hwndMain);\r
8980   SetMenuEnables(hmenu, ncpEnables);\r
8981   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8982     MF_BYPOSITION|MF_GRAYED);\r
8983     DrawMenuBar(hwndMain);\r
8984 }\r
8985 \r
8986 VOID\r
8987 SetCmailMode()\r
8988 {\r
8989   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8990 }\r
8991 \r
8992 VOID \r
8993 SetTrainingModeOn()\r
8994 {\r
8995   int i;\r
8996   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8997   for (i = 0; i < N_BUTTONS; i++) {\r
8998     if (buttonDesc[i].hwnd != NULL)\r
8999       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9000   }\r
9001   CommentPopDown();\r
9002 }\r
9003 \r
9004 VOID SetTrainingModeOff()\r
9005 {\r
9006   int i;\r
9007   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9008   for (i = 0; i < N_BUTTONS; i++) {\r
9009     if (buttonDesc[i].hwnd != NULL)\r
9010       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9011   }\r
9012 }\r
9013 \r
9014 \r
9015 VOID\r
9016 SetUserThinkingEnables()\r
9017 {\r
9018   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9019 }\r
9020 \r
9021 VOID\r
9022 SetMachineThinkingEnables()\r
9023 {\r
9024   HMENU hMenu = GetMenu(hwndMain);\r
9025   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9026 \r
9027   SetMenuEnables(hMenu, machineThinkingEnables);\r
9028 \r
9029   if (gameMode == MachinePlaysBlack) {\r
9030     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9031   } else if (gameMode == MachinePlaysWhite) {\r
9032     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9033   } else if (gameMode == TwoMachinesPlay) {\r
9034     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9035   }\r
9036 }\r
9037 \r
9038 \r
9039 VOID\r
9040 DisplayTitle(char *str)\r
9041 {\r
9042   char title[MSG_SIZ], *host;\r
9043   if (str[0] != NULLCHAR) {\r
9044     strcpy(title, str);\r
9045   } else if (appData.icsActive) {\r
9046     if (appData.icsCommPort[0] != NULLCHAR)\r
9047       host = "ICS";\r
9048     else \r
9049       host = appData.icsHost;\r
9050     sprintf(title, "%s: %s", szTitle, host);\r
9051   } else if (appData.noChessProgram) {\r
9052     strcpy(title, szTitle);\r
9053   } else {\r
9054     strcpy(title, szTitle);\r
9055     strcat(title, ": ");\r
9056     strcat(title, first.tidy);\r
9057   }\r
9058   SetWindowText(hwndMain, title);\r
9059 }\r
9060 \r
9061 \r
9062 VOID\r
9063 DisplayMessage(char *str1, char *str2)\r
9064 {\r
9065   HDC hdc;\r
9066   HFONT oldFont;\r
9067   int remain = MESSAGE_TEXT_MAX - 1;\r
9068   int len;\r
9069 \r
9070   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9071   messageText[0] = NULLCHAR;\r
9072   if (*str1) {\r
9073     len = strlen(str1);\r
9074     if (len > remain) len = remain;\r
9075     strncpy(messageText, str1, len);\r
9076     messageText[len] = NULLCHAR;\r
9077     remain -= len;\r
9078   }\r
9079   if (*str2 && remain >= 2) {\r
9080     if (*str1) {\r
9081       strcat(messageText, "  ");\r
9082       remain -= 2;\r
9083     }\r
9084     len = strlen(str2);\r
9085     if (len > remain) len = remain;\r
9086     strncat(messageText, str2, len);\r
9087   }\r
9088   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9089 \r
9090   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9091 \r
9092   SAYMACHINEMOVE();\r
9093 \r
9094   hdc = GetDC(hwndMain);\r
9095   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9096   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9097              &messageRect, messageText, strlen(messageText), NULL);\r
9098   (void) SelectObject(hdc, oldFont);\r
9099   (void) ReleaseDC(hwndMain, hdc);\r
9100 }\r
9101 \r
9102 VOID\r
9103 DisplayError(char *str, int error)\r
9104 {\r
9105   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9106   int len;\r
9107 \r
9108   if (error == 0) {\r
9109     strcpy(buf, str);\r
9110   } else {\r
9111     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9112                         NULL, error, LANG_NEUTRAL,\r
9113                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9114     if (len > 0) {\r
9115       sprintf(buf, "%s:\n%s", str, buf2);\r
9116     } else {\r
9117       ErrorMap *em = errmap;\r
9118       while (em->err != 0 && em->err != error) em++;\r
9119       if (em->err != 0) {\r
9120         sprintf(buf, "%s:\n%s", str, em->msg);\r
9121       } else {\r
9122         sprintf(buf, "%s:\nError code %d", str, error);\r
9123       }\r
9124     }\r
9125   }\r
9126   \r
9127   ErrorPopUp("Error", buf);\r
9128 }\r
9129 \r
9130 \r
9131 VOID\r
9132 DisplayMoveError(char *str)\r
9133 {\r
9134   fromX = fromY = -1;\r
9135   ClearHighlights();\r
9136   DrawPosition(FALSE, NULL);\r
9137   if (appData.popupMoveErrors) {\r
9138     ErrorPopUp("Error", str);\r
9139   } else {\r
9140     DisplayMessage(str, "");\r
9141     moveErrorMessageUp = TRUE;\r
9142   }\r
9143 }\r
9144 \r
9145 VOID\r
9146 DisplayFatalError(char *str, int error, int exitStatus)\r
9147 {\r
9148   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9149   int len;\r
9150   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9151 \r
9152   if (error != 0) {\r
9153     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9154                         NULL, error, LANG_NEUTRAL,\r
9155                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9156     if (len > 0) {\r
9157       sprintf(buf, "%s:\n%s", str, buf2);\r
9158     } else {\r
9159       ErrorMap *em = errmap;\r
9160       while (em->err != 0 && em->err != error) em++;\r
9161       if (em->err != 0) {\r
9162         sprintf(buf, "%s:\n%s", str, em->msg);\r
9163       } else {\r
9164         sprintf(buf, "%s:\nError code %d", str, error);\r
9165       }\r
9166     }\r
9167     str = buf;\r
9168   }\r
9169   if (appData.debugMode) {\r
9170     fprintf(debugFP, "%s: %s\n", label, str);\r
9171   }\r
9172   if (appData.popupExitMessage) {\r
9173     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9174                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9175   }\r
9176   ExitEvent(exitStatus);\r
9177 }\r
9178 \r
9179 \r
9180 VOID\r
9181 DisplayInformation(char *str)\r
9182 {\r
9183   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9184 }\r
9185 \r
9186 \r
9187 VOID\r
9188 DisplayNote(char *str)\r
9189 {\r
9190   ErrorPopUp("Note", str);\r
9191 }\r
9192 \r
9193 \r
9194 typedef struct {\r
9195   char *title, *question, *replyPrefix;\r
9196   ProcRef pr;\r
9197 } QuestionParams;\r
9198 \r
9199 LRESULT CALLBACK\r
9200 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9201 {\r
9202   static QuestionParams *qp;\r
9203   char reply[MSG_SIZ];\r
9204   int len, err;\r
9205 \r
9206   switch (message) {\r
9207   case WM_INITDIALOG:\r
9208     qp = (QuestionParams *) lParam;\r
9209     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9210     SetWindowText(hDlg, qp->title);\r
9211     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9212     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9213     return FALSE;\r
9214 \r
9215   case WM_COMMAND:\r
9216     switch (LOWORD(wParam)) {\r
9217     case IDOK:\r
9218       strcpy(reply, qp->replyPrefix);\r
9219       if (*reply) strcat(reply, " ");\r
9220       len = strlen(reply);\r
9221       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9222       strcat(reply, "\n");\r
9223       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9224       EndDialog(hDlg, TRUE);\r
9225       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9226       return TRUE;\r
9227     case IDCANCEL:\r
9228       EndDialog(hDlg, FALSE);\r
9229       return TRUE;\r
9230     default:\r
9231       break;\r
9232     }\r
9233     break;\r
9234   }\r
9235   return FALSE;\r
9236 }\r
9237 \r
9238 VOID\r
9239 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9240 {\r
9241     QuestionParams qp;\r
9242     FARPROC lpProc;\r
9243     \r
9244     qp.title = title;\r
9245     qp.question = question;\r
9246     qp.replyPrefix = replyPrefix;\r
9247     qp.pr = pr;\r
9248     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9249     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9250       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9251     FreeProcInstance(lpProc);\r
9252 }\r
9253 \r
9254 /* [AS] Pick FRC position */\r
9255 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9256 {\r
9257     static int * lpIndexFRC;\r
9258     BOOL index_is_ok;\r
9259     char buf[16];\r
9260 \r
9261     switch( message )\r
9262     {\r
9263     case WM_INITDIALOG:\r
9264         lpIndexFRC = (int *) lParam;\r
9265 \r
9266         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9267 \r
9268         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9269         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9270         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9271         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9272 \r
9273         break;\r
9274 \r
9275     case WM_COMMAND:\r
9276         switch( LOWORD(wParam) ) {\r
9277         case IDOK:\r
9278             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9279             EndDialog( hDlg, 0 );\r
9280             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9281             return TRUE;\r
9282         case IDCANCEL:\r
9283             EndDialog( hDlg, 1 );   \r
9284             return TRUE;\r
9285         case IDC_NFG_Edit:\r
9286             if( HIWORD(wParam) == EN_CHANGE ) {\r
9287                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9288 \r
9289                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9290             }\r
9291             return TRUE;\r
9292         case IDC_NFG_Random:\r
9293             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9294             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9295             return TRUE;\r
9296         }\r
9297 \r
9298         break;\r
9299     }\r
9300 \r
9301     return FALSE;\r
9302 }\r
9303 \r
9304 int NewGameFRC()\r
9305 {\r
9306     int result;\r
9307     int index = appData.defaultFrcPosition;\r
9308     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9309 \r
9310     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9311 \r
9312     if( result == 0 ) {\r
9313         appData.defaultFrcPosition = index;\r
9314     }\r
9315 \r
9316     return result;\r
9317 }\r
9318 \r
9319 /* [AS] Game list options */\r
9320 typedef struct {\r
9321     char id;\r
9322     char * name;\r
9323 } GLT_Item;\r
9324 \r
9325 static GLT_Item GLT_ItemInfo[] = {\r
9326     { GLT_EVENT,      "Event" },\r
9327     { GLT_SITE,       "Site" },\r
9328     { GLT_DATE,       "Date" },\r
9329     { GLT_ROUND,      "Round" },\r
9330     { GLT_PLAYERS,    "Players" },\r
9331     { GLT_RESULT,     "Result" },\r
9332     { GLT_WHITE_ELO,  "White Rating" },\r
9333     { GLT_BLACK_ELO,  "Black Rating" },\r
9334     { GLT_TIME_CONTROL,"Time Control" },\r
9335     { GLT_VARIANT,    "Variant" },\r
9336     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9337     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9338     { 0, 0 }\r
9339 };\r
9340 \r
9341 const char * GLT_FindItem( char id )\r
9342 {\r
9343     const char * result = 0;\r
9344 \r
9345     GLT_Item * list = GLT_ItemInfo;\r
9346 \r
9347     while( list->id != 0 ) {\r
9348         if( list->id == id ) {\r
9349             result = list->name;\r
9350             break;\r
9351         }\r
9352 \r
9353         list++;\r
9354     }\r
9355 \r
9356     return result;\r
9357 }\r
9358 \r
9359 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9360 {\r
9361     const char * name = GLT_FindItem( id );\r
9362 \r
9363     if( name != 0 ) {\r
9364         if( index >= 0 ) {\r
9365             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9366         }\r
9367         else {\r
9368             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9369         }\r
9370     }\r
9371 }\r
9372 \r
9373 void GLT_TagsToList( HWND hDlg, char * tags )\r
9374 {\r
9375     char * pc = tags;\r
9376 \r
9377     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9378 \r
9379     while( *pc ) {\r
9380         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9381         pc++;\r
9382     }\r
9383 \r
9384     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9385 \r
9386     pc = GLT_ALL_TAGS;\r
9387 \r
9388     while( *pc ) {\r
9389         if( strchr( tags, *pc ) == 0 ) {\r
9390             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9391         }\r
9392         pc++;\r
9393     }\r
9394 \r
9395     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9396 }\r
9397 \r
9398 char GLT_ListItemToTag( HWND hDlg, int index )\r
9399 {\r
9400     char result = '\0';\r
9401     char name[128];\r
9402 \r
9403     GLT_Item * list = GLT_ItemInfo;\r
9404 \r
9405     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9406         while( list->id != 0 ) {\r
9407             if( strcmp( list->name, name ) == 0 ) {\r
9408                 result = list->id;\r
9409                 break;\r
9410             }\r
9411 \r
9412             list++;\r
9413         }\r
9414     }\r
9415 \r
9416     return result;\r
9417 }\r
9418 \r
9419 void GLT_MoveSelection( HWND hDlg, int delta )\r
9420 {\r
9421     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9422     int idx2 = idx1 + delta;\r
9423     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9424 \r
9425     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9426         char buf[128];\r
9427 \r
9428         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9429         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9430         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9431         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9432     }\r
9433 }\r
9434 \r
9435 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9436 {\r
9437     static char glt[64];\r
9438     static char * lpUserGLT;\r
9439 \r
9440     switch( message )\r
9441     {\r
9442     case WM_INITDIALOG:\r
9443         lpUserGLT = (char *) lParam;\r
9444         \r
9445         strcpy( glt, lpUserGLT );\r
9446 \r
9447         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9448 \r
9449         /* Initialize list */\r
9450         GLT_TagsToList( hDlg, glt );\r
9451 \r
9452         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9453 \r
9454         break;\r
9455 \r
9456     case WM_COMMAND:\r
9457         switch( LOWORD(wParam) ) {\r
9458         case IDOK:\r
9459             {\r
9460                 char * pc = lpUserGLT;\r
9461                 int idx = 0;\r
9462 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9463                 char id;\r
9464 \r
9465                 do {\r
9466                     id = GLT_ListItemToTag( hDlg, idx );\r
9467 \r
9468                     *pc++ = id;\r
9469                     idx++;\r
9470                 } while( id != '\0' );\r
9471             }\r
9472             EndDialog( hDlg, 0 );\r
9473             return TRUE;\r
9474         case IDCANCEL:\r
9475             EndDialog( hDlg, 1 );\r
9476             return TRUE;\r
9477 \r
9478         case IDC_GLT_Default:\r
9479             strcpy( glt, GLT_DEFAULT_TAGS );\r
9480             GLT_TagsToList( hDlg, glt );\r
9481             return TRUE;\r
9482 \r
9483         case IDC_GLT_Restore:\r
9484             strcpy( glt, lpUserGLT );\r
9485             GLT_TagsToList( hDlg, glt );\r
9486             return TRUE;\r
9487 \r
9488         case IDC_GLT_Up:\r
9489             GLT_MoveSelection( hDlg, -1 );\r
9490             return TRUE;\r
9491 \r
9492         case IDC_GLT_Down:\r
9493             GLT_MoveSelection( hDlg, +1 );\r
9494             return TRUE;\r
9495         }\r
9496 \r
9497         break;\r
9498     }\r
9499 \r
9500     return FALSE;\r
9501 }\r
9502 \r
9503 int GameListOptions()\r
9504 {\r
9505     char glt[64];\r
9506     int result;\r
9507     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9508 \r
9509     strcpy( glt, appData.gameListTags );\r
9510 \r
9511     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9512 \r
9513     if( result == 0 ) {\r
9514         /* [AS] Memory leak here! */\r
9515         appData.gameListTags = strdup( glt ); \r
9516     }\r
9517 \r
9518     return result;\r
9519 }\r
9520 \r
9521 \r
9522 VOID\r
9523 DisplayIcsInteractionTitle(char *str)\r
9524 {\r
9525   char consoleTitle[MSG_SIZ];\r
9526 \r
9527   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9528   SetWindowText(hwndConsole, consoleTitle);\r
9529 }\r
9530 \r
9531 void\r
9532 DrawPosition(int fullRedraw, Board board)\r
9533 {\r
9534   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9535 }\r
9536 \r
9537 \r
9538 VOID\r
9539 ResetFrontEnd()\r
9540 {\r
9541   fromX = fromY = -1;\r
9542   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9543     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9544     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9545     dragInfo.lastpos = dragInfo.pos;\r
9546     dragInfo.start.x = dragInfo.start.y = -1;\r
9547     dragInfo.from = dragInfo.start;\r
9548     ReleaseCapture();\r
9549     DrawPosition(TRUE, NULL);\r
9550   }\r
9551 }\r
9552 \r
9553 \r
9554 VOID\r
9555 CommentPopUp(char *title, char *str)\r
9556 {\r
9557   HWND hwnd = GetActiveWindow();\r
9558   EitherCommentPopUp(0, title, str, FALSE);\r
9559   SAY(str);\r
9560   SetActiveWindow(hwnd);\r
9561 }\r
9562 \r
9563 VOID\r
9564 CommentPopDown(void)\r
9565 {\r
9566   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9567   if (commentDialog) {\r
9568     ShowWindow(commentDialog, SW_HIDE);\r
9569   }\r
9570   commentDialogUp = FALSE;\r
9571 }\r
9572 \r
9573 VOID\r
9574 EditCommentPopUp(int index, char *title, char *str)\r
9575 {\r
9576   EitherCommentPopUp(index, title, str, TRUE);\r
9577 }\r
9578 \r
9579 \r
9580 VOID\r
9581 RingBell()\r
9582 {\r
9583   MyPlaySound(&sounds[(int)SoundMove]);\r
9584 }\r
9585 \r
9586 VOID PlayIcsWinSound()\r
9587 {\r
9588   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9589 }\r
9590 \r
9591 VOID PlayIcsLossSound()\r
9592 {\r
9593   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9594 }\r
9595 \r
9596 VOID PlayIcsDrawSound()\r
9597 {\r
9598   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9599 }\r
9600 \r
9601 VOID PlayIcsUnfinishedSound()\r
9602 {\r
9603   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9604 }\r
9605 \r
9606 VOID\r
9607 PlayAlarmSound()\r
9608 {\r
9609   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9610 }\r
9611 \r
9612 \r
9613 VOID\r
9614 EchoOn()\r
9615 {\r
9616   HWND hInput;\r
9617   consoleEcho = TRUE;\r
9618   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9619   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9620   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9621 }\r
9622 \r
9623 \r
9624 VOID\r
9625 EchoOff()\r
9626 {\r
9627   CHARFORMAT cf;\r
9628   HWND hInput;\r
9629   consoleEcho = FALSE;\r
9630   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9631   /* This works OK: set text and background both to the same color */\r
9632   cf = consoleCF;\r
9633   cf.crTextColor = COLOR_ECHOOFF;\r
9634   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9635   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9636 }\r
9637 \r
9638 /* No Raw()...? */\r
9639 \r
9640 void Colorize(ColorClass cc, int continuation)\r
9641 {\r
9642   currentColorClass = cc;\r
9643   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9644   consoleCF.crTextColor = textAttribs[cc].color;\r
9645   consoleCF.dwEffects = textAttribs[cc].effects;\r
9646   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9647 }\r
9648 \r
9649 char *\r
9650 UserName()\r
9651 {\r
9652   static char buf[MSG_SIZ];\r
9653   DWORD bufsiz = MSG_SIZ;\r
9654 \r
9655   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9656         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9657   }\r
9658   if (!GetUserName(buf, &bufsiz)) {\r
9659     /*DisplayError("Error getting user name", GetLastError());*/\r
9660     strcpy(buf, "User");\r
9661   }\r
9662   return buf;\r
9663 }\r
9664 \r
9665 char *\r
9666 HostName()\r
9667 {\r
9668   static char buf[MSG_SIZ];\r
9669   DWORD bufsiz = MSG_SIZ;\r
9670 \r
9671   if (!GetComputerName(buf, &bufsiz)) {\r
9672     /*DisplayError("Error getting host name", GetLastError());*/\r
9673     strcpy(buf, "Unknown");\r
9674   }\r
9675   return buf;\r
9676 }\r
9677 \r
9678 \r
9679 int\r
9680 ClockTimerRunning()\r
9681 {\r
9682   return clockTimerEvent != 0;\r
9683 }\r
9684 \r
9685 int\r
9686 StopClockTimer()\r
9687 {\r
9688   if (clockTimerEvent == 0) return FALSE;\r
9689   KillTimer(hwndMain, clockTimerEvent);\r
9690   clockTimerEvent = 0;\r
9691   return TRUE;\r
9692 }\r
9693 \r
9694 void\r
9695 StartClockTimer(long millisec)\r
9696 {\r
9697   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9698                              (UINT) millisec, NULL);\r
9699 }\r
9700 \r
9701 void\r
9702 DisplayWhiteClock(long timeRemaining, int highlight)\r
9703 {\r
9704   HDC hdc;\r
9705   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9706 \r
9707   if(appData.noGUI) return;\r
9708   hdc = GetDC(hwndMain);\r
9709   if (!IsIconic(hwndMain)) {\r
9710     DisplayAClock(hdc, timeRemaining, highlight, \r
9711                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9712   }\r
9713   if (highlight && iconCurrent == iconBlack) {\r
9714     iconCurrent = iconWhite;\r
9715     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9716     if (IsIconic(hwndMain)) {\r
9717       DrawIcon(hdc, 2, 2, iconCurrent);\r
9718     }\r
9719   }\r
9720   (void) ReleaseDC(hwndMain, hdc);\r
9721   if (hwndConsole)\r
9722     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9723 }\r
9724 \r
9725 void\r
9726 DisplayBlackClock(long timeRemaining, int highlight)\r
9727 {\r
9728   HDC hdc;\r
9729   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9730 \r
9731   if(appData.noGUI) return;\r
9732   hdc = GetDC(hwndMain);\r
9733   if (!IsIconic(hwndMain)) {\r
9734     DisplayAClock(hdc, timeRemaining, highlight, \r
9735                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9736   }\r
9737   if (highlight && iconCurrent == iconWhite) {\r
9738     iconCurrent = iconBlack;\r
9739     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9740     if (IsIconic(hwndMain)) {\r
9741       DrawIcon(hdc, 2, 2, iconCurrent);\r
9742     }\r
9743   }\r
9744   (void) ReleaseDC(hwndMain, hdc);\r
9745   if (hwndConsole)\r
9746     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9747 }\r
9748 \r
9749 \r
9750 int\r
9751 LoadGameTimerRunning()\r
9752 {\r
9753   return loadGameTimerEvent != 0;\r
9754 }\r
9755 \r
9756 int\r
9757 StopLoadGameTimer()\r
9758 {\r
9759   if (loadGameTimerEvent == 0) return FALSE;\r
9760   KillTimer(hwndMain, loadGameTimerEvent);\r
9761   loadGameTimerEvent = 0;\r
9762   return TRUE;\r
9763 }\r
9764 \r
9765 void\r
9766 StartLoadGameTimer(long millisec)\r
9767 {\r
9768   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9769                                 (UINT) millisec, NULL);\r
9770 }\r
9771 \r
9772 void\r
9773 AutoSaveGame()\r
9774 {\r
9775   char *defName;\r
9776   FILE *f;\r
9777   char fileTitle[MSG_SIZ];\r
9778 \r
9779   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9780   f = OpenFileDialog(hwndMain, "a", defName,\r
9781                      appData.oldSaveStyle ? "gam" : "pgn",\r
9782                      GAME_FILT, \r
9783                      "Save Game to File", NULL, fileTitle, NULL);\r
9784   if (f != NULL) {\r
9785     SaveGame(f, 0, "");\r
9786     fclose(f);\r
9787   }\r
9788 }\r
9789 \r
9790 \r
9791 void\r
9792 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9793 {\r
9794   if (delayedTimerEvent != 0) {\r
9795     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9796       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9797     }\r
9798     KillTimer(hwndMain, delayedTimerEvent);\r
9799     delayedTimerEvent = 0;\r
9800     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9801     delayedTimerCallback();\r
9802   }\r
9803   delayedTimerCallback = cb;\r
9804   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9805                                 (UINT) millisec, NULL);\r
9806 }\r
9807 \r
9808 DelayedEventCallback\r
9809 GetDelayedEvent()\r
9810 {\r
9811   if (delayedTimerEvent) {\r
9812     return delayedTimerCallback;\r
9813   } else {\r
9814     return NULL;\r
9815   }\r
9816 }\r
9817 \r
9818 void\r
9819 CancelDelayedEvent()\r
9820 {\r
9821   if (delayedTimerEvent) {\r
9822     KillTimer(hwndMain, delayedTimerEvent);\r
9823     delayedTimerEvent = 0;\r
9824   }\r
9825 }\r
9826 \r
9827 DWORD GetWin32Priority(int nice)\r
9828 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9829 /*\r
9830 REALTIME_PRIORITY_CLASS     0x00000100\r
9831 HIGH_PRIORITY_CLASS         0x00000080\r
9832 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9833 NORMAL_PRIORITY_CLASS       0x00000020\r
9834 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9835 IDLE_PRIORITY_CLASS         0x00000040\r
9836 */\r
9837         if (nice < -15) return 0x00000080;\r
9838         if (nice < 0)   return 0x00008000;\r
9839         if (nice == 0)  return 0x00000020;\r
9840         if (nice < 15)  return 0x00004000;\r
9841         return 0x00000040;\r
9842 }\r
9843 \r
9844 /* Start a child process running the given program.\r
9845    The process's standard output can be read from "from", and its\r
9846    standard input can be written to "to".\r
9847    Exit with fatal error if anything goes wrong.\r
9848    Returns an opaque pointer that can be used to destroy the process\r
9849    later.\r
9850 */\r
9851 int\r
9852 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9853 {\r
9854 #define BUFSIZE 4096\r
9855 \r
9856   HANDLE hChildStdinRd, hChildStdinWr,\r
9857     hChildStdoutRd, hChildStdoutWr;\r
9858   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9859   SECURITY_ATTRIBUTES saAttr;\r
9860   BOOL fSuccess;\r
9861   PROCESS_INFORMATION piProcInfo;\r
9862   STARTUPINFO siStartInfo;\r
9863   ChildProc *cp;\r
9864   char buf[MSG_SIZ];\r
9865   DWORD err;\r
9866 \r
9867   if (appData.debugMode) {\r
9868     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9869   }\r
9870 \r
9871   *pr = NoProc;\r
9872 \r
9873   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9874   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9875   saAttr.bInheritHandle = TRUE;\r
9876   saAttr.lpSecurityDescriptor = NULL;\r
9877 \r
9878   /*\r
9879    * The steps for redirecting child's STDOUT:\r
9880    *     1. Create anonymous pipe to be STDOUT for child.\r
9881    *     2. Create a noninheritable duplicate of read handle,\r
9882    *         and close the inheritable read handle.\r
9883    */\r
9884 \r
9885   /* Create a pipe for the child's STDOUT. */\r
9886   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9887     return GetLastError();\r
9888   }\r
9889 \r
9890   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9891   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9892                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9893                              FALSE,     /* not inherited */\r
9894                              DUPLICATE_SAME_ACCESS);\r
9895   if (! fSuccess) {\r
9896     return GetLastError();\r
9897   }\r
9898   CloseHandle(hChildStdoutRd);\r
9899 \r
9900   /*\r
9901    * The steps for redirecting child's STDIN:\r
9902    *     1. Create anonymous pipe to be STDIN for child.\r
9903    *     2. Create a noninheritable duplicate of write handle,\r
9904    *         and close the inheritable write handle.\r
9905    */\r
9906 \r
9907   /* Create a pipe for the child's STDIN. */\r
9908   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9909     return GetLastError();\r
9910   }\r
9911 \r
9912   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9913   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9914                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9915                              FALSE,     /* not inherited */\r
9916                              DUPLICATE_SAME_ACCESS);\r
9917   if (! fSuccess) {\r
9918     return GetLastError();\r
9919   }\r
9920   CloseHandle(hChildStdinWr);\r
9921 \r
9922   /* Arrange to (1) look in dir for the child .exe file, and\r
9923    * (2) have dir be the child's working directory.  Interpret\r
9924    * dir relative to the directory WinBoard loaded from. */\r
9925   GetCurrentDirectory(MSG_SIZ, buf);\r
9926   SetCurrentDirectory(installDir);\r
9927   SetCurrentDirectory(dir);\r
9928 \r
9929   /* Now create the child process. */\r
9930 \r
9931   siStartInfo.cb = sizeof(STARTUPINFO);\r
9932   siStartInfo.lpReserved = NULL;\r
9933   siStartInfo.lpDesktop = NULL;\r
9934   siStartInfo.lpTitle = NULL;\r
9935   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9936   siStartInfo.cbReserved2 = 0;\r
9937   siStartInfo.lpReserved2 = NULL;\r
9938   siStartInfo.hStdInput = hChildStdinRd;\r
9939   siStartInfo.hStdOutput = hChildStdoutWr;\r
9940   siStartInfo.hStdError = hChildStdoutWr;\r
9941 \r
9942   fSuccess = CreateProcess(NULL,\r
9943                            cmdLine,        /* command line */\r
9944                            NULL,           /* process security attributes */\r
9945                            NULL,           /* primary thread security attrs */\r
9946                            TRUE,           /* handles are inherited */\r
9947                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9948                            NULL,           /* use parent's environment */\r
9949                            NULL,\r
9950                            &siStartInfo, /* STARTUPINFO pointer */\r
9951                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9952 \r
9953   err = GetLastError();\r
9954   SetCurrentDirectory(buf); /* return to prev directory */\r
9955   if (! fSuccess) {\r
9956     return err;\r
9957   }\r
9958 \r
9959   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9960     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9961     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9962   }\r
9963 \r
9964   /* Close the handles we don't need in the parent */\r
9965   CloseHandle(piProcInfo.hThread);\r
9966   CloseHandle(hChildStdinRd);\r
9967   CloseHandle(hChildStdoutWr);\r
9968 \r
9969   /* Prepare return value */\r
9970   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9971   cp->kind = CPReal;\r
9972   cp->hProcess = piProcInfo.hProcess;\r
9973   cp->pid = piProcInfo.dwProcessId;\r
9974   cp->hFrom = hChildStdoutRdDup;\r
9975   cp->hTo = hChildStdinWrDup;\r
9976 \r
9977   *pr = (void *) cp;\r
9978 \r
9979   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9980      2000 where engines sometimes don't see the initial command(s)\r
9981      from WinBoard and hang.  I don't understand how that can happen,\r
9982      but the Sleep is harmless, so I've put it in.  Others have also\r
9983      reported what may be the same problem, so hopefully this will fix\r
9984      it for them too.  */\r
9985   Sleep(500);\r
9986 \r
9987   return NO_ERROR;\r
9988 }\r
9989 \r
9990 \r
9991 void\r
9992 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9993 {\r
9994   ChildProc *cp; int result;\r
9995 \r
9996   cp = (ChildProc *) pr;\r
9997   if (cp == NULL) return;\r
9998 \r
9999   switch (cp->kind) {\r
10000   case CPReal:\r
10001     /* TerminateProcess is considered harmful, so... */\r
10002     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10003     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10004     /* The following doesn't work because the chess program\r
10005        doesn't "have the same console" as WinBoard.  Maybe\r
10006        we could arrange for this even though neither WinBoard\r
10007        nor the chess program uses a console for stdio? */\r
10008     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10009 \r
10010     /* [AS] Special termination modes for misbehaving programs... */\r
10011     if( signal == 9 ) { \r
10012         result = TerminateProcess( cp->hProcess, 0 );\r
10013 \r
10014         if ( appData.debugMode) {\r
10015             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10016         }\r
10017     }\r
10018     else if( signal == 10 ) {\r
10019         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10020 \r
10021         if( dw != WAIT_OBJECT_0 ) {\r
10022             result = TerminateProcess( cp->hProcess, 0 );\r
10023 \r
10024             if ( appData.debugMode) {\r
10025                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10026             }\r
10027 \r
10028         }\r
10029     }\r
10030 \r
10031     CloseHandle(cp->hProcess);\r
10032     break;\r
10033 \r
10034   case CPComm:\r
10035     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10036     break;\r
10037 \r
10038   case CPSock:\r
10039     closesocket(cp->sock);\r
10040     WSACleanup();\r
10041     break;\r
10042 \r
10043   case CPRcmd:\r
10044     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10045     closesocket(cp->sock);\r
10046     closesocket(cp->sock2);\r
10047     WSACleanup();\r
10048     break;\r
10049   }\r
10050   free(cp);\r
10051 }\r
10052 \r
10053 void\r
10054 InterruptChildProcess(ProcRef pr)\r
10055 {\r
10056   ChildProc *cp;\r
10057 \r
10058   cp = (ChildProc *) pr;\r
10059   if (cp == NULL) return;\r
10060   switch (cp->kind) {\r
10061   case CPReal:\r
10062     /* The following doesn't work because the chess program\r
10063        doesn't "have the same console" as WinBoard.  Maybe\r
10064        we could arrange for this even though neither WinBoard\r
10065        nor the chess program uses a console for stdio */\r
10066     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10067     break;\r
10068 \r
10069   case CPComm:\r
10070   case CPSock:\r
10071     /* Can't interrupt */\r
10072     break;\r
10073 \r
10074   case CPRcmd:\r
10075     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10076     break;\r
10077   }\r
10078 }\r
10079 \r
10080 \r
10081 int\r
10082 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10083 {\r
10084   char cmdLine[MSG_SIZ];\r
10085 \r
10086   if (port[0] == NULLCHAR) {\r
10087     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10088   } else {\r
10089     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10090   }\r
10091   return StartChildProcess(cmdLine, "", pr);\r
10092 }\r
10093 \r
10094 \r
10095 /* Code to open TCP sockets */\r
10096 \r
10097 int\r
10098 OpenTCP(char *host, char *port, ProcRef *pr)\r
10099 {\r
10100   ChildProc *cp;\r
10101   int err;\r
10102   SOCKET s;\r
10103   struct sockaddr_in sa, mysa;\r
10104   struct hostent FAR *hp;\r
10105   unsigned short uport;\r
10106   WORD wVersionRequested;\r
10107   WSADATA wsaData;\r
10108 \r
10109   /* Initialize socket DLL */\r
10110   wVersionRequested = MAKEWORD(1, 1);\r
10111   err = WSAStartup(wVersionRequested, &wsaData);\r
10112   if (err != 0) return err;\r
10113 \r
10114   /* Make socket */\r
10115   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10116     err = WSAGetLastError();\r
10117     WSACleanup();\r
10118     return err;\r
10119   }\r
10120 \r
10121   /* Bind local address using (mostly) don't-care values.\r
10122    */\r
10123   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10124   mysa.sin_family = AF_INET;\r
10125   mysa.sin_addr.s_addr = INADDR_ANY;\r
10126   uport = (unsigned short) 0;\r
10127   mysa.sin_port = htons(uport);\r
10128   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10129       == SOCKET_ERROR) {\r
10130     err = WSAGetLastError();\r
10131     WSACleanup();\r
10132     return err;\r
10133   }\r
10134 \r
10135   /* Resolve remote host name */\r
10136   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10137   if (!(hp = gethostbyname(host))) {\r
10138     unsigned int b0, b1, b2, b3;\r
10139 \r
10140     err = WSAGetLastError();\r
10141 \r
10142     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10143       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10144       hp->h_addrtype = AF_INET;\r
10145       hp->h_length = 4;\r
10146       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10147       hp->h_addr_list[0] = (char *) malloc(4);\r
10148       hp->h_addr_list[0][0] = (char) b0;\r
10149       hp->h_addr_list[0][1] = (char) b1;\r
10150       hp->h_addr_list[0][2] = (char) b2;\r
10151       hp->h_addr_list[0][3] = (char) b3;\r
10152     } else {\r
10153       WSACleanup();\r
10154       return err;\r
10155     }\r
10156   }\r
10157   sa.sin_family = hp->h_addrtype;\r
10158   uport = (unsigned short) atoi(port);\r
10159   sa.sin_port = htons(uport);\r
10160   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10161 \r
10162   /* Make connection */\r
10163   if (connect(s, (struct sockaddr *) &sa,\r
10164               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10165     err = WSAGetLastError();\r
10166     WSACleanup();\r
10167     return err;\r
10168   }\r
10169 \r
10170   /* Prepare return value */\r
10171   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10172   cp->kind = CPSock;\r
10173   cp->sock = s;\r
10174   *pr = (ProcRef *) cp;\r
10175 \r
10176   return NO_ERROR;\r
10177 }\r
10178 \r
10179 int\r
10180 OpenCommPort(char *name, ProcRef *pr)\r
10181 {\r
10182   HANDLE h;\r
10183   COMMTIMEOUTS ct;\r
10184   ChildProc *cp;\r
10185   char fullname[MSG_SIZ];\r
10186 \r
10187   if (*name != '\\')\r
10188     sprintf(fullname, "\\\\.\\%s", name);\r
10189   else\r
10190     strcpy(fullname, name);\r
10191 \r
10192   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10193                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10194   if (h == (HANDLE) -1) {\r
10195     return GetLastError();\r
10196   }\r
10197   hCommPort = h;\r
10198 \r
10199   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10200 \r
10201   /* Accumulate characters until a 100ms pause, then parse */\r
10202   ct.ReadIntervalTimeout = 100;\r
10203   ct.ReadTotalTimeoutMultiplier = 0;\r
10204   ct.ReadTotalTimeoutConstant = 0;\r
10205   ct.WriteTotalTimeoutMultiplier = 0;\r
10206   ct.WriteTotalTimeoutConstant = 0;\r
10207   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10208 \r
10209   /* Prepare return value */\r
10210   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10211   cp->kind = CPComm;\r
10212   cp->hFrom = h;\r
10213   cp->hTo = h;\r
10214   *pr = (ProcRef *) cp;\r
10215 \r
10216   return NO_ERROR;\r
10217 }\r
10218 \r
10219 int\r
10220 OpenLoopback(ProcRef *pr)\r
10221 {\r
10222   DisplayFatalError("Not implemented", 0, 1);\r
10223   return NO_ERROR;\r
10224 }\r
10225 \r
10226 \r
10227 int\r
10228 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10229 {\r
10230   ChildProc *cp;\r
10231   int err;\r
10232   SOCKET s, s2, s3;\r
10233   struct sockaddr_in sa, mysa;\r
10234   struct hostent FAR *hp;\r
10235   unsigned short uport;\r
10236   WORD wVersionRequested;\r
10237   WSADATA wsaData;\r
10238   int fromPort;\r
10239   char stderrPortStr[MSG_SIZ];\r
10240 \r
10241   /* Initialize socket DLL */\r
10242   wVersionRequested = MAKEWORD(1, 1);\r
10243   err = WSAStartup(wVersionRequested, &wsaData);\r
10244   if (err != 0) return err;\r
10245 \r
10246   /* Resolve remote host name */\r
10247   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10248   if (!(hp = gethostbyname(host))) {\r
10249     unsigned int b0, b1, b2, b3;\r
10250 \r
10251     err = WSAGetLastError();\r
10252 \r
10253     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10254       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10255       hp->h_addrtype = AF_INET;\r
10256       hp->h_length = 4;\r
10257       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10258       hp->h_addr_list[0] = (char *) malloc(4);\r
10259       hp->h_addr_list[0][0] = (char) b0;\r
10260       hp->h_addr_list[0][1] = (char) b1;\r
10261       hp->h_addr_list[0][2] = (char) b2;\r
10262       hp->h_addr_list[0][3] = (char) b3;\r
10263     } else {\r
10264       WSACleanup();\r
10265       return err;\r
10266     }\r
10267   }\r
10268   sa.sin_family = hp->h_addrtype;\r
10269   uport = (unsigned short) 514;\r
10270   sa.sin_port = htons(uport);\r
10271   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10272 \r
10273   /* Bind local socket to unused "privileged" port address\r
10274    */\r
10275   s = INVALID_SOCKET;\r
10276   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10277   mysa.sin_family = AF_INET;\r
10278   mysa.sin_addr.s_addr = INADDR_ANY;\r
10279   for (fromPort = 1023;; fromPort--) {\r
10280     if (fromPort < 0) {\r
10281       WSACleanup();\r
10282       return WSAEADDRINUSE;\r
10283     }\r
10284     if (s == INVALID_SOCKET) {\r
10285       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10286         err = WSAGetLastError();\r
10287         WSACleanup();\r
10288         return err;\r
10289       }\r
10290     }\r
10291     uport = (unsigned short) fromPort;\r
10292     mysa.sin_port = htons(uport);\r
10293     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10294         == SOCKET_ERROR) {\r
10295       err = WSAGetLastError();\r
10296       if (err == WSAEADDRINUSE) continue;\r
10297       WSACleanup();\r
10298       return err;\r
10299     }\r
10300     if (connect(s, (struct sockaddr *) &sa,\r
10301       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10302       err = WSAGetLastError();\r
10303       if (err == WSAEADDRINUSE) {\r
10304         closesocket(s);\r
10305         s = -1;\r
10306         continue;\r
10307       }\r
10308       WSACleanup();\r
10309       return err;\r
10310     }\r
10311     break;\r
10312   }\r
10313 \r
10314   /* Bind stderr local socket to unused "privileged" port address\r
10315    */\r
10316   s2 = 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 == prevStderrPort) continue; // don't reuse port\r
10322     if (fromPort < 0) {\r
10323       (void) closesocket(s);\r
10324       WSACleanup();\r
10325       return WSAEADDRINUSE;\r
10326     }\r
10327     if (s2 == INVALID_SOCKET) {\r
10328       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10329         err = WSAGetLastError();\r
10330         closesocket(s);\r
10331         WSACleanup();\r
10332         return err;\r
10333       }\r
10334     }\r
10335     uport = (unsigned short) fromPort;\r
10336     mysa.sin_port = htons(uport);\r
10337     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10338         == SOCKET_ERROR) {\r
10339       err = WSAGetLastError();\r
10340       if (err == WSAEADDRINUSE) continue;\r
10341       (void) closesocket(s);\r
10342       WSACleanup();\r
10343       return err;\r
10344     }\r
10345     if (listen(s2, 1) == SOCKET_ERROR) {\r
10346       err = WSAGetLastError();\r
10347       if (err == WSAEADDRINUSE) {\r
10348         closesocket(s2);\r
10349         s2 = INVALID_SOCKET;\r
10350         continue;\r
10351       }\r
10352       (void) closesocket(s);\r
10353       (void) closesocket(s2);\r
10354       WSACleanup();\r
10355       return err;\r
10356     }\r
10357     break;\r
10358   }\r
10359   prevStderrPort = fromPort; // remember port used\r
10360   sprintf(stderrPortStr, "%d", fromPort);\r
10361 \r
10362   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10363     err = WSAGetLastError();\r
10364     (void) closesocket(s);\r
10365     (void) closesocket(s2);\r
10366     WSACleanup();\r
10367     return err;\r
10368   }\r
10369 \r
10370   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10371     err = WSAGetLastError();\r
10372     (void) closesocket(s);\r
10373     (void) closesocket(s2);\r
10374     WSACleanup();\r
10375     return err;\r
10376   }\r
10377   if (*user == NULLCHAR) user = UserName();\r
10378   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10379     err = WSAGetLastError();\r
10380     (void) closesocket(s);\r
10381     (void) closesocket(s2);\r
10382     WSACleanup();\r
10383     return err;\r
10384   }\r
10385   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10386     err = WSAGetLastError();\r
10387     (void) closesocket(s);\r
10388     (void) closesocket(s2);\r
10389     WSACleanup();\r
10390     return err;\r
10391   }\r
10392 \r
10393   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10394     err = WSAGetLastError();\r
10395     (void) closesocket(s);\r
10396     (void) closesocket(s2);\r
10397     WSACleanup();\r
10398     return err;\r
10399   }\r
10400   (void) closesocket(s2);  /* Stop listening */\r
10401 \r
10402   /* Prepare return value */\r
10403   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10404   cp->kind = CPRcmd;\r
10405   cp->sock = s;\r
10406   cp->sock2 = s3;\r
10407   *pr = (ProcRef *) cp;\r
10408 \r
10409   return NO_ERROR;\r
10410 }\r
10411 \r
10412 \r
10413 InputSourceRef\r
10414 AddInputSource(ProcRef pr, int lineByLine,\r
10415                InputCallback func, VOIDSTAR closure)\r
10416 {\r
10417   InputSource *is, *is2 = NULL;\r
10418   ChildProc *cp = (ChildProc *) pr;\r
10419 \r
10420   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10421   is->lineByLine = lineByLine;\r
10422   is->func = func;\r
10423   is->closure = closure;\r
10424   is->second = NULL;\r
10425   is->next = is->buf;\r
10426   if (pr == NoProc) {\r
10427     is->kind = CPReal;\r
10428     consoleInputSource = is;\r
10429   } else {\r
10430     is->kind = cp->kind;\r
10431     /* \r
10432         [AS] Try to avoid a race condition if the thread is given control too early:\r
10433         we create all threads suspended so that the is->hThread variable can be\r
10434         safely assigned, then let the threads start with ResumeThread.\r
10435     */\r
10436     switch (cp->kind) {\r
10437     case CPReal:\r
10438       is->hFile = cp->hFrom;\r
10439       cp->hFrom = NULL; /* now owned by InputThread */\r
10440       is->hThread =\r
10441         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10442                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10443       break;\r
10444 \r
10445     case CPComm:\r
10446       is->hFile = cp->hFrom;\r
10447       cp->hFrom = NULL; /* now owned by InputThread */\r
10448       is->hThread =\r
10449         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10450                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10451       break;\r
10452 \r
10453     case CPSock:\r
10454       is->sock = cp->sock;\r
10455       is->hThread =\r
10456         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10457                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10458       break;\r
10459 \r
10460     case CPRcmd:\r
10461       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10462       *is2 = *is;\r
10463       is->sock = cp->sock;\r
10464       is->second = is2;\r
10465       is2->sock = cp->sock2;\r
10466       is2->second = is2;\r
10467       is->hThread =\r
10468         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10469                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10470       is2->hThread =\r
10471         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10472                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10473       break;\r
10474     }\r
10475 \r
10476     if( is->hThread != NULL ) {\r
10477         ResumeThread( is->hThread );\r
10478     }\r
10479 \r
10480     if( is2 != NULL && is2->hThread != NULL ) {\r
10481         ResumeThread( is2->hThread );\r
10482     }\r
10483   }\r
10484 \r
10485   return (InputSourceRef) is;\r
10486 }\r
10487 \r
10488 void\r
10489 RemoveInputSource(InputSourceRef isr)\r
10490 {\r
10491   InputSource *is;\r
10492 \r
10493   is = (InputSource *) isr;\r
10494   is->hThread = NULL;  /* tell thread to stop */\r
10495   CloseHandle(is->hThread);\r
10496   if (is->second != NULL) {\r
10497     is->second->hThread = NULL;\r
10498     CloseHandle(is->second->hThread);\r
10499   }\r
10500 }\r
10501 \r
10502 \r
10503 int\r
10504 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10505 {\r
10506   DWORD dOutCount;\r
10507   int outCount = SOCKET_ERROR;\r
10508   ChildProc *cp = (ChildProc *) pr;\r
10509   static OVERLAPPED ovl;\r
10510 \r
10511   if (pr == NoProc) {\r
10512     ConsoleOutput(message, count, FALSE);\r
10513     return count;\r
10514   } \r
10515 \r
10516   if (ovl.hEvent == NULL) {\r
10517     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10518   }\r
10519   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10520 \r
10521   switch (cp->kind) {\r
10522   case CPSock:\r
10523   case CPRcmd:\r
10524     outCount = send(cp->sock, message, count, 0);\r
10525     if (outCount == SOCKET_ERROR) {\r
10526       *outError = WSAGetLastError();\r
10527     } else {\r
10528       *outError = NO_ERROR;\r
10529     }\r
10530     break;\r
10531 \r
10532   case CPReal:\r
10533     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10534                   &dOutCount, NULL)) {\r
10535       *outError = NO_ERROR;\r
10536       outCount = (int) dOutCount;\r
10537     } else {\r
10538       *outError = GetLastError();\r
10539     }\r
10540     break;\r
10541 \r
10542   case CPComm:\r
10543     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10544                             &dOutCount, &ovl);\r
10545     if (*outError == NO_ERROR) {\r
10546       outCount = (int) dOutCount;\r
10547     }\r
10548     break;\r
10549   }\r
10550   return outCount;\r
10551 }\r
10552 \r
10553 int\r
10554 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10555                        long msdelay)\r
10556 {\r
10557   /* Ignore delay, not implemented for WinBoard */\r
10558   return OutputToProcess(pr, message, count, outError);\r
10559 }\r
10560 \r
10561 \r
10562 void\r
10563 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10564                         char *buf, int count, int error)\r
10565 {\r
10566   DisplayFatalError("Not implemented", 0, 1);\r
10567 }\r
10568 \r
10569 /* see wgamelist.c for Game List functions */\r
10570 /* see wedittags.c for Edit Tags functions */\r
10571 \r
10572 \r
10573 VOID\r
10574 ICSInitScript()\r
10575 {\r
10576   FILE *f;\r
10577   char buf[MSG_SIZ];\r
10578   char *dummy;\r
10579 \r
10580   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10581     f = fopen(buf, "r");\r
10582     if (f != NULL) {\r
10583       ProcessICSInitScript(f);\r
10584       fclose(f);\r
10585     }\r
10586   }\r
10587 }\r
10588 \r
10589 \r
10590 VOID\r
10591 StartAnalysisClock()\r
10592 {\r
10593   if (analysisTimerEvent) return;\r
10594   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10595                                         (UINT) 2000, NULL);\r
10596 }\r
10597 \r
10598 LRESULT CALLBACK\r
10599 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10600 {\r
10601   static HANDLE hwndText;\r
10602   RECT rect;\r
10603   static int sizeX, sizeY;\r
10604   int newSizeX, newSizeY, flags;\r
10605   MINMAXINFO *mmi;\r
10606 \r
10607   switch (message) {\r
10608   case WM_INITDIALOG: /* message: initialize dialog box */\r
10609     /* Initialize the dialog items */\r
10610     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10611     SetWindowText(hDlg, analysisTitle);\r
10612     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10613     /* Size and position the dialog */\r
10614     if (!analysisDialog) {\r
10615       analysisDialog = hDlg;\r
10616       flags = SWP_NOZORDER;\r
10617       GetClientRect(hDlg, &rect);\r
10618       sizeX = rect.right;\r
10619       sizeY = rect.bottom;\r
10620       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10621           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10622         WINDOWPLACEMENT wp;\r
10623         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10624         wp.length = sizeof(WINDOWPLACEMENT);\r
10625         wp.flags = 0;\r
10626         wp.showCmd = SW_SHOW;\r
10627         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10628         wp.rcNormalPosition.left = analysisX;\r
10629         wp.rcNormalPosition.right = analysisX + analysisW;\r
10630         wp.rcNormalPosition.top = analysisY;\r
10631         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10632         SetWindowPlacement(hDlg, &wp);\r
10633 \r
10634         GetClientRect(hDlg, &rect);\r
10635         newSizeX = rect.right;\r
10636         newSizeY = rect.bottom;\r
10637         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10638                               newSizeX, newSizeY);\r
10639         sizeX = newSizeX;\r
10640         sizeY = newSizeY;\r
10641       }\r
10642     }\r
10643     return FALSE;\r
10644 \r
10645   case WM_COMMAND: /* message: received a command */\r
10646     switch (LOWORD(wParam)) {\r
10647     case IDCANCEL:\r
10648       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10649           ExitAnalyzeMode();\r
10650           ModeHighlight();\r
10651           return TRUE;\r
10652       }\r
10653       EditGameEvent();\r
10654       return TRUE;\r
10655     default:\r
10656       break;\r
10657     }\r
10658     break;\r
10659 \r
10660   case WM_SIZE:\r
10661     newSizeX = LOWORD(lParam);\r
10662     newSizeY = HIWORD(lParam);\r
10663     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10664     sizeX = newSizeX;\r
10665     sizeY = newSizeY;\r
10666     break;\r
10667 \r
10668   case WM_GETMINMAXINFO:\r
10669     /* Prevent resizing window too small */\r
10670     mmi = (MINMAXINFO *) lParam;\r
10671     mmi->ptMinTrackSize.x = 100;\r
10672     mmi->ptMinTrackSize.y = 100;\r
10673     break;\r
10674   }\r
10675   return FALSE;\r
10676 }\r
10677 \r
10678 VOID\r
10679 AnalysisPopUp(char* title, char* str)\r
10680 {\r
10681   FARPROC lpProc;\r
10682   char *p, *q;\r
10683 \r
10684   /* [AS] */\r
10685   EngineOutputPopUp();\r
10686   return;\r
10687 \r
10688   if (str == NULL) str = "";\r
10689   p = (char *) malloc(2 * strlen(str) + 2);\r
10690   q = p;\r
10691   while (*str) {\r
10692     if (*str == '\n') *q++ = '\r';\r
10693     *q++ = *str++;\r
10694   }\r
10695   *q = NULLCHAR;\r
10696   if (analysisText != NULL) free(analysisText);\r
10697   analysisText = p;\r
10698 \r
10699   if (analysisDialog) {\r
10700     SetWindowText(analysisDialog, title);\r
10701     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10702     ShowWindow(analysisDialog, SW_SHOW);\r
10703   } else {\r
10704     analysisTitle = title;\r
10705     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10706     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10707                  hwndMain, (DLGPROC)lpProc);\r
10708     FreeProcInstance(lpProc);\r
10709   }\r
10710   analysisDialogUp = TRUE;  \r
10711 }\r
10712 \r
10713 VOID\r
10714 AnalysisPopDown()\r
10715 {\r
10716   if (analysisDialog) {\r
10717     ShowWindow(analysisDialog, SW_HIDE);\r
10718   }\r
10719   analysisDialogUp = FALSE;  \r
10720 }\r
10721 \r
10722 \r
10723 VOID\r
10724 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10725 {\r
10726   highlightInfo.sq[0].x = fromX;\r
10727   highlightInfo.sq[0].y = fromY;\r
10728   highlightInfo.sq[1].x = toX;\r
10729   highlightInfo.sq[1].y = toY;\r
10730 }\r
10731 \r
10732 VOID\r
10733 ClearHighlights()\r
10734 {\r
10735   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10736     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10737 }\r
10738 \r
10739 VOID\r
10740 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10741 {\r
10742   premoveHighlightInfo.sq[0].x = fromX;\r
10743   premoveHighlightInfo.sq[0].y = fromY;\r
10744   premoveHighlightInfo.sq[1].x = toX;\r
10745   premoveHighlightInfo.sq[1].y = toY;\r
10746 }\r
10747 \r
10748 VOID\r
10749 ClearPremoveHighlights()\r
10750 {\r
10751   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10752     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10753 }\r
10754 \r
10755 VOID\r
10756 ShutDownFrontEnd()\r
10757 {\r
10758   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10759   DeleteClipboardTempFiles();\r
10760 }\r
10761 \r
10762 void\r
10763 BoardToTop()\r
10764 {\r
10765     if (IsIconic(hwndMain))\r
10766       ShowWindow(hwndMain, SW_RESTORE);\r
10767 \r
10768     SetActiveWindow(hwndMain);\r
10769 }\r
10770 \r
10771 /*\r
10772  * Prototypes for animation support routines\r
10773  */\r
10774 static void ScreenSquare(int column, int row, POINT * pt);\r
10775 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10776      POINT frames[], int * nFrames);\r
10777 \r
10778 \r
10779 void\r
10780 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10781 {       // [HGM] atomic: animate blast wave\r
10782         int i;\r
10783 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10784         explodeInfo.fromX = fromX;\r
10785         explodeInfo.fromY = fromY;\r
10786         explodeInfo.toX = toX;\r
10787         explodeInfo.toY = toY;\r
10788         for(i=1; i<nFrames; i++) {\r
10789             explodeInfo.radius = (i*180)/(nFrames-1);\r
10790             DrawPosition(FALSE, NULL);\r
10791             Sleep(appData.animSpeed);\r
10792         }\r
10793         explodeInfo.radius = 0;\r
10794         DrawPosition(TRUE, NULL);\r
10795 }\r
10796 \r
10797 #define kFactor 4\r
10798 \r
10799 void\r
10800 AnimateMove(board, fromX, fromY, toX, toY)\r
10801      Board board;\r
10802      int fromX;\r
10803      int fromY;\r
10804      int toX;\r
10805      int toY;\r
10806 {\r
10807   ChessSquare piece;\r
10808   POINT start, finish, mid;\r
10809   POINT frames[kFactor * 2 + 1];\r
10810   int nFrames, n;\r
10811 \r
10812   if (!appData.animate) return;\r
10813   if (doingSizing) return;\r
10814   if (fromY < 0 || fromX < 0) return;\r
10815   piece = board[fromY][fromX];\r
10816   if (piece >= EmptySquare) return;\r
10817 \r
10818   ScreenSquare(fromX, fromY, &start);\r
10819   ScreenSquare(toX, toY, &finish);\r
10820 \r
10821   /* All pieces except knights move in straight line */\r
10822   if (piece != WhiteKnight && piece != BlackKnight) {\r
10823     mid.x = start.x + (finish.x - start.x) / 2;\r
10824     mid.y = start.y + (finish.y - start.y) / 2;\r
10825   } else {\r
10826     /* Knight: make diagonal movement then straight */\r
10827     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10828        mid.x = start.x + (finish.x - start.x) / 2;\r
10829        mid.y = finish.y;\r
10830      } else {\r
10831        mid.x = finish.x;\r
10832        mid.y = start.y + (finish.y - start.y) / 2;\r
10833      }\r
10834   }\r
10835   \r
10836   /* Don't use as many frames for very short moves */\r
10837   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10838     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10839   else\r
10840     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10841 \r
10842   animInfo.from.x = fromX;\r
10843   animInfo.from.y = fromY;\r
10844   animInfo.to.x = toX;\r
10845   animInfo.to.y = toY;\r
10846   animInfo.lastpos = start;\r
10847   animInfo.piece = piece;\r
10848   for (n = 0; n < nFrames; n++) {\r
10849     animInfo.pos = frames[n];\r
10850     DrawPosition(FALSE, NULL);\r
10851     animInfo.lastpos = animInfo.pos;\r
10852     Sleep(appData.animSpeed);\r
10853   }\r
10854   animInfo.pos = finish;\r
10855   DrawPosition(FALSE, NULL);\r
10856   animInfo.piece = EmptySquare;\r
10857   if(gameInfo.variant == VariantAtomic && \r
10858      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10859         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10860 }\r
10861 \r
10862 /*      Convert board position to corner of screen rect and color       */\r
10863 \r
10864 static void\r
10865 ScreenSquare(column, row, pt)\r
10866      int column; int row; POINT * pt;\r
10867 {\r
10868   if (flipView) {\r
10869     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10870     pt->y = lineGap + row * (squareSize + lineGap);\r
10871   } else {\r
10872     pt->x = lineGap + column * (squareSize + lineGap);\r
10873     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10874   }\r
10875 }\r
10876 \r
10877 /*      Generate a series of frame coords from start->mid->finish.\r
10878         The movement rate doubles until the half way point is\r
10879         reached, then halves back down to the final destination,\r
10880         which gives a nice slow in/out effect. The algorithmn\r
10881         may seem to generate too many intermediates for short\r
10882         moves, but remember that the purpose is to attract the\r
10883         viewers attention to the piece about to be moved and\r
10884         then to where it ends up. Too few frames would be less\r
10885         noticeable.                                             */\r
10886 \r
10887 static void\r
10888 Tween(start, mid, finish, factor, frames, nFrames)\r
10889      POINT * start; POINT * mid;\r
10890      POINT * finish; int factor;\r
10891      POINT frames[]; int * nFrames;\r
10892 {\r
10893   int n, fraction = 1, count = 0;\r
10894 \r
10895   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10896   for (n = 0; n < factor; n++)\r
10897     fraction *= 2;\r
10898   for (n = 0; n < factor; n++) {\r
10899     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10900     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10901     count ++;\r
10902     fraction = fraction / 2;\r
10903   }\r
10904   \r
10905   /* Midpoint */\r
10906   frames[count] = *mid;\r
10907   count ++;\r
10908   \r
10909   /* Slow out, stepping 1/2, then 1/4, ... */\r
10910   fraction = 2;\r
10911   for (n = 0; n < factor; n++) {\r
10912     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10913     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10914     count ++;\r
10915     fraction = fraction * 2;\r
10916   }\r
10917   *nFrames = count;\r
10918 }\r
10919 \r
10920 void\r
10921 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10922 {\r
10923     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10924 \r
10925     EvalGraphSet( first, last, current, pvInfoList );\r
10926 }\r
10927 \r
10928 void SetProgramStats( FrontEndProgramStats * stats )\r
10929 {\r
10930     EngineOutputUpdate( stats );\r
10931 }\r