This patch adds <Enter> to the characters that cause an automatic switch to the ICS...
[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   { "keepLineBreaksICS", ArgBoolean, (LPVOID) &appData.noJoin, TRUE },\r
1396   { "wrapContinuationSequence", ArgString, (LPVOID) &appData.wrapContSeq, FALSE },\r
1397   { "useInternalWrap", ArgTrue, (LPVOID) &appData.useInternalWrap, FALSE }, /* noJoin usurps this if set */\r
1398   \r
1399   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1400   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1401   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1402   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1403   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1404   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1405   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1406   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1407   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1408   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1409   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1410   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1411   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1412   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1413   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1414   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1415   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1416   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1417   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1418   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1419   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1420   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1421   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1422   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1423   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1424   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1425   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1426   /* [AS] Layout stuff */\r
1427   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1428   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1429   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1430   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1431   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1432 \r
1433   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1434   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1435   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1436   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1437   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1438 \r
1439   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1440   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1441   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1442   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1443   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1444 \r
1445   { NULL, ArgNone, NULL, FALSE }\r
1446 };\r
1447 \r
1448 \r
1449 /* Kludge for indirection files on command line */\r
1450 char* lastIndirectionFilename;\r
1451 ArgDescriptor argDescriptorIndirection =\r
1452 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1453 \r
1454 \r
1455 VOID\r
1456 ExitArgError(char *msg, char *badArg)\r
1457 {\r
1458   char buf[MSG_SIZ];\r
1459 \r
1460   sprintf(buf, "%s %s", msg, badArg);\r
1461   DisplayFatalError(buf, 0, 2);\r
1462   exit(2);\r
1463 }\r
1464 \r
1465 /* Command line font name parser.  NULL name means do nothing.\r
1466    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1467    For backward compatibility, syntax without the colon is also\r
1468    accepted, but font names with digits in them won't work in that case.\r
1469 */\r
1470 VOID\r
1471 ParseFontName(char *name, MyFontParams *mfp)\r
1472 {\r
1473   char *p, *q;\r
1474   if (name == NULL) return;\r
1475   p = name;\r
1476   q = strchr(p, ':');\r
1477   if (q) {\r
1478     if (q - p >= sizeof(mfp->faceName))\r
1479       ExitArgError("Font name too long:", name);\r
1480     memcpy(mfp->faceName, p, q - p);\r
1481     mfp->faceName[q - p] = NULLCHAR;\r
1482     p = q + 1;\r
1483   } else {\r
1484     q = mfp->faceName;\r
1485     while (*p && !isdigit(*p)) {\r
1486       *q++ = *p++;\r
1487       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1488         ExitArgError("Font name too long:", name);\r
1489     }\r
1490     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1491     *q = NULLCHAR;\r
1492   }\r
1493   if (!*p) ExitArgError("Font point size missing:", name);\r
1494   mfp->pointSize = (float) atof(p);\r
1495   mfp->bold = (strchr(p, 'b') != NULL);\r
1496   mfp->italic = (strchr(p, 'i') != NULL);\r
1497   mfp->underline = (strchr(p, 'u') != NULL);\r
1498   mfp->strikeout = (strchr(p, 's') != NULL);\r
1499   mfp->charset = DEFAULT_CHARSET;\r
1500   q = strchr(p, 'c');\r
1501   if (q)\r
1502     mfp->charset = (BYTE) atoi(q+1);\r
1503 }\r
1504 \r
1505 /* Color name parser.\r
1506    X version accepts X color names, but this one\r
1507    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1508 COLORREF\r
1509 ParseColorName(char *name)\r
1510 {\r
1511   int red, green, blue, count;\r
1512   char buf[MSG_SIZ];\r
1513 \r
1514   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1515   if (count != 3) {\r
1516     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1517       &red, &green, &blue);\r
1518   }\r
1519   if (count != 3) {\r
1520     sprintf(buf, "Can't parse color name %s", name);\r
1521     DisplayError(buf, 0);\r
1522     return RGB(0, 0, 0);\r
1523   }\r
1524   return PALETTERGB(red, green, blue);\r
1525 }\r
1526 \r
1527 \r
1528 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1529 {\r
1530   char *e = argValue;\r
1531   int eff = 0;\r
1532 \r
1533   while (*e) {\r
1534     if (*e == 'b')      eff |= CFE_BOLD;\r
1535     else if (*e == 'i') eff |= CFE_ITALIC;\r
1536     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1537     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1538     else if (*e == '#' || isdigit(*e)) break;\r
1539     e++;\r
1540   }\r
1541   *effects = eff;\r
1542   *color   = ParseColorName(e);\r
1543 }\r
1544 \r
1545 \r
1546 BoardSize\r
1547 ParseBoardSize(char *name)\r
1548 {\r
1549   BoardSize bs = SizeTiny;\r
1550   while (sizeInfo[bs].name != NULL) {\r
1551     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1552     bs++;\r
1553   }\r
1554   ExitArgError("Unrecognized board size value", name);\r
1555   return bs; /* not reached */\r
1556 }\r
1557 \r
1558 \r
1559 char\r
1560 StringGet(void *getClosure)\r
1561 {\r
1562   char **p = (char **) getClosure;\r
1563   return *((*p)++);\r
1564 }\r
1565 \r
1566 char\r
1567 FileGet(void *getClosure)\r
1568 {\r
1569   int c;\r
1570   FILE* f = (FILE*) getClosure;\r
1571 \r
1572   c = getc(f);\r
1573   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1574   if (c == EOF)\r
1575     return NULLCHAR;\r
1576   else\r
1577     return (char) c;\r
1578 }\r
1579 \r
1580 /* Parse settings file named "name". If file found, return the\r
1581    full name in fullname and return TRUE; else return FALSE */\r
1582 BOOLEAN\r
1583 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1584 {\r
1585   char *dummy;\r
1586   FILE *f;\r
1587   int ok; char buf[MSG_SIZ];\r
1588 \r
1589   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1590   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1591     sprintf(buf, "%s.ini", name);\r
1592     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1593   }\r
1594   if (ok) {\r
1595     f = fopen(fullname, "r");\r
1596     if (f != NULL) {\r
1597       ParseArgs(FileGet, f);\r
1598       fclose(f);\r
1599       return TRUE;\r
1600     }\r
1601   }\r
1602   return FALSE;\r
1603 }\r
1604 \r
1605 VOID\r
1606 ParseArgs(GetFunc get, void *cl)\r
1607 {\r
1608   char argName[ARG_MAX];\r
1609   char argValue[ARG_MAX];\r
1610   ArgDescriptor *ad;\r
1611   char start;\r
1612   char *q;\r
1613   int i, octval;\r
1614   char ch;\r
1615   int posarg = 0;\r
1616 \r
1617   ch = get(cl);\r
1618   for (;;) {\r
1619     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1620     if (ch == NULLCHAR) break;\r
1621     if (ch == ';') {\r
1622       /* Comment to end of line */\r
1623       ch = get(cl);\r
1624       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1625       continue;\r
1626     } else if (ch == '/' || ch == '-') {\r
1627       /* Switch */\r
1628       q = argName;\r
1629       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1630              ch != '\n' && ch != '\t') {\r
1631         *q++ = ch;\r
1632         ch = get(cl);\r
1633       }\r
1634       *q = NULLCHAR;\r
1635 \r
1636       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1637         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1638 \r
1639       if (ad->argName == NULL)\r
1640         ExitArgError("Unrecognized argument", argName);\r
1641 \r
1642     } else if (ch == '@') {\r
1643       /* Indirection file */\r
1644       ad = &argDescriptorIndirection;\r
1645       ch = get(cl);\r
1646     } else {\r
1647       /* Positional argument */\r
1648       ad = &argDescriptors[posarg++];\r
1649       strcpy(argName, ad->argName);\r
1650     }\r
1651 \r
1652     if (ad->argType == ArgTrue) {\r
1653       *(Boolean *) ad->argLoc = TRUE;\r
1654       continue;\r
1655     }\r
1656     if (ad->argType == ArgFalse) {\r
1657       *(Boolean *) ad->argLoc = FALSE;\r
1658       continue;\r
1659     }\r
1660 \r
1661     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1662     if (ch == NULLCHAR || ch == '\n') {\r
1663       ExitArgError("No value provided for argument", argName);\r
1664     }\r
1665     q = argValue;\r
1666     if (ch == '{') {\r
1667       // Quoting with { }.  No characters have to (or can) be escaped.\r
1668       // Thus the string cannot contain a '}' character.\r
1669       start = ch;\r
1670       ch = get(cl);\r
1671       while (start) {\r
1672         switch (ch) {\r
1673         case NULLCHAR:\r
1674           start = NULLCHAR;\r
1675           break;\r
1676           \r
1677         case '}':\r
1678           ch = get(cl);\r
1679           start = NULLCHAR;\r
1680           break;\r
1681 \r
1682         default:\r
1683           *q++ = ch;\r
1684           ch = get(cl);\r
1685           break;\r
1686         }\r
1687       }   \r
1688     } else if (ch == '\'' || ch == '"') {\r
1689       // Quoting with ' ' or " ", with \ as escape character.\r
1690       // Inconvenient for long strings that may contain Windows filenames.\r
1691       start = ch;\r
1692       ch = get(cl);\r
1693       while (start) {\r
1694         switch (ch) {\r
1695         case NULLCHAR:\r
1696           start = NULLCHAR;\r
1697           break;\r
1698 \r
1699         default:\r
1700         not_special:\r
1701           *q++ = ch;\r
1702           ch = get(cl);\r
1703           break;\r
1704 \r
1705         case '\'':\r
1706         case '\"':\r
1707           if (ch == start) {\r
1708             ch = get(cl);\r
1709             start = NULLCHAR;\r
1710             break;\r
1711           } else {\r
1712             goto not_special;\r
1713           }\r
1714 \r
1715         case '\\':\r
1716           if (ad->argType == ArgFilename\r
1717               || ad->argType == ArgSettingsFilename) {\r
1718               goto not_special;\r
1719           }\r
1720           ch = get(cl);\r
1721           switch (ch) {\r
1722           case NULLCHAR:\r
1723             ExitArgError("Incomplete \\ escape in value for", argName);\r
1724             break;\r
1725           case 'n':\r
1726             *q++ = '\n';\r
1727             ch = get(cl);\r
1728             break;\r
1729           case 'r':\r
1730             *q++ = '\r';\r
1731             ch = get(cl);\r
1732             break;\r
1733           case 't':\r
1734             *q++ = '\t';\r
1735             ch = get(cl);\r
1736             break;\r
1737           case 'b':\r
1738             *q++ = '\b';\r
1739             ch = get(cl);\r
1740             break;\r
1741           case 'f':\r
1742             *q++ = '\f';\r
1743             ch = get(cl);\r
1744             break;\r
1745           default:\r
1746             octval = 0;\r
1747             for (i = 0; i < 3; i++) {\r
1748               if (ch >= '0' && ch <= '7') {\r
1749                 octval = octval*8 + (ch - '0');\r
1750                 ch = get(cl);\r
1751               } else {\r
1752                 break;\r
1753               }\r
1754             }\r
1755             if (i > 0) {\r
1756               *q++ = (char) octval;\r
1757             } else {\r
1758               *q++ = ch;\r
1759               ch = get(cl);\r
1760             }\r
1761             break;\r
1762           }\r
1763           break;\r
1764         }\r
1765       }\r
1766     } else {\r
1767       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1768         *q++ = ch;\r
1769         ch = get(cl);\r
1770       }\r
1771     }\r
1772     *q = NULLCHAR;\r
1773 \r
1774     switch (ad->argType) {\r
1775     case ArgInt:\r
1776       *(int *) ad->argLoc = atoi(argValue);\r
1777       break;\r
1778 \r
1779     case ArgX:\r
1780       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1781       break;\r
1782 \r
1783     case ArgY:\r
1784       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1785       break;\r
1786 \r
1787     case ArgZ:\r
1788       *(int *) ad->argLoc = atoi(argValue);\r
1789       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1790       break;\r
1791 \r
1792     case ArgFloat:\r
1793       *(float *) ad->argLoc = (float) atof(argValue);\r
1794       break;\r
1795 \r
1796     case ArgString:\r
1797     case ArgFilename:\r
1798       *(char **) ad->argLoc = strdup(argValue);\r
1799       break;\r
1800 \r
1801     case ArgSettingsFilename:\r
1802       {\r
1803         char fullname[MSG_SIZ];\r
1804         if (ParseSettingsFile(argValue, fullname)) {\r
1805           if (ad->argLoc != NULL) {\r
1806             *(char **) ad->argLoc = strdup(fullname);\r
1807           }\r
1808         } else {\r
1809           if (ad->argLoc != NULL) {\r
1810           } else {\r
1811             ExitArgError("Failed to open indirection file", argValue);\r
1812           }\r
1813         }\r
1814       }\r
1815       break;\r
1816 \r
1817     case ArgBoolean:\r
1818       switch (argValue[0]) {\r
1819       case 't':\r
1820       case 'T':\r
1821         *(Boolean *) ad->argLoc = TRUE;\r
1822         break;\r
1823       case 'f':\r
1824       case 'F':\r
1825         *(Boolean *) ad->argLoc = FALSE;\r
1826         break;\r
1827       default:\r
1828         ExitArgError("Unrecognized boolean argument value", argValue);\r
1829         break;\r
1830       }\r
1831       break;\r
1832 \r
1833     case ArgColor:\r
1834       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1835       break;\r
1836 \r
1837     case ArgAttribs: {\r
1838       ColorClass cc = (ColorClass)ad->argLoc;\r
1839       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1840       }\r
1841       break;\r
1842       \r
1843     case ArgBoardSize:\r
1844       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1845       break;\r
1846 \r
1847     case ArgFont:\r
1848       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1849       break;\r
1850 \r
1851     case ArgCommSettings:\r
1852       ParseCommSettings(argValue, &dcb);\r
1853       break;\r
1854 \r
1855     case ArgNone:\r
1856       ExitArgError("Unrecognized argument", argValue);\r
1857       break;\r
1858     case ArgTrue:\r
1859     case ArgFalse: ;\r
1860     }\r
1861   }\r
1862 }\r
1863 \r
1864 VOID\r
1865 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1866 {\r
1867   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1868   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1869   DeleteDC(hdc);\r
1870   lf->lfWidth = 0;\r
1871   lf->lfEscapement = 0;\r
1872   lf->lfOrientation = 0;\r
1873   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1874   lf->lfItalic = mfp->italic;\r
1875   lf->lfUnderline = mfp->underline;\r
1876   lf->lfStrikeOut = mfp->strikeout;\r
1877   lf->lfCharSet = mfp->charset;\r
1878   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1879   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1880   lf->lfQuality = DEFAULT_QUALITY;\r
1881   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1882   strcpy(lf->lfFaceName, mfp->faceName);\r
1883 }\r
1884 \r
1885 VOID\r
1886 CreateFontInMF(MyFont *mf)\r
1887 {\r
1888   LFfromMFP(&mf->lf, &mf->mfp);\r
1889   if (mf->hf) DeleteObject(mf->hf);\r
1890   mf->hf = CreateFontIndirect(&mf->lf);\r
1891 }\r
1892 \r
1893 VOID\r
1894 SetDefaultTextAttribs()\r
1895 {\r
1896   ColorClass cc;\r
1897   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1898     ParseAttribs(&textAttribs[cc].color, \r
1899                  &textAttribs[cc].effects, \r
1900                  defaultTextAttribs[cc]);\r
1901   }\r
1902 }\r
1903 \r
1904 VOID\r
1905 SetDefaultSounds()\r
1906 {\r
1907   ColorClass cc;\r
1908   SoundClass sc;\r
1909   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1910     textAttribs[cc].sound.name = strdup("");\r
1911     textAttribs[cc].sound.data = NULL;\r
1912   }\r
1913   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1914     sounds[sc].name = strdup("");\r
1915     sounds[sc].data = NULL;\r
1916   }\r
1917   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1918 }\r
1919 \r
1920 VOID\r
1921 LoadAllSounds()\r
1922 {\r
1923   ColorClass cc;\r
1924   SoundClass sc;\r
1925   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1926     MyLoadSound(&textAttribs[cc].sound);\r
1927   }\r
1928   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1929     MyLoadSound(&sounds[sc]);\r
1930   }\r
1931 }\r
1932 \r
1933 VOID\r
1934 InitAppData(LPSTR lpCmdLine)\r
1935 {\r
1936   int i, j;\r
1937   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1938   char *dummy, *p;\r
1939 \r
1940   programName = szAppName;\r
1941 \r
1942   /* Initialize to defaults */\r
1943   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1944   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1945   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1946   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1947   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1948   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1949   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1950   SetDefaultTextAttribs();\r
1951   SetDefaultSounds();\r
1952   appData.movesPerSession = MOVES_PER_SESSION;\r
1953   appData.initString = INIT_STRING;\r
1954   appData.secondInitString = INIT_STRING;\r
1955   appData.firstComputerString = COMPUTER_STRING;\r
1956   appData.secondComputerString = COMPUTER_STRING;\r
1957   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1958   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1959   appData.firstPlaysBlack = FALSE;\r
1960   appData.noChessProgram = FALSE;\r
1961   chessProgram = FALSE;\r
1962   appData.firstHost = FIRST_HOST;\r
1963   appData.secondHost = SECOND_HOST;\r
1964   appData.firstDirectory = FIRST_DIRECTORY;\r
1965   appData.secondDirectory = SECOND_DIRECTORY;\r
1966   appData.bitmapDirectory = "";\r
1967   appData.remoteShell = REMOTE_SHELL;\r
1968   appData.remoteUser = "";\r
1969   appData.timeDelay = TIME_DELAY;\r
1970   appData.timeControl = TIME_CONTROL;\r
1971   appData.timeIncrement = TIME_INCREMENT;\r
1972   appData.icsActive = FALSE;\r
1973   appData.icsHost = "";\r
1974   appData.icsPort = ICS_PORT;\r
1975   appData.icsCommPort = ICS_COMM_PORT;\r
1976   appData.icsLogon = ICS_LOGON;\r
1977   appData.icsHelper = "";\r
1978   appData.useTelnet = FALSE;\r
1979   appData.telnetProgram = TELNET_PROGRAM;\r
1980   appData.gateway = "";\r
1981   appData.loadGameFile = "";\r
1982   appData.loadGameIndex = 0;\r
1983   appData.saveGameFile = "";\r
1984   appData.autoSaveGames = FALSE;\r
1985   appData.loadPositionFile = "";\r
1986   appData.loadPositionIndex = 1;\r
1987   appData.savePositionFile = "";\r
1988   appData.matchMode = FALSE;\r
1989   appData.matchGames = 0;\r
1990   appData.monoMode = FALSE;\r
1991   appData.debugMode = FALSE;\r
1992   appData.clockMode = TRUE;\r
1993   boardSize = (BoardSize) -1; /* determine by screen size */\r
1994   appData.Iconic = FALSE; /*unused*/\r
1995   appData.searchTime = "";\r
1996   appData.searchDepth = 0;\r
1997   appData.showCoords = FALSE;\r
1998   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1999   appData.autoCallFlag = FALSE;\r
2000   appData.flipView = FALSE;\r
2001   appData.autoFlipView = TRUE;\r
2002   appData.cmailGameName = "";\r
2003   appData.alwaysPromoteToQueen = FALSE;\r
2004   appData.oldSaveStyle = FALSE;\r
2005   appData.quietPlay = FALSE;\r
2006   appData.showThinking = FALSE;\r
2007   appData.ponderNextMove = TRUE;\r
2008   appData.periodicUpdates = TRUE;\r
2009   appData.popupExitMessage = TRUE;\r
2010   appData.popupMoveErrors = FALSE;\r
2011   appData.autoObserve = FALSE;\r
2012   appData.autoComment = FALSE;\r
2013   appData.animate = TRUE;\r
2014   appData.animSpeed = 10;\r
2015   appData.animateDragging = TRUE;\r
2016   appData.highlightLastMove = TRUE;\r
2017   appData.getMoveList = TRUE;\r
2018   appData.testLegality = TRUE;\r
2019   appData.premove = TRUE;\r
2020   appData.premoveWhite = FALSE;\r
2021   appData.premoveWhiteText = "";\r
2022   appData.premoveBlack = FALSE;\r
2023   appData.premoveBlackText = "";\r
2024   appData.icsAlarm = TRUE;\r
2025   appData.icsAlarmTime = 5000;\r
2026   appData.autoRaiseBoard = TRUE;\r
2027   appData.localLineEditing = TRUE;\r
2028   appData.colorize = TRUE;\r
2029   appData.reuseFirst = TRUE;\r
2030   appData.reuseSecond = TRUE;\r
2031   appData.blindfold = FALSE;\r
2032   appData.icsEngineAnalyze = FALSE;\r
2033   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2034   dcb.DCBlength = sizeof(DCB);\r
2035   dcb.BaudRate = 9600;\r
2036   dcb.fBinary = TRUE;\r
2037   dcb.fParity = FALSE;\r
2038   dcb.fOutxCtsFlow = FALSE;\r
2039   dcb.fOutxDsrFlow = FALSE;\r
2040   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2041   dcb.fDsrSensitivity = FALSE;\r
2042   dcb.fTXContinueOnXoff = TRUE;\r
2043   dcb.fOutX = FALSE;\r
2044   dcb.fInX = FALSE;\r
2045   dcb.fNull = FALSE;\r
2046   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2047   dcb.fAbortOnError = FALSE;\r
2048   dcb.ByteSize = 7;\r
2049   dcb.Parity = SPACEPARITY;\r
2050   dcb.StopBits = ONESTOPBIT;\r
2051   settingsFileName = SETTINGS_FILE;\r
2052   saveSettingsOnExit = TRUE;\r
2053   boardX = CW_USEDEFAULT;\r
2054   boardY = CW_USEDEFAULT;\r
2055   analysisX = CW_USEDEFAULT; \r
2056   analysisY = CW_USEDEFAULT; \r
2057   analysisW = CW_USEDEFAULT;\r
2058   analysisH = CW_USEDEFAULT;\r
2059   commentX = CW_USEDEFAULT; \r
2060   commentY = CW_USEDEFAULT; \r
2061   commentW = CW_USEDEFAULT;\r
2062   commentH = CW_USEDEFAULT;\r
2063   editTagsX = CW_USEDEFAULT; \r
2064   editTagsY = CW_USEDEFAULT; \r
2065   editTagsW = CW_USEDEFAULT;\r
2066   editTagsH = CW_USEDEFAULT;\r
2067   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2068   icsNames = ICS_NAMES;\r
2069   firstChessProgramNames = FCP_NAMES;\r
2070   secondChessProgramNames = SCP_NAMES;\r
2071   appData.initialMode = "";\r
2072   appData.variant = "normal";\r
2073   appData.firstProtocolVersion = PROTOVER;\r
2074   appData.secondProtocolVersion = PROTOVER;\r
2075   appData.showButtonBar = TRUE;\r
2076 \r
2077    /* [AS] New properties (see comments in header file) */\r
2078   appData.firstScoreIsAbsolute = FALSE;\r
2079   appData.secondScoreIsAbsolute = FALSE;\r
2080   appData.saveExtendedInfoInPGN = FALSE;\r
2081   appData.hideThinkingFromHuman = FALSE;\r
2082   appData.liteBackTextureFile = "";\r
2083   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2084   appData.darkBackTextureFile = "";\r
2085   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2086   appData.renderPiecesWithFont = "";\r
2087   appData.fontToPieceTable = "";\r
2088   appData.fontBackColorWhite = 0;\r
2089   appData.fontForeColorWhite = 0;\r
2090   appData.fontBackColorBlack = 0;\r
2091   appData.fontForeColorBlack = 0;\r
2092   appData.fontPieceSize = 80;\r
2093   appData.overrideLineGap = 1;\r
2094   appData.adjudicateLossThreshold = 0;\r
2095   appData.delayBeforeQuit = 0;\r
2096   appData.delayAfterQuit = 0;\r
2097   appData.nameOfDebugFile = "winboard.debug";\r
2098   appData.pgnEventHeader = "Computer Chess Game";\r
2099   appData.defaultFrcPosition = -1;\r
2100   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2101   appData.saveOutOfBookInfo = TRUE;\r
2102   appData.showEvalInMoveHistory = TRUE;\r
2103   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2104   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2105   appData.highlightMoveWithArrow = FALSE;\r
2106   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2107   appData.useStickyWindows = TRUE;\r
2108   appData.adjudicateDrawMoves = 0;\r
2109   appData.autoDisplayComment = TRUE;\r
2110   appData.autoDisplayTags = TRUE;\r
2111   appData.firstIsUCI = FALSE;\r
2112   appData.secondIsUCI = FALSE;\r
2113   appData.firstHasOwnBookUCI = TRUE;\r
2114   appData.secondHasOwnBookUCI = TRUE;\r
2115   appData.polyglotDir = "";\r
2116   appData.usePolyglotBook = FALSE;\r
2117   appData.polyglotBook = "";\r
2118   appData.defaultHashSize = 64;\r
2119   appData.defaultCacheSizeEGTB = 4;\r
2120   appData.defaultPathEGTB = "c:\\egtb";\r
2121   appData.firstOptions = "";\r
2122   appData.secondOptions = "";\r
2123 \r
2124   InitWindowPlacement( &wpGameList );\r
2125   InitWindowPlacement( &wpMoveHistory );\r
2126   InitWindowPlacement( &wpEvalGraph );\r
2127   InitWindowPlacement( &wpEngineOutput );\r
2128   InitWindowPlacement( &wpConsole );\r
2129 \r
2130   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2131   appData.NrFiles      = -1;\r
2132   appData.NrRanks      = -1;\r
2133   appData.holdingsSize = -1;\r
2134   appData.testClaims   = FALSE;\r
2135   appData.checkMates   = FALSE;\r
2136   appData.materialDraws= FALSE;\r
2137   appData.trivialDraws = FALSE;\r
2138   appData.ruleMoves    = 51;\r
2139   appData.drawRepeats  = 6;\r
2140   appData.matchPause   = 10000;\r
2141   appData.alphaRank    = FALSE;\r
2142   appData.allWhite     = FALSE;\r
2143   appData.upsideDown   = FALSE;\r
2144   appData.serverPause  = 15;\r
2145   appData.serverMovesName   = NULL;\r
2146   appData.suppressLoadMoves = FALSE;\r
2147   appData.firstTimeOdds  = 1;\r
2148   appData.secondTimeOdds = 1;\r
2149   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2150   appData.secondAccumulateTC = 1;\r
2151   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2152   appData.secondNPS = -1;\r
2153   appData.engineComments = 1;\r
2154   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2155   appData.egtFormats = "";\r
2156 \r
2157 #ifdef ZIPPY\r
2158   appData.zippyTalk = ZIPPY_TALK;\r
2159   appData.zippyPlay = ZIPPY_PLAY;\r
2160   appData.zippyLines = ZIPPY_LINES;\r
2161   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2162   appData.zippyPassword = ZIPPY_PASSWORD;\r
2163   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2164   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2165   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2166   appData.zippyUseI = ZIPPY_USE_I;\r
2167   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2168   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2169   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2170   appData.zippyGameStart = ZIPPY_GAME_START;\r
2171   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2172   appData.zippyAbort = ZIPPY_ABORT;\r
2173   appData.zippyVariants = ZIPPY_VARIANTS;\r
2174   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2175   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2176 #endif\r
2177 \r
2178   /* Point font array elements to structures and\r
2179      parse default font names */\r
2180   for (i=0; i<NUM_FONTS; i++) {\r
2181     for (j=0; j<NUM_SIZES; j++) {\r
2182       font[j][i] = &fontRec[j][i];\r
2183       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2184     }\r
2185   }\r
2186   \r
2187   /* Parse default settings file if any */\r
2188   if (ParseSettingsFile(settingsFileName, buf)) {\r
2189     settingsFileName = strdup(buf);\r
2190   }\r
2191 \r
2192   /* Parse command line */\r
2193   ParseArgs(StringGet, &lpCmdLine);\r
2194 \r
2195   /* [HGM] make sure board size is acceptable */\r
2196   if(appData.NrFiles > BOARD_SIZE ||\r
2197      appData.NrRanks > BOARD_SIZE   )\r
2198       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2199 \r
2200   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2201    * with options from the command line, we now make an even higher priority\r
2202    * overrule by WB options attached to the engine command line. This so that\r
2203    * tournament managers can use WB options (such as /timeOdds) that follow\r
2204    * the engines.\r
2205    */\r
2206   if(appData.firstChessProgram != NULL) {\r
2207       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2208       static char *f = "first";\r
2209       char buf[MSG_SIZ], *q = buf;\r
2210       if(p != NULL) { // engine command line contains WinBoard options\r
2211           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2212           ParseArgs(StringGet, &q);\r
2213           p[-1] = 0; // cut them offengine command line\r
2214       }\r
2215   }\r
2216   // now do same for second chess program\r
2217   if(appData.secondChessProgram != NULL) {\r
2218       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2219       static char *s = "second";\r
2220       char buf[MSG_SIZ], *q = buf;\r
2221       if(p != NULL) { // engine command line contains WinBoard options\r
2222           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2223           ParseArgs(StringGet, &q);\r
2224           p[-1] = 0; // cut them offengine command line\r
2225       }\r
2226   }\r
2227 \r
2228 \r
2229   /* Propagate options that affect others */\r
2230   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2231   if (appData.icsActive || appData.noChessProgram) {\r
2232      chessProgram = FALSE;  /* not local chess program mode */\r
2233   }\r
2234 \r
2235   /* Open startup dialog if needed */\r
2236   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2237       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2238       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2239                         *appData.secondChessProgram == NULLCHAR))) {\r
2240     FARPROC lpProc;\r
2241     \r
2242     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2243     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2244     FreeProcInstance(lpProc);\r
2245   }\r
2246 \r
2247   /* Make sure save files land in the right (?) directory */\r
2248   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2249     appData.saveGameFile = strdup(buf);\r
2250   }\r
2251   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2252     appData.savePositionFile = strdup(buf);\r
2253   }\r
2254 \r
2255   /* Finish initialization for fonts and sounds */\r
2256   for (i=0; i<NUM_FONTS; i++) {\r
2257     for (j=0; j<NUM_SIZES; j++) {\r
2258       CreateFontInMF(font[j][i]);\r
2259     }\r
2260   }\r
2261   /* xboard, and older WinBoards, controlled the move sound with the\r
2262      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2263      always turn the option on (so that the backend will call us),\r
2264      then let the user turn the sound off by setting it to silence if\r
2265      desired.  To accommodate old winboard.ini files saved by old\r
2266      versions of WinBoard, we also turn off the sound if the option\r
2267      was initially set to false. */\r
2268   if (!appData.ringBellAfterMoves) {\r
2269     sounds[(int)SoundMove].name = strdup("");\r
2270     appData.ringBellAfterMoves = TRUE;\r
2271   }\r
2272   GetCurrentDirectory(MSG_SIZ, currDir);\r
2273   SetCurrentDirectory(installDir);\r
2274   LoadAllSounds();\r
2275   SetCurrentDirectory(currDir);\r
2276 \r
2277   p = icsTextMenuString;\r
2278   if (p[0] == '@') {\r
2279     FILE* f = fopen(p + 1, "r");\r
2280     if (f == NULL) {\r
2281       DisplayFatalError(p + 1, errno, 2);\r
2282       return;\r
2283     }\r
2284     i = fread(buf, 1, sizeof(buf)-1, f);\r
2285     fclose(f);\r
2286     buf[i] = NULLCHAR;\r
2287     p = buf;\r
2288   }\r
2289   ParseIcsTextMenu(strdup(p));\r
2290 }\r
2291 \r
2292 \r
2293 VOID\r
2294 InitMenuChecks()\r
2295 {\r
2296   HMENU hmenu = GetMenu(hwndMain);\r
2297 \r
2298   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2299                         MF_BYCOMMAND|((appData.icsActive &&\r
2300                                        *appData.icsCommPort != NULLCHAR) ?\r
2301                                       MF_ENABLED : MF_GRAYED));\r
2302   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2303                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2304                                      MF_CHECKED : MF_UNCHECKED));\r
2305 }\r
2306 \r
2307 \r
2308 VOID\r
2309 SaveSettings(char* name)\r
2310 {\r
2311   FILE *f;\r
2312   ArgDescriptor *ad;\r
2313   WINDOWPLACEMENT wp;\r
2314   char dir[MSG_SIZ];\r
2315 \r
2316   if (!hwndMain) return;\r
2317 \r
2318   GetCurrentDirectory(MSG_SIZ, dir);\r
2319   SetCurrentDirectory(installDir);\r
2320   f = fopen(name, "w");\r
2321   SetCurrentDirectory(dir);\r
2322   if (f == NULL) {\r
2323     DisplayError(name, errno);\r
2324     return;\r
2325   }\r
2326   fprintf(f, ";\n");\r
2327   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2328   fprintf(f, ";\n");\r
2329   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2330   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2331   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2332   fprintf(f, ";\n");\r
2333 \r
2334   wp.length = sizeof(WINDOWPLACEMENT);\r
2335   GetWindowPlacement(hwndMain, &wp);\r
2336   boardX = wp.rcNormalPosition.left;\r
2337   boardY = wp.rcNormalPosition.top;\r
2338 \r
2339   if (hwndConsole) {\r
2340     GetWindowPlacement(hwndConsole, &wp);\r
2341     wpConsole.x = wp.rcNormalPosition.left;\r
2342     wpConsole.y = wp.rcNormalPosition.top;\r
2343     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2344     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2345   }\r
2346 \r
2347   if (analysisDialog) {\r
2348     GetWindowPlacement(analysisDialog, &wp);\r
2349     analysisX = wp.rcNormalPosition.left;\r
2350     analysisY = wp.rcNormalPosition.top;\r
2351     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2352     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2353   }\r
2354 \r
2355   if (commentDialog) {\r
2356     GetWindowPlacement(commentDialog, &wp);\r
2357     commentX = wp.rcNormalPosition.left;\r
2358     commentY = wp.rcNormalPosition.top;\r
2359     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2360     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2361   }\r
2362 \r
2363   if (editTagsDialog) {\r
2364     GetWindowPlacement(editTagsDialog, &wp);\r
2365     editTagsX = wp.rcNormalPosition.left;\r
2366     editTagsY = wp.rcNormalPosition.top;\r
2367     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2368     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2369   }\r
2370 \r
2371   if (gameListDialog) {\r
2372     GetWindowPlacement(gameListDialog, &wp);\r
2373     wpGameList.x = wp.rcNormalPosition.left;\r
2374     wpGameList.y = wp.rcNormalPosition.top;\r
2375     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2376     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2377   }\r
2378 \r
2379   /* [AS] Move history */\r
2380   wpMoveHistory.visible = MoveHistoryIsUp();\r
2381   \r
2382   if( moveHistoryDialog ) {\r
2383     GetWindowPlacement(moveHistoryDialog, &wp);\r
2384     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2385     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2386     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2387     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2388   }\r
2389 \r
2390   /* [AS] Eval graph */\r
2391   wpEvalGraph.visible = EvalGraphIsUp();\r
2392 \r
2393   if( evalGraphDialog ) {\r
2394     GetWindowPlacement(evalGraphDialog, &wp);\r
2395     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2396     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2397     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2398     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2399   }\r
2400 \r
2401   /* [AS] Engine output */\r
2402   wpEngineOutput.visible = EngineOutputIsUp();\r
2403 \r
2404   if( engineOutputDialog ) {\r
2405     GetWindowPlacement(engineOutputDialog, &wp);\r
2406     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2407     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2408     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2409     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2410   }\r
2411 \r
2412   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2413     if (!ad->save) continue;\r
2414     switch (ad->argType) {\r
2415     case ArgString:\r
2416       {\r
2417         char *p = *(char **)ad->argLoc;\r
2418         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2419           /* Quote multiline values or \-containing values\r
2420              with { } if possible */\r
2421           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2422         } else {\r
2423           /* Else quote with " " */\r
2424           fprintf(f, "/%s=\"", ad->argName);\r
2425           while (*p) {\r
2426             if (*p == '\n') fprintf(f, "\n");\r
2427             else if (*p == '\r') fprintf(f, "\\r");\r
2428             else if (*p == '\t') fprintf(f, "\\t");\r
2429             else if (*p == '\b') fprintf(f, "\\b");\r
2430             else if (*p == '\f') fprintf(f, "\\f");\r
2431             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2432             else if (*p == '\"') fprintf(f, "\\\"");\r
2433             else if (*p == '\\') fprintf(f, "\\\\");\r
2434             else putc(*p, f);\r
2435             p++;\r
2436           }\r
2437           fprintf(f, "\"\n");\r
2438         }\r
2439       }\r
2440       break;\r
2441     case ArgInt:\r
2442     case ArgZ:\r
2443       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2444       break;\r
2445     case ArgX:\r
2446       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2447       break;\r
2448     case ArgY:\r
2449       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2450       break;\r
2451     case ArgFloat:\r
2452       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2453       break;\r
2454     case ArgBoolean:\r
2455       fprintf(f, "/%s=%s\n", ad->argName, \r
2456         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2457       break;\r
2458     case ArgTrue:\r
2459       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2460       break;\r
2461     case ArgFalse:\r
2462       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2463       break;\r
2464     case ArgColor:\r
2465       {\r
2466         COLORREF color = *(COLORREF *)ad->argLoc;\r
2467         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2468           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2469       }\r
2470       break;\r
2471     case ArgAttribs:\r
2472       {\r
2473         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2474         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2475           (ta->effects & CFE_BOLD) ? "b" : "",\r
2476           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2477           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2478           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2479           (ta->effects) ? " " : "",\r
2480           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2481       }\r
2482       break;\r
2483     case ArgFilename:\r
2484       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2485         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2486       } else {\r
2487         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2488       }\r
2489       break;\r
2490     case ArgBoardSize:\r
2491       fprintf(f, "/%s=%s\n", ad->argName,\r
2492               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2493       break;\r
2494     case ArgFont:\r
2495       {\r
2496         int bs;\r
2497         for (bs=0; bs<NUM_SIZES; bs++) {\r
2498           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2499           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2500           fprintf(f, "/%s=\"%s:%g%s%s%s%s%sc%d\"\n",\r
2501             ad->argName, mfp->faceName, mfp->pointSize,\r
2502             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2503             mfp->bold ? "b" : "",\r
2504             mfp->italic ? "i" : "",\r
2505             mfp->underline ? "u" : "",\r
2506             mfp->strikeout ? "s" : "",\r
2507             (int)mfp->charset);\r
2508         }\r
2509       }\r
2510       break;\r
2511     case ArgCommSettings:\r
2512       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2513     case ArgNone:\r
2514     case ArgSettingsFilename: ;\r
2515     }\r
2516   }\r
2517   fclose(f);\r
2518 }\r
2519 \r
2520 \r
2521 \r
2522 /*---------------------------------------------------------------------------*\\r
2523  *\r
2524  * GDI board drawing routines\r
2525  *\r
2526 \*---------------------------------------------------------------------------*/\r
2527 \r
2528 /* [AS] Draw square using background texture */\r
2529 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2530 {\r
2531     XFORM   x;\r
2532 \r
2533     if( mode == 0 ) {\r
2534         return; /* Should never happen! */\r
2535     }\r
2536 \r
2537     SetGraphicsMode( dst, GM_ADVANCED );\r
2538 \r
2539     switch( mode ) {\r
2540     case 1:\r
2541         /* Identity */\r
2542         break;\r
2543     case 2:\r
2544         /* X reflection */\r
2545         x.eM11 = -1.0;\r
2546         x.eM12 = 0;\r
2547         x.eM21 = 0;\r
2548         x.eM22 = 1.0;\r
2549         x.eDx = (FLOAT) dw + dx - 1;\r
2550         x.eDy = 0;\r
2551         dx = 0;\r
2552         SetWorldTransform( dst, &x );\r
2553         break;\r
2554     case 3:\r
2555         /* Y reflection */\r
2556         x.eM11 = 1.0;\r
2557         x.eM12 = 0;\r
2558         x.eM21 = 0;\r
2559         x.eM22 = -1.0;\r
2560         x.eDx = 0;\r
2561         x.eDy = (FLOAT) dh + dy - 1;\r
2562         dy = 0;\r
2563         SetWorldTransform( dst, &x );\r
2564         break;\r
2565     case 4:\r
2566         /* X/Y flip */\r
2567         x.eM11 = 0;\r
2568         x.eM12 = 1.0;\r
2569         x.eM21 = 1.0;\r
2570         x.eM22 = 0;\r
2571         x.eDx = (FLOAT) dx;\r
2572         x.eDy = (FLOAT) dy;\r
2573         dx = 0;\r
2574         dy = 0;\r
2575         SetWorldTransform( dst, &x );\r
2576         break;\r
2577     }\r
2578 \r
2579     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2580 \r
2581     x.eM11 = 1.0;\r
2582     x.eM12 = 0;\r
2583     x.eM21 = 0;\r
2584     x.eM22 = 1.0;\r
2585     x.eDx = 0;\r
2586     x.eDy = 0;\r
2587     SetWorldTransform( dst, &x );\r
2588 \r
2589     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2590 }\r
2591 \r
2592 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2593 enum {\r
2594     PM_WP = (int) WhitePawn, \r
2595     PM_WN = (int) WhiteKnight, \r
2596     PM_WB = (int) WhiteBishop, \r
2597     PM_WR = (int) WhiteRook, \r
2598     PM_WQ = (int) WhiteQueen, \r
2599     PM_WF = (int) WhiteFerz, \r
2600     PM_WW = (int) WhiteWazir, \r
2601     PM_WE = (int) WhiteAlfil, \r
2602     PM_WM = (int) WhiteMan, \r
2603     PM_WO = (int) WhiteCannon, \r
2604     PM_WU = (int) WhiteUnicorn, \r
2605     PM_WH = (int) WhiteNightrider, \r
2606     PM_WA = (int) WhiteAngel, \r
2607     PM_WC = (int) WhiteMarshall, \r
2608     PM_WAB = (int) WhiteCardinal, \r
2609     PM_WD = (int) WhiteDragon, \r
2610     PM_WL = (int) WhiteLance, \r
2611     PM_WS = (int) WhiteCobra, \r
2612     PM_WV = (int) WhiteFalcon, \r
2613     PM_WSG = (int) WhiteSilver, \r
2614     PM_WG = (int) WhiteGrasshopper, \r
2615     PM_WK = (int) WhiteKing,\r
2616     PM_BP = (int) BlackPawn, \r
2617     PM_BN = (int) BlackKnight, \r
2618     PM_BB = (int) BlackBishop, \r
2619     PM_BR = (int) BlackRook, \r
2620     PM_BQ = (int) BlackQueen, \r
2621     PM_BF = (int) BlackFerz, \r
2622     PM_BW = (int) BlackWazir, \r
2623     PM_BE = (int) BlackAlfil, \r
2624     PM_BM = (int) BlackMan,\r
2625     PM_BO = (int) BlackCannon, \r
2626     PM_BU = (int) BlackUnicorn, \r
2627     PM_BH = (int) BlackNightrider, \r
2628     PM_BA = (int) BlackAngel, \r
2629     PM_BC = (int) BlackMarshall, \r
2630     PM_BG = (int) BlackGrasshopper, \r
2631     PM_BAB = (int) BlackCardinal,\r
2632     PM_BD = (int) BlackDragon,\r
2633     PM_BL = (int) BlackLance,\r
2634     PM_BS = (int) BlackCobra,\r
2635     PM_BV = (int) BlackFalcon,\r
2636     PM_BSG = (int) BlackSilver,\r
2637     PM_BK = (int) BlackKing\r
2638 };\r
2639 \r
2640 static HFONT hPieceFont = NULL;\r
2641 static HBITMAP hPieceMask[(int) EmptySquare];\r
2642 static HBITMAP hPieceFace[(int) EmptySquare];\r
2643 static int fontBitmapSquareSize = 0;\r
2644 static char pieceToFontChar[(int) EmptySquare] =\r
2645                               { 'p', 'n', 'b', 'r', 'q', \r
2646                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2647                       'k', 'o', 'm', 'v', 't', 'w', \r
2648                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2649                                                               'l' };\r
2650 \r
2651 extern BOOL SetCharTable( char *table, const char * map );\r
2652 /* [HGM] moved to backend.c */\r
2653 \r
2654 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2655 {\r
2656     HBRUSH hbrush;\r
2657     BYTE r1 = GetRValue( color );\r
2658     BYTE g1 = GetGValue( color );\r
2659     BYTE b1 = GetBValue( color );\r
2660     BYTE r2 = r1 / 2;\r
2661     BYTE g2 = g1 / 2;\r
2662     BYTE b2 = b1 / 2;\r
2663     RECT rc;\r
2664 \r
2665     /* Create a uniform background first */\r
2666     hbrush = CreateSolidBrush( color );\r
2667     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2668     FillRect( hdc, &rc, hbrush );\r
2669     DeleteObject( hbrush );\r
2670     \r
2671     if( mode == 1 ) {\r
2672         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2673         int steps = squareSize / 2;\r
2674         int i;\r
2675 \r
2676         for( i=0; i<steps; i++ ) {\r
2677             BYTE r = r1 - (r1-r2) * i / steps;\r
2678             BYTE g = g1 - (g1-g2) * i / steps;\r
2679             BYTE b = b1 - (b1-b2) * i / steps;\r
2680 \r
2681             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2682             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2683             FillRect( hdc, &rc, hbrush );\r
2684             DeleteObject(hbrush);\r
2685         }\r
2686     }\r
2687     else if( mode == 2 ) {\r
2688         /* Diagonal gradient, good more or less for every piece */\r
2689         POINT triangle[3];\r
2690         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2691         HBRUSH hbrush_old;\r
2692         int steps = squareSize;\r
2693         int i;\r
2694 \r
2695         triangle[0].x = squareSize - steps;\r
2696         triangle[0].y = squareSize;\r
2697         triangle[1].x = squareSize;\r
2698         triangle[1].y = squareSize;\r
2699         triangle[2].x = squareSize;\r
2700         triangle[2].y = squareSize - steps;\r
2701 \r
2702         for( i=0; i<steps; i++ ) {\r
2703             BYTE r = r1 - (r1-r2) * i / steps;\r
2704             BYTE g = g1 - (g1-g2) * i / steps;\r
2705             BYTE b = b1 - (b1-b2) * i / steps;\r
2706 \r
2707             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2708             hbrush_old = SelectObject( hdc, hbrush );\r
2709             Polygon( hdc, triangle, 3 );\r
2710             SelectObject( hdc, hbrush_old );\r
2711             DeleteObject(hbrush);\r
2712             triangle[0].x++;\r
2713             triangle[2].y++;\r
2714         }\r
2715 \r
2716         SelectObject( hdc, hpen );\r
2717     }\r
2718 }\r
2719 \r
2720 /*\r
2721     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2722     seems to work ok. The main problem here is to find the "inside" of a chess\r
2723     piece: follow the steps as explained below.\r
2724 */\r
2725 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2726 {\r
2727     HBITMAP hbm;\r
2728     HBITMAP hbm_old;\r
2729     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2730     RECT rc;\r
2731     SIZE sz;\r
2732     POINT pt;\r
2733     int backColor = whitePieceColor; \r
2734     int foreColor = blackPieceColor;\r
2735     \r
2736     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2737         backColor = appData.fontBackColorWhite;\r
2738         foreColor = appData.fontForeColorWhite;\r
2739     }\r
2740     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2741         backColor = appData.fontBackColorBlack;\r
2742         foreColor = appData.fontForeColorBlack;\r
2743     }\r
2744 \r
2745     /* Mask */\r
2746     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2747 \r
2748     hbm_old = SelectObject( hdc, hbm );\r
2749 \r
2750     rc.left = 0;\r
2751     rc.top = 0;\r
2752     rc.right = squareSize;\r
2753     rc.bottom = squareSize;\r
2754 \r
2755     /* Step 1: background is now black */\r
2756     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2757 \r
2758     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2759 \r
2760     pt.x = (squareSize - sz.cx) / 2;\r
2761     pt.y = (squareSize - sz.cy) / 2;\r
2762 \r
2763     SetBkMode( hdc, TRANSPARENT );\r
2764     SetTextColor( hdc, chroma );\r
2765     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2766     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2767 \r
2768     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2769     /* Step 3: the area outside the piece is filled with white */\r
2770 //    FloodFill( hdc, 0, 0, chroma );\r
2771     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2772     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2773     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2774     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2775     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2776     /* \r
2777         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2778         but if the start point is not inside the piece we're lost!\r
2779         There should be a better way to do this... if we could create a region or path\r
2780         from the fill operation we would be fine for example.\r
2781     */\r
2782 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2783     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2784 \r
2785     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2786         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2787         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2788 \r
2789         SelectObject( dc2, bm2 );\r
2790         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2791         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2792         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2793         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2794         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2795 \r
2796         DeleteDC( dc2 );\r
2797         DeleteObject( bm2 );\r
2798     }\r
2799 \r
2800     SetTextColor( hdc, 0 );\r
2801     /* \r
2802         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2803         draw the piece again in black for safety.\r
2804     */\r
2805     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2806 \r
2807     SelectObject( hdc, hbm_old );\r
2808 \r
2809     if( hPieceMask[index] != NULL ) {\r
2810         DeleteObject( hPieceMask[index] );\r
2811     }\r
2812 \r
2813     hPieceMask[index] = hbm;\r
2814 \r
2815     /* Face */\r
2816     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2817 \r
2818     SelectObject( hdc, hbm );\r
2819 \r
2820     {\r
2821         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2822         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2823         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2824 \r
2825         SelectObject( dc1, hPieceMask[index] );\r
2826         SelectObject( dc2, bm2 );\r
2827         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2828         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2829         \r
2830         /* \r
2831             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2832             the piece background and deletes (makes transparent) the rest.\r
2833             Thanks to that mask, we are free to paint the background with the greates\r
2834             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2835             We use this, to make gradients and give the pieces a "roundish" look.\r
2836         */\r
2837         SetPieceBackground( hdc, backColor, 2 );\r
2838         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2839 \r
2840         DeleteDC( dc2 );\r
2841         DeleteDC( dc1 );\r
2842         DeleteObject( bm2 );\r
2843     }\r
2844 \r
2845     SetTextColor( hdc, foreColor );\r
2846     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2847 \r
2848     SelectObject( hdc, hbm_old );\r
2849 \r
2850     if( hPieceFace[index] != NULL ) {\r
2851         DeleteObject( hPieceFace[index] );\r
2852     }\r
2853 \r
2854     hPieceFace[index] = hbm;\r
2855 }\r
2856 \r
2857 static int TranslatePieceToFontPiece( int piece )\r
2858 {\r
2859     switch( piece ) {\r
2860     case BlackPawn:\r
2861         return PM_BP;\r
2862     case BlackKnight:\r
2863         return PM_BN;\r
2864     case BlackBishop:\r
2865         return PM_BB;\r
2866     case BlackRook:\r
2867         return PM_BR;\r
2868     case BlackQueen:\r
2869         return PM_BQ;\r
2870     case BlackKing:\r
2871         return PM_BK;\r
2872     case WhitePawn:\r
2873         return PM_WP;\r
2874     case WhiteKnight:\r
2875         return PM_WN;\r
2876     case WhiteBishop:\r
2877         return PM_WB;\r
2878     case WhiteRook:\r
2879         return PM_WR;\r
2880     case WhiteQueen:\r
2881         return PM_WQ;\r
2882     case WhiteKing:\r
2883         return PM_WK;\r
2884 \r
2885     case BlackAngel:\r
2886         return PM_BA;\r
2887     case BlackMarshall:\r
2888         return PM_BC;\r
2889     case BlackFerz:\r
2890         return PM_BF;\r
2891     case BlackNightrider:\r
2892         return PM_BH;\r
2893     case BlackAlfil:\r
2894         return PM_BE;\r
2895     case BlackWazir:\r
2896         return PM_BW;\r
2897     case BlackUnicorn:\r
2898         return PM_BU;\r
2899     case BlackCannon:\r
2900         return PM_BO;\r
2901     case BlackGrasshopper:\r
2902         return PM_BG;\r
2903     case BlackMan:\r
2904         return PM_BM;\r
2905     case BlackSilver:\r
2906         return PM_BSG;\r
2907     case BlackLance:\r
2908         return PM_BL;\r
2909     case BlackFalcon:\r
2910         return PM_BV;\r
2911     case BlackCobra:\r
2912         return PM_BS;\r
2913     case BlackCardinal:\r
2914         return PM_BAB;\r
2915     case BlackDragon:\r
2916         return PM_BD;\r
2917 \r
2918     case WhiteAngel:\r
2919         return PM_WA;\r
2920     case WhiteMarshall:\r
2921         return PM_WC;\r
2922     case WhiteFerz:\r
2923         return PM_WF;\r
2924     case WhiteNightrider:\r
2925         return PM_WH;\r
2926     case WhiteAlfil:\r
2927         return PM_WE;\r
2928     case WhiteWazir:\r
2929         return PM_WW;\r
2930     case WhiteUnicorn:\r
2931         return PM_WU;\r
2932     case WhiteCannon:\r
2933         return PM_WO;\r
2934     case WhiteGrasshopper:\r
2935         return PM_WG;\r
2936     case WhiteMan:\r
2937         return PM_WM;\r
2938     case WhiteSilver:\r
2939         return PM_WSG;\r
2940     case WhiteLance:\r
2941         return PM_WL;\r
2942     case WhiteFalcon:\r
2943         return PM_WV;\r
2944     case WhiteCobra:\r
2945         return PM_WS;\r
2946     case WhiteCardinal:\r
2947         return PM_WAB;\r
2948     case WhiteDragon:\r
2949         return PM_WD;\r
2950     }\r
2951 \r
2952     return 0;\r
2953 }\r
2954 \r
2955 void CreatePiecesFromFont()\r
2956 {\r
2957     LOGFONT lf;\r
2958     HDC hdc_window = NULL;\r
2959     HDC hdc = NULL;\r
2960     HFONT hfont_old;\r
2961     int fontHeight;\r
2962     int i;\r
2963 \r
2964     if( fontBitmapSquareSize < 0 ) {\r
2965         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2966         return;\r
2967     }\r
2968 \r
2969     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2970         fontBitmapSquareSize = -1;\r
2971         return;\r
2972     }\r
2973 \r
2974     if( fontBitmapSquareSize != squareSize ) {\r
2975         hdc_window = GetDC( hwndMain );\r
2976         hdc = CreateCompatibleDC( hdc_window );\r
2977 \r
2978         if( hPieceFont != NULL ) {\r
2979             DeleteObject( hPieceFont );\r
2980         }\r
2981         else {\r
2982             for( i=0; i<=(int)BlackKing; i++ ) {\r
2983                 hPieceMask[i] = NULL;\r
2984                 hPieceFace[i] = NULL;\r
2985             }\r
2986         }\r
2987 \r
2988         fontHeight = 75;\r
2989 \r
2990         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2991             fontHeight = appData.fontPieceSize;\r
2992         }\r
2993 \r
2994         fontHeight = (fontHeight * squareSize) / 100;\r
2995 \r
2996         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2997         lf.lfWidth = 0;\r
2998         lf.lfEscapement = 0;\r
2999         lf.lfOrientation = 0;\r
3000         lf.lfWeight = FW_NORMAL;\r
3001         lf.lfItalic = 0;\r
3002         lf.lfUnderline = 0;\r
3003         lf.lfStrikeOut = 0;\r
3004         lf.lfCharSet = DEFAULT_CHARSET;\r
3005         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
3006         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
3007         lf.lfQuality = PROOF_QUALITY;\r
3008         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
3009         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
3010         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
3011 \r
3012         hPieceFont = CreateFontIndirect( &lf );\r
3013 \r
3014         if( hPieceFont == NULL ) {\r
3015             fontBitmapSquareSize = -2;\r
3016         }\r
3017         else {\r
3018             /* Setup font-to-piece character table */\r
3019             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
3020                 /* No (or wrong) global settings, try to detect the font */\r
3021                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
3022                     /* Alpha */\r
3023                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
3024                 }\r
3025                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3026                     /* DiagramTT* family */\r
3027                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3028                 }\r
3029                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3030                     /* Fairy symbols */\r
3031                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3032                 }\r
3033                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3034                     /* Good Companion (Some characters get warped as literal :-( */\r
3035                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3036                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3037                     SetCharTable(pieceToFontChar, s);\r
3038                 }\r
3039                 else {\r
3040                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3041                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3042                 }\r
3043             }\r
3044 \r
3045             /* Create bitmaps */\r
3046             hfont_old = SelectObject( hdc, hPieceFont );\r
3047             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3048                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3049                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3050 \r
3051             SelectObject( hdc, hfont_old );\r
3052 \r
3053             fontBitmapSquareSize = squareSize;\r
3054         }\r
3055     }\r
3056 \r
3057     if( hdc != NULL ) {\r
3058         DeleteDC( hdc );\r
3059     }\r
3060 \r
3061     if( hdc_window != NULL ) {\r
3062         ReleaseDC( hwndMain, hdc_window );\r
3063     }\r
3064 }\r
3065 \r
3066 HBITMAP\r
3067 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3068 {\r
3069   char name[128];\r
3070 \r
3071   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3072   if (gameInfo.event &&\r
3073       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3074       strcmp(name, "k80s") == 0) {\r
3075     strcpy(name, "tim");\r
3076   }\r
3077   return LoadBitmap(hinst, name);\r
3078 }\r
3079 \r
3080 \r
3081 /* Insert a color into the program's logical palette\r
3082    structure.  This code assumes the given color is\r
3083    the result of the RGB or PALETTERGB macro, and it\r
3084    knows how those macros work (which is documented).\r
3085 */\r
3086 VOID\r
3087 InsertInPalette(COLORREF color)\r
3088 {\r
3089   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3090 \r
3091   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3092     DisplayFatalError("Too many colors", 0, 1);\r
3093     pLogPal->palNumEntries--;\r
3094     return;\r
3095   }\r
3096 \r
3097   pe->peFlags = (char) 0;\r
3098   pe->peRed = (char) (0xFF & color);\r
3099   pe->peGreen = (char) (0xFF & (color >> 8));\r
3100   pe->peBlue = (char) (0xFF & (color >> 16));\r
3101   return;\r
3102 }\r
3103 \r
3104 \r
3105 VOID\r
3106 InitDrawingColors()\r
3107 {\r
3108   if (pLogPal == NULL) {\r
3109     /* Allocate enough memory for a logical palette with\r
3110      * PALETTESIZE entries and set the size and version fields\r
3111      * of the logical palette structure.\r
3112      */\r
3113     pLogPal = (NPLOGPALETTE)\r
3114       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3115                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3116     pLogPal->palVersion    = 0x300;\r
3117   }\r
3118   pLogPal->palNumEntries = 0;\r
3119 \r
3120   InsertInPalette(lightSquareColor);\r
3121   InsertInPalette(darkSquareColor);\r
3122   InsertInPalette(whitePieceColor);\r
3123   InsertInPalette(blackPieceColor);\r
3124   InsertInPalette(highlightSquareColor);\r
3125   InsertInPalette(premoveHighlightColor);\r
3126 \r
3127   /*  create a logical color palette according the information\r
3128    *  in the LOGPALETTE structure.\r
3129    */\r
3130   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3131 \r
3132   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3133   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3134   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3135   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3136   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3137   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3138   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3139   /* [AS] Force rendering of the font-based pieces */\r
3140   if( fontBitmapSquareSize > 0 ) {\r
3141     fontBitmapSquareSize = 0;\r
3142   }\r
3143 }\r
3144 \r
3145 \r
3146 int\r
3147 BoardWidth(int boardSize, int n)\r
3148 { /* [HGM] argument n added to allow different width and height */\r
3149   int lineGap = sizeInfo[boardSize].lineGap;\r
3150 \r
3151   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3152       lineGap = appData.overrideLineGap;\r
3153   }\r
3154 \r
3155   return (n + 1) * lineGap +\r
3156           n * sizeInfo[boardSize].squareSize;\r
3157 }\r
3158 \r
3159 /* Respond to board resize by dragging edge */\r
3160 VOID\r
3161 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3162 {\r
3163   BoardSize newSize = NUM_SIZES - 1;\r
3164   static int recurse = 0;\r
3165   if (IsIconic(hwndMain)) return;\r
3166   if (recurse > 0) return;\r
3167   recurse++;\r
3168   while (newSize > 0) {\r
3169         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3170         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3171            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3172     newSize--;\r
3173   } \r
3174   boardSize = newSize;\r
3175   InitDrawingSizes(boardSize, flags);\r
3176   recurse--;\r
3177 }\r
3178 \r
3179 \r
3180 \r
3181 VOID\r
3182 InitDrawingSizes(BoardSize boardSize, int flags)\r
3183 {\r
3184   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3185   ChessSquare piece;\r
3186   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3187   HDC hdc;\r
3188   SIZE clockSize, messageSize;\r
3189   HFONT oldFont;\r
3190   char buf[MSG_SIZ];\r
3191   char *str;\r
3192   HMENU hmenu = GetMenu(hwndMain);\r
3193   RECT crect, wrect, oldRect;\r
3194   int offby;\r
3195   LOGBRUSH logbrush;\r
3196 \r
3197   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3198   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3199 \r
3200   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3201   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3202 \r
3203   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3204   oldRect.top = boardY;\r
3205   oldRect.right = boardX + winWidth;\r
3206   oldRect.bottom = boardY + winHeight;\r
3207 \r
3208   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3209   smallLayout = sizeInfo[boardSize].smallLayout;\r
3210   squareSize = sizeInfo[boardSize].squareSize;\r
3211   lineGap = sizeInfo[boardSize].lineGap;\r
3212   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3213 \r
3214   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3215       lineGap = appData.overrideLineGap;\r
3216   }\r
3217 \r
3218   if (tinyLayout != oldTinyLayout) {\r
3219     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3220     if (tinyLayout) {\r
3221       style &= ~WS_SYSMENU;\r
3222       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3223                  "&Minimize\tCtrl+F4");\r
3224     } else {\r
3225       style |= WS_SYSMENU;\r
3226       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3227     }\r
3228     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3229 \r
3230     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3231       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3232         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3233     }\r
3234     DrawMenuBar(hwndMain);\r
3235   }\r
3236 \r
3237   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3238   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3239 \r
3240   /* Get text area sizes */\r
3241   hdc = GetDC(hwndMain);\r
3242   if (appData.clockMode) {\r
3243     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3244   } else {\r
3245     sprintf(buf, "White");\r
3246   }\r
3247   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3248   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3249   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3250   str = "We only care about the height here";\r
3251   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3252   SelectObject(hdc, oldFont);\r
3253   ReleaseDC(hwndMain, hdc);\r
3254 \r
3255   /* Compute where everything goes */\r
3256   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3257         /* [HGM] logo: if either logo is on, reserve space for it */\r
3258         logoHeight =  2*clockSize.cy;\r
3259         leftLogoRect.left   = OUTER_MARGIN;\r
3260         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3261         leftLogoRect.top    = OUTER_MARGIN;\r
3262         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3263 \r
3264         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3265         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3266         rightLogoRect.top    = OUTER_MARGIN;\r
3267         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3268 \r
3269 \r
3270     whiteRect.left = leftLogoRect.right;\r
3271     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3272     whiteRect.top = OUTER_MARGIN;\r
3273     whiteRect.bottom = whiteRect.top + logoHeight;\r
3274 \r
3275     blackRect.right = rightLogoRect.left;\r
3276     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3277     blackRect.top = whiteRect.top;\r
3278     blackRect.bottom = whiteRect.bottom;\r
3279   } else {\r
3280     whiteRect.left = OUTER_MARGIN;\r
3281     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3282     whiteRect.top = OUTER_MARGIN;\r
3283     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3284 \r
3285     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3286     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3287     blackRect.top = whiteRect.top;\r
3288     blackRect.bottom = whiteRect.bottom;\r
3289   }\r
3290 \r
3291   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3292   if (appData.showButtonBar) {\r
3293     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3294       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3295   } else {\r
3296     messageRect.right = OUTER_MARGIN + boardWidth;\r
3297   }\r
3298   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3299   messageRect.bottom = messageRect.top + messageSize.cy;\r
3300 \r
3301   boardRect.left = OUTER_MARGIN;\r
3302   boardRect.right = boardRect.left + boardWidth;\r
3303   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3304   boardRect.bottom = boardRect.top + boardHeight;\r
3305 \r
3306   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3307   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3308   oldBoardSize = boardSize;\r
3309   oldTinyLayout = tinyLayout;\r
3310   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3311   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3312     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3313   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3314   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3315   winHeight = winH; //       without disturbing window attachments\r
3316   GetWindowRect(hwndMain, &wrect);\r
3317   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3318                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3319 \r
3320   // [HGM] placement: let attached windows follow size change.\r
3321   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3322   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3323   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3324   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3325   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3326 \r
3327   /* compensate if menu bar wrapped */\r
3328   GetClientRect(hwndMain, &crect);\r
3329   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3330   winHeight += offby;\r
3331   switch (flags) {\r
3332   case WMSZ_TOPLEFT:\r
3333     SetWindowPos(hwndMain, NULL, \r
3334                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3335                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3336     break;\r
3337 \r
3338   case WMSZ_TOPRIGHT:\r
3339   case WMSZ_TOP:\r
3340     SetWindowPos(hwndMain, NULL, \r
3341                  wrect.left, wrect.bottom - winHeight, \r
3342                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3343     break;\r
3344 \r
3345   case WMSZ_BOTTOMLEFT:\r
3346   case WMSZ_LEFT:\r
3347     SetWindowPos(hwndMain, NULL, \r
3348                  wrect.right - winWidth, wrect.top, \r
3349                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3350     break;\r
3351 \r
3352   case WMSZ_BOTTOMRIGHT:\r
3353   case WMSZ_BOTTOM:\r
3354   case WMSZ_RIGHT:\r
3355   default:\r
3356     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3357                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3358     break;\r
3359   }\r
3360 \r
3361   hwndPause = NULL;\r
3362   for (i = 0; i < N_BUTTONS; i++) {\r
3363     if (buttonDesc[i].hwnd != NULL) {\r
3364       DestroyWindow(buttonDesc[i].hwnd);\r
3365       buttonDesc[i].hwnd = NULL;\r
3366     }\r
3367     if (appData.showButtonBar) {\r
3368       buttonDesc[i].hwnd =\r
3369         CreateWindow("BUTTON", buttonDesc[i].label,\r
3370                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3371                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3372                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3373                      (HMENU) buttonDesc[i].id,\r
3374                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3375       if (tinyLayout) {\r
3376         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3377                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3378                     MAKELPARAM(FALSE, 0));\r
3379       }\r
3380       if (buttonDesc[i].id == IDM_Pause)\r
3381         hwndPause = buttonDesc[i].hwnd;\r
3382       buttonDesc[i].wndproc = (WNDPROC)\r
3383         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3384     }\r
3385   }\r
3386   if (gridPen != NULL) DeleteObject(gridPen);\r
3387   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3388   if (premovePen != NULL) DeleteObject(premovePen);\r
3389   if (lineGap != 0) {\r
3390     logbrush.lbStyle = BS_SOLID;\r
3391     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3392     gridPen =\r
3393       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3394                    lineGap, &logbrush, 0, NULL);\r
3395     logbrush.lbColor = highlightSquareColor;\r
3396     highlightPen =\r
3397       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3398                    lineGap, &logbrush, 0, NULL);\r
3399 \r
3400     logbrush.lbColor = premoveHighlightColor; \r
3401     premovePen =\r
3402       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3403                    lineGap, &logbrush, 0, NULL);\r
3404 \r
3405     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3406     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3407       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3408       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3409         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3410       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3411         BOARD_WIDTH * (squareSize + lineGap);\r
3412       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3413     }\r
3414     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3415       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3416       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3417         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3418         lineGap / 2 + (i * (squareSize + lineGap));\r
3419       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3420         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3421       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3422     }\r
3423   }\r
3424 \r
3425   /* [HGM] Licensing requirement */\r
3426 #ifdef GOTHIC\r
3427   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3428 #endif\r
3429 #ifdef FALCON\r
3430   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3431 #endif\r
3432   GothicPopUp( "", VariantNormal);\r
3433 \r
3434 \r
3435 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3436 \r
3437   /* Load piece bitmaps for this board size */\r
3438   for (i=0; i<=2; i++) {\r
3439     for (piece = WhitePawn;\r
3440          (int) piece < (int) BlackPawn;\r
3441          piece = (ChessSquare) ((int) piece + 1)) {\r
3442       if (pieceBitmap[i][piece] != NULL)\r
3443         DeleteObject(pieceBitmap[i][piece]);\r
3444     }\r
3445   }\r
3446 \r
3447   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3448   // Orthodox Chess pieces\r
3449   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3450   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3451   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3452   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3453   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3454   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3455   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3456   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3457   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3458   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3459   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3460   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3461   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3462   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3463   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3464   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3465     // in Shogi, Hijack the unused Queen for Lance\r
3466     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3467     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3468     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3469   } else {\r
3470     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3471     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3472     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3473   }\r
3474 \r
3475   if(squareSize <= 72 && squareSize >= 33) { \r
3476     /* A & C are available in most sizes now */\r
3477     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3478       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3479       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3480       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3481       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3482       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3483       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3484       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3485       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3486       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3487       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3488       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3489       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3490     } else { // Smirf-like\r
3491       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3492       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3493       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3494     }\r
3495     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3496       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3497       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3498       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3499     } else { // WinBoard standard\r
3500       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3501       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3502       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3503     }\r
3504   }\r
3505 \r
3506 \r
3507   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3508     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3509     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3510     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3511     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3512     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3513     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3514     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3515     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3516     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3517     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3518     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3519     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3520     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3521     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3522     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3523     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3524     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3525     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3526     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3527     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3528     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3529     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3530     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3531     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3532     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3533     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3534     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3535     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3536     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3537     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3538 \r
3539     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3540       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3541       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3542       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3543       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3544       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3545       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3546       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3547       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3548       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3549       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3550       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3551       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3552     } else {\r
3553       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3554       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3555       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3556       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3557       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3558       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3559       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3560       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3561       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3562       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3563       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3564       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3565     }\r
3566 \r
3567   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3568     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3569     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3570     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3571     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3572     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3573     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3574     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3575     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3576     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3577     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3578     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3579     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3580     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3581     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3582   }\r
3583 \r
3584 \r
3585   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3586   /* special Shogi support in this size */\r
3587   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3588       for (piece = WhitePawn;\r
3589            (int) piece < (int) BlackPawn;\r
3590            piece = (ChessSquare) ((int) piece + 1)) {\r
3591         if (pieceBitmap[i][piece] != NULL)\r
3592           DeleteObject(pieceBitmap[i][piece]);\r
3593       }\r
3594     }\r
3595   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3596   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3597   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3598   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3599   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3600   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3601   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3602   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3603   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3604   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3605   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3606   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3607   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3608   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3609   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3610   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3611   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3612   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3613   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3614   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3615   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3616   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3617   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3618   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3619   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3620   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3621   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3622   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3623   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3624   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3625   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3626   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3627   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3628   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3629   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3630   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3631   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3632   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3633   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3634   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3635   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3636   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3637   minorSize = 0;\r
3638   }\r
3639 }\r
3640 \r
3641 HBITMAP\r
3642 PieceBitmap(ChessSquare p, int kind)\r
3643 {\r
3644   if ((int) p >= (int) BlackPawn)\r
3645     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3646 \r
3647   return pieceBitmap[kind][(int) p];\r
3648 }\r
3649 \r
3650 /***************************************************************/\r
3651 \r
3652 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3653 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3654 /*\r
3655 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3656 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3657 */\r
3658 \r
3659 VOID\r
3660 SquareToPos(int row, int column, int * x, int * y)\r
3661 {\r
3662   if (flipView) {\r
3663     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3664     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3665   } else {\r
3666     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3667     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3668   }\r
3669 }\r
3670 \r
3671 VOID\r
3672 DrawCoordsOnDC(HDC hdc)\r
3673 {\r
3674   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
3675   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
3676   char str[2] = { NULLCHAR, NULLCHAR };\r
3677   int oldMode, oldAlign, x, y, start, i;\r
3678   HFONT oldFont;\r
3679   HBRUSH oldBrush;\r
3680 \r
3681   if (!appData.showCoords)\r
3682     return;\r
3683 \r
3684   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3685 \r
3686   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3687   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3688   oldAlign = GetTextAlign(hdc);\r
3689   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3690 \r
3691   y = boardRect.top + lineGap;\r
3692   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3693 \r
3694   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3695   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3696     str[0] = files[start + i];\r
3697     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3698     y += squareSize + lineGap;\r
3699   }\r
3700 \r
3701   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3702 \r
3703   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3704   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3705     str[0] = ranks[start + i];\r
3706     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3707     x += squareSize + lineGap;\r
3708   }    \r
3709 \r
3710   SelectObject(hdc, oldBrush);\r
3711   SetBkMode(hdc, oldMode);\r
3712   SetTextAlign(hdc, oldAlign);\r
3713   SelectObject(hdc, oldFont);\r
3714 }\r
3715 \r
3716 VOID\r
3717 DrawGridOnDC(HDC hdc)\r
3718 {\r
3719   HPEN oldPen;\r
3720  \r
3721   if (lineGap != 0) {\r
3722     oldPen = SelectObject(hdc, gridPen);\r
3723     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3724     SelectObject(hdc, oldPen);\r
3725   }\r
3726 }\r
3727 \r
3728 #define HIGHLIGHT_PEN 0\r
3729 #define PREMOVE_PEN   1\r
3730 \r
3731 VOID\r
3732 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3733 {\r
3734   int x1, y1;\r
3735   HPEN oldPen, hPen;\r
3736   if (lineGap == 0) return;\r
3737   if (flipView) {\r
3738     x1 = boardRect.left +\r
3739       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3740     y1 = boardRect.top +\r
3741       lineGap/2 + y * (squareSize + lineGap);\r
3742   } else {\r
3743     x1 = boardRect.left +\r
3744       lineGap/2 + x * (squareSize + lineGap);\r
3745     y1 = boardRect.top +\r
3746       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3747   }\r
3748   hPen = pen ? premovePen : highlightPen;\r
3749   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3750   MoveToEx(hdc, x1, y1, NULL);\r
3751   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3752   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3753   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3754   LineTo(hdc, x1, y1);\r
3755   SelectObject(hdc, oldPen);\r
3756 }\r
3757 \r
3758 VOID\r
3759 DrawHighlightsOnDC(HDC hdc)\r
3760 {\r
3761   int i;\r
3762   for (i=0; i<2; i++) {\r
3763     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3764       DrawHighlightOnDC(hdc, TRUE,\r
3765                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3766                         HIGHLIGHT_PEN);\r
3767   }\r
3768   for (i=0; i<2; i++) {\r
3769     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3770         premoveHighlightInfo.sq[i].y >= 0) {\r
3771         DrawHighlightOnDC(hdc, TRUE,\r
3772                           premoveHighlightInfo.sq[i].x, \r
3773                           premoveHighlightInfo.sq[i].y,\r
3774                           PREMOVE_PEN);\r
3775     }\r
3776   }\r
3777 }\r
3778 \r
3779 /* Note: sqcolor is used only in monoMode */\r
3780 /* Note that this code is largely duplicated in woptions.c,\r
3781    function DrawSampleSquare, so that needs to be updated too */\r
3782 VOID\r
3783 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3784 {\r
3785   HBITMAP oldBitmap;\r
3786   HBRUSH oldBrush;\r
3787   int tmpSize;\r
3788 \r
3789   if (appData.blindfold) return;\r
3790 \r
3791   /* [AS] Use font-based pieces if needed */\r
3792   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3793     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3794     CreatePiecesFromFont();\r
3795 \r
3796     if( fontBitmapSquareSize == squareSize ) {\r
3797         int index = TranslatePieceToFontPiece(piece);\r
3798 \r
3799         SelectObject( tmphdc, hPieceMask[ index ] );\r
3800 \r
3801         BitBlt( hdc,\r
3802             x, y,\r
3803             squareSize, squareSize,\r
3804             tmphdc,\r
3805             0, 0,\r
3806             SRCAND );\r
3807 \r
3808         SelectObject( tmphdc, hPieceFace[ index ] );\r
3809 \r
3810         BitBlt( hdc,\r
3811             x, y,\r
3812             squareSize, squareSize,\r
3813             tmphdc,\r
3814             0, 0,\r
3815             SRCPAINT );\r
3816 \r
3817         return;\r
3818     }\r
3819   }\r
3820 \r
3821   if (appData.monoMode) {\r
3822     SelectObject(tmphdc, PieceBitmap(piece, \r
3823       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3824     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3825            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3826   } else {\r
3827     tmpSize = squareSize;\r
3828     if(minorSize &&\r
3829         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3830          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3831       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3832       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3833       x += (squareSize - minorSize)>>1;\r
3834       y += squareSize - minorSize - 2;\r
3835       tmpSize = minorSize;\r
3836     }\r
3837     if (color || appData.allWhite ) {\r
3838       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3839       if( color )\r
3840               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3841       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3842       if(appData.upsideDown && color==flipView)\r
3843         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3844       else\r
3845         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3846       /* Use black for outline of white pieces */\r
3847       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3848       if(appData.upsideDown && color==flipView)\r
3849         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3850       else\r
3851         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3852     } else {\r
3853       /* Use square color for details of black pieces */\r
3854       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3855       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3856       if(appData.upsideDown && !flipView)\r
3857         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3858       else\r
3859         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3860     }\r
3861     SelectObject(hdc, oldBrush);\r
3862     SelectObject(tmphdc, oldBitmap);\r
3863   }\r
3864 }\r
3865 \r
3866 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3867 int GetBackTextureMode( int algo )\r
3868 {\r
3869     int result = BACK_TEXTURE_MODE_DISABLED;\r
3870 \r
3871     switch( algo ) \r
3872     {\r
3873         case BACK_TEXTURE_MODE_PLAIN:\r
3874             result = 1; /* Always use identity map */\r
3875             break;\r
3876         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3877             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3878             break;\r
3879     }\r
3880 \r
3881     return result;\r
3882 }\r
3883 \r
3884 /* \r
3885     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3886     to handle redraws cleanly (as random numbers would always be different).\r
3887 */\r
3888 VOID RebuildTextureSquareInfo()\r
3889 {\r
3890     BITMAP bi;\r
3891     int lite_w = 0;\r
3892     int lite_h = 0;\r
3893     int dark_w = 0;\r
3894     int dark_h = 0;\r
3895     int row;\r
3896     int col;\r
3897 \r
3898     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3899 \r
3900     if( liteBackTexture != NULL ) {\r
3901         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3902             lite_w = bi.bmWidth;\r
3903             lite_h = bi.bmHeight;\r
3904         }\r
3905     }\r
3906 \r
3907     if( darkBackTexture != NULL ) {\r
3908         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3909             dark_w = bi.bmWidth;\r
3910             dark_h = bi.bmHeight;\r
3911         }\r
3912     }\r
3913 \r
3914     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3915         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3916             if( (col + row) & 1 ) {\r
3917                 /* Lite square */\r
3918                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3919                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3920                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3921                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3922                 }\r
3923             }\r
3924             else {\r
3925                 /* Dark square */\r
3926                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3927                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3928                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3929                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3930                 }\r
3931             }\r
3932         }\r
3933     }\r
3934 }\r
3935 \r
3936 /* [AS] Arrow highlighting support */\r
3937 \r
3938 static int A_WIDTH = 5; /* Width of arrow body */\r
3939 \r
3940 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3941 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3942 \r
3943 static double Sqr( double x )\r
3944 {\r
3945     return x*x;\r
3946 }\r
3947 \r
3948 static int Round( double x )\r
3949 {\r
3950     return (int) (x + 0.5);\r
3951 }\r
3952 \r
3953 /* Draw an arrow between two points using current settings */\r
3954 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3955 {\r
3956     POINT arrow[7];\r
3957     double dx, dy, j, k, x, y;\r
3958 \r
3959     if( d_x == s_x ) {\r
3960         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3961 \r
3962         arrow[0].x = s_x + A_WIDTH;\r
3963         arrow[0].y = s_y;\r
3964 \r
3965         arrow[1].x = s_x + A_WIDTH;\r
3966         arrow[1].y = d_y - h;\r
3967 \r
3968         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3969         arrow[2].y = d_y - h;\r
3970 \r
3971         arrow[3].x = d_x;\r
3972         arrow[3].y = d_y;\r
3973 \r
3974         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3975         arrow[4].y = d_y - h;\r
3976 \r
3977         arrow[5].x = s_x - A_WIDTH;\r
3978         arrow[5].y = d_y - h;\r
3979 \r
3980         arrow[6].x = s_x - A_WIDTH;\r
3981         arrow[6].y = s_y;\r
3982     }\r
3983     else if( d_y == s_y ) {\r
3984         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3985 \r
3986         arrow[0].x = s_x;\r
3987         arrow[0].y = s_y + A_WIDTH;\r
3988 \r
3989         arrow[1].x = d_x - w;\r
3990         arrow[1].y = s_y + A_WIDTH;\r
3991 \r
3992         arrow[2].x = d_x - w;\r
3993         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3994 \r
3995         arrow[3].x = d_x;\r
3996         arrow[3].y = d_y;\r
3997 \r
3998         arrow[4].x = d_x - w;\r
3999         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
4000 \r
4001         arrow[5].x = d_x - w;\r
4002         arrow[5].y = s_y - A_WIDTH;\r
4003 \r
4004         arrow[6].x = s_x;\r
4005         arrow[6].y = s_y - A_WIDTH;\r
4006     }\r
4007     else {\r
4008         /* [AS] Needed a lot of paper for this! :-) */\r
4009         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4010         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4011   \r
4012         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4013 \r
4014         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4015 \r
4016         x = s_x;\r
4017         y = s_y;\r
4018 \r
4019         arrow[0].x = Round(x - j);\r
4020         arrow[0].y = Round(y + j*dx);\r
4021 \r
4022         arrow[1].x = Round(x + j);\r
4023         arrow[1].y = Round(y - j*dx);\r
4024 \r
4025         if( d_x > s_x ) {\r
4026             x = (double) d_x - k;\r
4027             y = (double) d_y - k*dy;\r
4028         }\r
4029         else {\r
4030             x = (double) d_x + k;\r
4031             y = (double) d_y + k*dy;\r
4032         }\r
4033 \r
4034         arrow[2].x = Round(x + j);\r
4035         arrow[2].y = Round(y - j*dx);\r
4036 \r
4037         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4038         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4039 \r
4040         arrow[4].x = d_x;\r
4041         arrow[4].y = d_y;\r
4042 \r
4043         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4044         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4045 \r
4046         arrow[6].x = Round(x - j);\r
4047         arrow[6].y = Round(y + j*dx);\r
4048     }\r
4049 \r
4050     Polygon( hdc, arrow, 7 );\r
4051 }\r
4052 \r
4053 /* [AS] Draw an arrow between two squares */\r
4054 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4055 {\r
4056     int s_x, s_y, d_x, d_y;\r
4057     HPEN hpen;\r
4058     HPEN holdpen;\r
4059     HBRUSH hbrush;\r
4060     HBRUSH holdbrush;\r
4061     LOGBRUSH stLB;\r
4062 \r
4063     if( s_col == d_col && s_row == d_row ) {\r
4064         return;\r
4065     }\r
4066 \r
4067     /* Get source and destination points */\r
4068     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4069     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4070 \r
4071     if( d_y > s_y ) {\r
4072         d_y += squareSize / 4;\r
4073     }\r
4074     else if( d_y < s_y ) {\r
4075         d_y += 3 * squareSize / 4;\r
4076     }\r
4077     else {\r
4078         d_y += squareSize / 2;\r
4079     }\r
4080 \r
4081     if( d_x > s_x ) {\r
4082         d_x += squareSize / 4;\r
4083     }\r
4084     else if( d_x < s_x ) {\r
4085         d_x += 3 * squareSize / 4;\r
4086     }\r
4087     else {\r
4088         d_x += squareSize / 2;\r
4089     }\r
4090 \r
4091     s_x += squareSize / 2;\r
4092     s_y += squareSize / 2;\r
4093 \r
4094     /* Adjust width */\r
4095     A_WIDTH = squareSize / 14;\r
4096 \r
4097     /* Draw */\r
4098     stLB.lbStyle = BS_SOLID;\r
4099     stLB.lbColor = appData.highlightArrowColor;\r
4100     stLB.lbHatch = 0;\r
4101 \r
4102     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4103     holdpen = SelectObject( hdc, hpen );\r
4104     hbrush = CreateBrushIndirect( &stLB );\r
4105     holdbrush = SelectObject( hdc, hbrush );\r
4106 \r
4107     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4108 \r
4109     SelectObject( hdc, holdpen );\r
4110     SelectObject( hdc, holdbrush );\r
4111     DeleteObject( hpen );\r
4112     DeleteObject( hbrush );\r
4113 }\r
4114 \r
4115 BOOL HasHighlightInfo()\r
4116 {\r
4117     BOOL result = FALSE;\r
4118 \r
4119     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4120         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4121     {\r
4122         result = TRUE;\r
4123     }\r
4124 \r
4125     return result;\r
4126 }\r
4127 \r
4128 BOOL IsDrawArrowEnabled()\r
4129 {\r
4130     BOOL result = FALSE;\r
4131 \r
4132     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4133         result = TRUE;\r
4134     }\r
4135 \r
4136     return result;\r
4137 }\r
4138 \r
4139 VOID DrawArrowHighlight( HDC hdc )\r
4140 {\r
4141     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4142         DrawArrowBetweenSquares( hdc,\r
4143             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4144             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4145     }\r
4146 }\r
4147 \r
4148 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4149 {\r
4150     HRGN result = NULL;\r
4151 \r
4152     if( HasHighlightInfo() ) {\r
4153         int x1, y1, x2, y2;\r
4154         int sx, sy, dx, dy;\r
4155 \r
4156         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4157         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4158 \r
4159         sx = MIN( x1, x2 );\r
4160         sy = MIN( y1, y2 );\r
4161         dx = MAX( x1, x2 ) + squareSize;\r
4162         dy = MAX( y1, y2 ) + squareSize;\r
4163 \r
4164         result = CreateRectRgn( sx, sy, dx, dy );\r
4165     }\r
4166 \r
4167     return result;\r
4168 }\r
4169 \r
4170 /*\r
4171     Warning: this function modifies the behavior of several other functions. \r
4172     \r
4173     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4174     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4175     repaint is scattered all over the place, which is not good for features such as\r
4176     "arrow highlighting" that require a full repaint of the board.\r
4177 \r
4178     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4179     user interaction, when speed is not so important) but especially to avoid errors\r
4180     in the displayed graphics.\r
4181 \r
4182     In such patched places, I always try refer to this function so there is a single\r
4183     place to maintain knowledge.\r
4184     \r
4185     To restore the original behavior, just return FALSE unconditionally.\r
4186 */\r
4187 BOOL IsFullRepaintPreferrable()\r
4188 {\r
4189     BOOL result = FALSE;\r
4190 \r
4191     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4192         /* Arrow may appear on the board */\r
4193         result = TRUE;\r
4194     }\r
4195 \r
4196     return result;\r
4197 }\r
4198 \r
4199 /* \r
4200     This function is called by DrawPosition to know whether a full repaint must\r
4201     be forced or not.\r
4202 \r
4203     Only DrawPosition may directly call this function, which makes use of \r
4204     some state information. Other function should call DrawPosition specifying \r
4205     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4206 */\r
4207 BOOL DrawPositionNeedsFullRepaint()\r
4208 {\r
4209     BOOL result = FALSE;\r
4210 \r
4211     /* \r
4212         Probably a slightly better policy would be to trigger a full repaint\r
4213         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4214         but animation is fast enough that it's difficult to notice.\r
4215     */\r
4216     if( animInfo.piece == EmptySquare ) {\r
4217         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4218             result = TRUE;\r
4219         }\r
4220     }\r
4221 \r
4222     return result;\r
4223 }\r
4224 \r
4225 VOID\r
4226 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4227 {\r
4228   int row, column, x, y, square_color, piece_color;\r
4229   ChessSquare piece;\r
4230   HBRUSH oldBrush;\r
4231   HDC texture_hdc = NULL;\r
4232 \r
4233   /* [AS] Initialize background textures if needed */\r
4234   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4235       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4236       if( backTextureSquareSize != squareSize \r
4237        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4238           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4239           backTextureSquareSize = squareSize;\r
4240           RebuildTextureSquareInfo();\r
4241       }\r
4242 \r
4243       texture_hdc = CreateCompatibleDC( hdc );\r
4244   }\r
4245 \r
4246   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4247     for (column = 0; column < BOARD_WIDTH; column++) {\r
4248   \r
4249       SquareToPos(row, column, &x, &y);\r
4250 \r
4251       piece = board[row][column];\r
4252 \r
4253       square_color = ((column + row) % 2) == 1;\r
4254       if( gameInfo.variant == VariantXiangqi ) {\r
4255           square_color = !InPalace(row, column);\r
4256           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4257           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4258       }\r
4259       piece_color = (int) piece < (int) BlackPawn;\r
4260 \r
4261 \r
4262       /* [HGM] holdings file: light square or black */\r
4263       if(column == BOARD_LEFT-2) {\r
4264             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4265                 square_color = 1;\r
4266             else {\r
4267                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4268                 continue;\r
4269             }\r
4270       } else\r
4271       if(column == BOARD_RGHT + 1 ) {\r
4272             if( row < gameInfo.holdingsSize )\r
4273                 square_color = 1;\r
4274             else {\r
4275                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4276                 continue;\r
4277             }\r
4278       }\r
4279       if(column == BOARD_LEFT-1 ) /* left align */\r
4280             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4281       else if( column == BOARD_RGHT) /* right align */\r
4282             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4283       else\r
4284       if (appData.monoMode) {\r
4285         if (piece == EmptySquare) {\r
4286           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4287                  square_color ? WHITENESS : BLACKNESS);\r
4288         } else {\r
4289           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4290         }\r
4291       } \r
4292       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4293           /* [AS] Draw the square using a texture bitmap */\r
4294           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4295           int r = row, c = column; // [HGM] do not flip board in flipView\r
4296           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4297 \r
4298           DrawTile( x, y, \r
4299               squareSize, squareSize, \r
4300               hdc, \r
4301               texture_hdc,\r
4302               backTextureSquareInfo[r][c].mode,\r
4303               backTextureSquareInfo[r][c].x,\r
4304               backTextureSquareInfo[r][c].y );\r
4305 \r
4306           SelectObject( texture_hdc, hbm );\r
4307 \r
4308           if (piece != EmptySquare) {\r
4309               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4310           }\r
4311       }\r
4312       else {\r
4313         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4314 \r
4315         oldBrush = SelectObject(hdc, brush );\r
4316         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4317         SelectObject(hdc, oldBrush);\r
4318         if (piece != EmptySquare)\r
4319           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4320       }\r
4321     }\r
4322   }\r
4323 \r
4324   if( texture_hdc != NULL ) {\r
4325     DeleteDC( texture_hdc );\r
4326   }\r
4327 }\r
4328 \r
4329 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4330 void fputDW(FILE *f, int x)\r
4331 {\r
4332         fputc(x     & 255, f);\r
4333         fputc(x>>8  & 255, f);\r
4334         fputc(x>>16 & 255, f);\r
4335         fputc(x>>24 & 255, f);\r
4336 }\r
4337 \r
4338 #define MAX_CLIPS 200   /* more than enough */\r
4339 \r
4340 VOID\r
4341 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4342 {\r
4343 //  HBITMAP bufferBitmap;\r
4344   BITMAP bi;\r
4345 //  RECT Rect;\r
4346   HDC tmphdc;\r
4347   HBITMAP hbm;\r
4348   int w = 100, h = 50;\r
4349 \r
4350   if(logo == NULL) return;\r
4351 //  GetClientRect(hwndMain, &Rect);\r
4352 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4353 //                                      Rect.bottom-Rect.top+1);\r
4354   tmphdc = CreateCompatibleDC(hdc);\r
4355   hbm = SelectObject(tmphdc, logo);\r
4356   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4357             w = bi.bmWidth;\r
4358             h = bi.bmHeight;\r
4359   }\r
4360   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4361                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4362   SelectObject(tmphdc, hbm);\r
4363   DeleteDC(tmphdc);\r
4364 }\r
4365 \r
4366 VOID\r
4367 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4368 {\r
4369   static Board lastReq, lastDrawn;\r
4370   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4371   static int lastDrawnFlipView = 0;\r
4372   static int lastReqValid = 0, lastDrawnValid = 0;\r
4373   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4374   HDC tmphdc;\r
4375   HDC hdcmem;\r
4376   HBITMAP bufferBitmap;\r
4377   HBITMAP oldBitmap;\r
4378   RECT Rect;\r
4379   HRGN clips[MAX_CLIPS];\r
4380   ChessSquare dragged_piece = EmptySquare;\r
4381 \r
4382   /* I'm undecided on this - this function figures out whether a full\r
4383    * repaint is necessary on its own, so there's no real reason to have the\r
4384    * caller tell it that.  I think this can safely be set to FALSE - but\r
4385    * if we trust the callers not to request full repaints unnessesarily, then\r
4386    * we could skip some clipping work.  In other words, only request a full\r
4387    * redraw when the majority of pieces have changed positions (ie. flip, \r
4388    * gamestart and similar)  --Hawk\r
4389    */\r
4390   Boolean fullrepaint = repaint;\r
4391 \r
4392   if( DrawPositionNeedsFullRepaint() ) {\r
4393       fullrepaint = TRUE;\r
4394   }\r
4395 \r
4396   if (board == NULL) {\r
4397     if (!lastReqValid) {\r
4398       return;\r
4399     }\r
4400     board = lastReq;\r
4401   } else {\r
4402     CopyBoard(lastReq, board);\r
4403     lastReqValid = 1;\r
4404   }\r
4405 \r
4406   if (doingSizing) {\r
4407     return;\r
4408   }\r
4409 \r
4410   if (IsIconic(hwndMain)) {\r
4411     return;\r
4412   }\r
4413 \r
4414   if (hdc == NULL) {\r
4415     hdc = GetDC(hwndMain);\r
4416     if (!appData.monoMode) {\r
4417       SelectPalette(hdc, hPal, FALSE);\r
4418       RealizePalette(hdc);\r
4419     }\r
4420     releaseDC = TRUE;\r
4421   } else {\r
4422     releaseDC = FALSE;\r
4423   }\r
4424 \r
4425   /* Create some work-DCs */\r
4426   hdcmem = CreateCompatibleDC(hdc);\r
4427   tmphdc = CreateCompatibleDC(hdc);\r
4428 \r
4429   /* If dragging is in progress, we temporarely remove the piece */\r
4430   /* [HGM] or temporarily decrease count if stacked              */\r
4431   /*       !! Moved to before board compare !!                   */\r
4432   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4433     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4434     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4435             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4436         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4437     } else \r
4438     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4439             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4440         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4441     } else \r
4442         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4443   }\r
4444 \r
4445   /* Figure out which squares need updating by comparing the \r
4446    * newest board with the last drawn board and checking if\r
4447    * flipping has changed.\r
4448    */\r
4449   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4450     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4451       for (column = 0; column < BOARD_WIDTH; column++) {\r
4452         if (lastDrawn[row][column] != board[row][column]) {\r
4453           SquareToPos(row, column, &x, &y);\r
4454           clips[num_clips++] =\r
4455             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4456         }\r
4457       }\r
4458     }\r
4459     for (i=0; i<2; i++) {\r
4460       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4461           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4462         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4463             lastDrawnHighlight.sq[i].y >= 0) {\r
4464           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4465                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4466           clips[num_clips++] =\r
4467             CreateRectRgn(x - lineGap, y - lineGap, \r
4468                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4469         }\r
4470         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4471           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4472           clips[num_clips++] =\r
4473             CreateRectRgn(x - lineGap, y - lineGap, \r
4474                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4475         }\r
4476       }\r
4477     }\r
4478     for (i=0; i<2; i++) {\r
4479       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4480           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4481         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4482             lastDrawnPremove.sq[i].y >= 0) {\r
4483           SquareToPos(lastDrawnPremove.sq[i].y,\r
4484                       lastDrawnPremove.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         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4490             premoveHighlightInfo.sq[i].y >= 0) {\r
4491           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4492                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4493           clips[num_clips++] =\r
4494             CreateRectRgn(x - lineGap, y - lineGap, \r
4495                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4496         }\r
4497       }\r
4498     }\r
4499   } else {\r
4500     fullrepaint = TRUE;\r
4501   }\r
4502 \r
4503   /* Create a buffer bitmap - this is the actual bitmap\r
4504    * being written to.  When all the work is done, we can\r
4505    * copy it to the real DC (the screen).  This avoids\r
4506    * the problems with flickering.\r
4507    */\r
4508   GetClientRect(hwndMain, &Rect);\r
4509   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4510                                         Rect.bottom-Rect.top+1);\r
4511   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4512   if (!appData.monoMode) {\r
4513     SelectPalette(hdcmem, hPal, FALSE);\r
4514   }\r
4515 \r
4516   /* Create clips for dragging */\r
4517   if (!fullrepaint) {\r
4518     if (dragInfo.from.x >= 0) {\r
4519       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4520       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4521     }\r
4522     if (dragInfo.start.x >= 0) {\r
4523       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4524       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4525     }\r
4526     if (dragInfo.pos.x >= 0) {\r
4527       x = dragInfo.pos.x - squareSize / 2;\r
4528       y = dragInfo.pos.y - squareSize / 2;\r
4529       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4530     }\r
4531     if (dragInfo.lastpos.x >= 0) {\r
4532       x = dragInfo.lastpos.x - squareSize / 2;\r
4533       y = dragInfo.lastpos.y - squareSize / 2;\r
4534       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4535     }\r
4536   }\r
4537 \r
4538   /* Are we animating a move?  \r
4539    * If so, \r
4540    *   - remove the piece from the board (temporarely)\r
4541    *   - calculate the clipping region\r
4542    */\r
4543   if (!fullrepaint) {\r
4544     if (animInfo.piece != EmptySquare) {\r
4545       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4546       x = boardRect.left + animInfo.lastpos.x;\r
4547       y = boardRect.top + animInfo.lastpos.y;\r
4548       x2 = boardRect.left + animInfo.pos.x;\r
4549       y2 = boardRect.top + animInfo.pos.y;\r
4550       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4551       /* Slight kludge.  The real problem is that after AnimateMove is\r
4552          done, the position on the screen does not match lastDrawn.\r
4553          This currently causes trouble only on e.p. captures in\r
4554          atomic, where the piece moves to an empty square and then\r
4555          explodes.  The old and new positions both had an empty square\r
4556          at the destination, but animation has drawn a piece there and\r
4557          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4558       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4559     }\r
4560   }\r
4561 \r
4562   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4563   if (num_clips == 0)\r
4564     fullrepaint = TRUE;\r
4565 \r
4566   /* Set clipping on the memory DC */\r
4567   if (!fullrepaint) {\r
4568     SelectClipRgn(hdcmem, clips[0]);\r
4569     for (x = 1; x < num_clips; x++) {\r
4570       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4571         abort();  // this should never ever happen!\r
4572     }\r
4573   }\r
4574 \r
4575   /* Do all the drawing to the memory DC */\r
4576   if(explodeInfo.radius) { // [HGM] atomic\r
4577         HBRUSH oldBrush;\r
4578         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4579         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4580         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4581         x += squareSize/2;\r
4582         y += squareSize/2;\r
4583         if(!fullrepaint) {\r
4584           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4585           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4586         }\r
4587         DrawGridOnDC(hdcmem);\r
4588         DrawHighlightsOnDC(hdcmem);\r
4589         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4590         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4591         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4592         SelectObject(hdcmem, oldBrush);\r
4593   } else {\r
4594     DrawGridOnDC(hdcmem);\r
4595     DrawHighlightsOnDC(hdcmem);\r
4596     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4597   }\r
4598   if(logoHeight) {\r
4599         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4600         if(appData.autoLogo) {\r
4601           \r
4602           switch(gameMode) { // pick logos based on game mode\r
4603             case IcsObserving:\r
4604                 whiteLogo = second.programLogo; // ICS logo\r
4605                 blackLogo = second.programLogo;\r
4606             default:\r
4607                 break;\r
4608             case IcsPlayingWhite:\r
4609                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4610                 blackLogo = second.programLogo; // ICS logo\r
4611                 break;\r
4612             case IcsPlayingBlack:\r
4613                 whiteLogo = second.programLogo; // ICS logo\r
4614                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4615                 break;\r
4616             case TwoMachinesPlay:\r
4617                 if(first.twoMachinesColor[0] == 'b') {\r
4618                     whiteLogo = second.programLogo;\r
4619                     blackLogo = first.programLogo;\r
4620                 }\r
4621                 break;\r
4622             case MachinePlaysWhite:\r
4623                 blackLogo = userLogo;\r
4624                 break;\r
4625             case MachinePlaysBlack:\r
4626                 whiteLogo = userLogo;\r
4627                 blackLogo = first.programLogo;\r
4628           }\r
4629         }\r
4630         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4631         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4632   }\r
4633 \r
4634   if( appData.highlightMoveWithArrow ) {\r
4635     DrawArrowHighlight(hdcmem);\r
4636   }\r
4637 \r
4638   DrawCoordsOnDC(hdcmem);\r
4639 \r
4640   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4641                  /* to make sure lastDrawn contains what is actually drawn */\r
4642 \r
4643   /* Put the dragged piece back into place and draw it (out of place!) */\r
4644     if (dragged_piece != EmptySquare) {\r
4645     /* [HGM] or restack */\r
4646     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4647                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4648     else\r
4649     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4650                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4651     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4652     x = dragInfo.pos.x - squareSize / 2;\r
4653     y = dragInfo.pos.y - squareSize / 2;\r
4654     DrawPieceOnDC(hdcmem, dragged_piece,\r
4655                   ((int) dragged_piece < (int) BlackPawn), \r
4656                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4657   }   \r
4658   \r
4659   /* Put the animated piece back into place and draw it */\r
4660   if (animInfo.piece != EmptySquare) {\r
4661     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4662     x = boardRect.left + animInfo.pos.x;\r
4663     y = boardRect.top + animInfo.pos.y;\r
4664     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4665                   ((int) animInfo.piece < (int) BlackPawn),\r
4666                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4667   }\r
4668 \r
4669   /* Release the bufferBitmap by selecting in the old bitmap \r
4670    * and delete the memory DC\r
4671    */\r
4672   SelectObject(hdcmem, oldBitmap);\r
4673   DeleteDC(hdcmem);\r
4674 \r
4675   /* Set clipping on the target DC */\r
4676   if (!fullrepaint) {\r
4677     SelectClipRgn(hdc, clips[0]);\r
4678     for (x = 1; x < num_clips; x++) {\r
4679       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4680         abort();   // this should never ever happen!\r
4681     } \r
4682   }\r
4683 \r
4684   /* Copy the new bitmap onto the screen in one go.\r
4685    * This way we avoid any flickering\r
4686    */\r
4687   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4688   BitBlt(hdc, boardRect.left, boardRect.top,\r
4689          boardRect.right - boardRect.left,\r
4690          boardRect.bottom - boardRect.top,\r
4691          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4692   if(saveDiagFlag) { \r
4693     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4694     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4695 \r
4696     GetObject(bufferBitmap, sizeof(b), &b);\r
4697     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4698         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4699         bih.biWidth = b.bmWidth;\r
4700         bih.biHeight = b.bmHeight;\r
4701         bih.biPlanes = 1;\r
4702         bih.biBitCount = b.bmBitsPixel;\r
4703         bih.biCompression = 0;\r
4704         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4705         bih.biXPelsPerMeter = 0;\r
4706         bih.biYPelsPerMeter = 0;\r
4707         bih.biClrUsed = 0;\r
4708         bih.biClrImportant = 0;\r
4709 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4710 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4711         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4712 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4713 \r
4714         wb = b.bmWidthBytes;\r
4715         // count colors\r
4716         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4717                 int k = ((int*) pData)[i];\r
4718                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4719                 if(j >= 16) break;\r
4720                 color[j] = k;\r
4721                 if(j >= nrColors) nrColors = j+1;\r
4722         }\r
4723         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4724                 INT p = 0;\r
4725                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4726                     for(w=0; w<(wb>>2); w+=2) {\r
4727                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4728                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4729                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4730                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4731                         pData[p++] = m | j<<4;\r
4732                     }\r
4733                     while(p&3) pData[p++] = 0;\r
4734                 }\r
4735                 fac = 3;\r
4736                 wb = ((wb+31)>>5)<<2;\r
4737         }\r
4738         // write BITMAPFILEHEADER\r
4739         fprintf(diagFile, "BM");\r
4740         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4741         fputDW(diagFile, 0);\r
4742         fputDW(diagFile, 0x36 + (fac?64:0));\r
4743         // write BITMAPINFOHEADER\r
4744         fputDW(diagFile, 40);\r
4745         fputDW(diagFile, b.bmWidth);\r
4746         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4747         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4748         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4749         fputDW(diagFile, 0);\r
4750         fputDW(diagFile, 0);\r
4751         fputDW(diagFile, 0);\r
4752         fputDW(diagFile, 0);\r
4753         fputDW(diagFile, 0);\r
4754         fputDW(diagFile, 0);\r
4755         // write color table\r
4756         if(fac)\r
4757         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4758         // write bitmap data\r
4759         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4760                 fputc(pData[i], diagFile);\r
4761      }\r
4762   }\r
4763 \r
4764   SelectObject(tmphdc, oldBitmap);\r
4765 \r
4766   /* Massive cleanup */\r
4767   for (x = 0; x < num_clips; x++)\r
4768     DeleteObject(clips[x]);\r
4769 \r
4770   DeleteDC(tmphdc);\r
4771   DeleteObject(bufferBitmap);\r
4772 \r
4773   if (releaseDC) \r
4774     ReleaseDC(hwndMain, hdc);\r
4775   \r
4776   if (lastDrawnFlipView != flipView) {\r
4777     if (flipView)\r
4778       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4779     else\r
4780       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4781   }\r
4782 \r
4783 /*  CopyBoard(lastDrawn, board);*/\r
4784   lastDrawnHighlight = highlightInfo;\r
4785   lastDrawnPremove   = premoveHighlightInfo;\r
4786   lastDrawnFlipView = flipView;\r
4787   lastDrawnValid = 1;\r
4788 }\r
4789 \r
4790 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4791 int\r
4792 SaveDiagram(f)\r
4793      FILE *f;\r
4794 {\r
4795     saveDiagFlag = 1; diagFile = f;\r
4796     HDCDrawPosition(NULL, TRUE, NULL);\r
4797 \r
4798     saveDiagFlag = 0;\r
4799 \r
4800 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4801     \r
4802     fclose(f);\r
4803     return TRUE;\r
4804 }\r
4805 \r
4806 \r
4807 /*---------------------------------------------------------------------------*\\r
4808 | CLIENT PAINT PROCEDURE\r
4809 |   This is the main event-handler for the WM_PAINT message.\r
4810 |\r
4811 \*---------------------------------------------------------------------------*/\r
4812 VOID\r
4813 PaintProc(HWND hwnd)\r
4814 {\r
4815   HDC         hdc;\r
4816   PAINTSTRUCT ps;\r
4817   HFONT       oldFont;\r
4818 \r
4819   if((hdc = BeginPaint(hwnd, &ps))) {\r
4820     if (IsIconic(hwnd)) {\r
4821       DrawIcon(hdc, 2, 2, iconCurrent);\r
4822     } else {\r
4823       if (!appData.monoMode) {\r
4824         SelectPalette(hdc, hPal, FALSE);\r
4825         RealizePalette(hdc);\r
4826       }\r
4827       HDCDrawPosition(hdc, 1, NULL);\r
4828       oldFont =\r
4829         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4830       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4831                  ETO_CLIPPED|ETO_OPAQUE,\r
4832                  &messageRect, messageText, strlen(messageText), NULL);\r
4833       SelectObject(hdc, oldFont);\r
4834       DisplayBothClocks();\r
4835     }\r
4836     EndPaint(hwnd,&ps);\r
4837   }\r
4838 \r
4839   return;\r
4840 }\r
4841 \r
4842 \r
4843 /*\r
4844  * If the user selects on a border boundary, return -1; if off the board,\r
4845  *   return -2.  Otherwise map the event coordinate to the square.\r
4846  * The offset boardRect.left or boardRect.top must already have been\r
4847  *   subtracted from x.\r
4848  */\r
4849 int EventToSquare(x, limit)\r
4850      int x;\r
4851 {\r
4852   if (x <= 0)\r
4853     return -2;\r
4854   if (x < lineGap)\r
4855     return -1;\r
4856   x -= lineGap;\r
4857   if ((x % (squareSize + lineGap)) >= squareSize)\r
4858     return -1;\r
4859   x /= (squareSize + lineGap);\r
4860     if (x >= limit)\r
4861     return -2;\r
4862   return x;\r
4863 }\r
4864 \r
4865 typedef struct {\r
4866   char piece;\r
4867   int command;\r
4868   char* name;\r
4869 } DropEnable;\r
4870 \r
4871 DropEnable dropEnables[] = {\r
4872   { 'P', DP_Pawn, "Pawn" },\r
4873   { 'N', DP_Knight, "Knight" },\r
4874   { 'B', DP_Bishop, "Bishop" },\r
4875   { 'R', DP_Rook, "Rook" },\r
4876   { 'Q', DP_Queen, "Queen" },\r
4877 };\r
4878 \r
4879 VOID\r
4880 SetupDropMenu(HMENU hmenu)\r
4881 {\r
4882   int i, count, enable;\r
4883   char *p;\r
4884   extern char white_holding[], black_holding[];\r
4885   char item[MSG_SIZ];\r
4886 \r
4887   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4888     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4889                dropEnables[i].piece);\r
4890     count = 0;\r
4891     while (p && *p++ == dropEnables[i].piece) count++;\r
4892     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4893     enable = count > 0 || !appData.testLegality\r
4894       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4895                       && !appData.icsActive);\r
4896     ModifyMenu(hmenu, dropEnables[i].command,\r
4897                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4898                dropEnables[i].command, item);\r
4899   }\r
4900 }\r
4901 \r
4902 void DragPieceBegin(int x, int y)\r
4903 {\r
4904       dragInfo.lastpos.x = boardRect.left + x;\r
4905       dragInfo.lastpos.y = boardRect.top + y;\r
4906       dragInfo.from.x = fromX;\r
4907       dragInfo.from.y = fromY;\r
4908       dragInfo.start = dragInfo.from;\r
4909       SetCapture(hwndMain);\r
4910 }\r
4911 \r
4912 void DragPieceEnd(int x, int y)\r
4913 {\r
4914     ReleaseCapture();\r
4915     dragInfo.start.x = dragInfo.start.y = -1;\r
4916     dragInfo.from = dragInfo.start;\r
4917     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4918 }\r
4919 \r
4920 /* Event handler for mouse messages */\r
4921 VOID\r
4922 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4923 {\r
4924   int x, y;\r
4925   POINT pt;\r
4926   static int recursive = 0;\r
4927   HMENU hmenu;\r
4928   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4929 \r
4930   if (recursive) {\r
4931     if (message == WM_MBUTTONUP) {\r
4932       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4933          to the middle button: we simulate pressing the left button too!\r
4934          */\r
4935       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4936       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4937     }\r
4938     return;\r
4939   }\r
4940   recursive++;\r
4941   \r
4942   pt.x = LOWORD(lParam);\r
4943   pt.y = HIWORD(lParam);\r
4944   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4945   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4946   if (!flipView && y >= 0) {\r
4947     y = BOARD_HEIGHT - 1 - y;\r
4948   }\r
4949   if (flipView && x >= 0) {\r
4950     x = BOARD_WIDTH - 1 - x;\r
4951   }\r
4952 \r
4953   switch (message) {\r
4954   case WM_LBUTTONDOWN:\r
4955       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4956         if (gameMode == EditPosition) {\r
4957           SetWhiteToPlayEvent();\r
4958         } else if (gameMode == IcsPlayingBlack ||\r
4959                    gameMode == MachinePlaysWhite) {\r
4960           CallFlagEvent();\r
4961         } else if (gameMode == EditGame) {\r
4962           AdjustClock(flipClock, -1);\r
4963         }\r
4964       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4965         if (gameMode == EditPosition) {\r
4966           SetBlackToPlayEvent();\r
4967         } else if (gameMode == IcsPlayingWhite ||\r
4968                    gameMode == MachinePlaysBlack) {\r
4969           CallFlagEvent();\r
4970         } else if (gameMode == EditGame) {\r
4971           AdjustClock(!flipClock, -1);\r
4972         }\r
4973       }\r
4974       dragInfo.start.x = dragInfo.start.y = -1;\r
4975       dragInfo.from = dragInfo.start;\r
4976     if(fromX == -1 && frozen) { // not sure where this is for\r
4977                 fromX = fromY = -1; \r
4978       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4979       break;\r
4980     }\r
4981       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4982       DrawPosition(TRUE, NULL);\r
4983     break;\r
4984 \r
4985   case WM_LBUTTONUP:\r
4986       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4987       DrawPosition(TRUE, NULL);\r
4988     break;\r
4989 \r
4990   case WM_MOUSEMOVE:\r
4991     if ((appData.animateDragging || appData.highlightDragging)\r
4992         && (wParam & MK_LBUTTON)\r
4993         && dragInfo.from.x >= 0) \r
4994     {\r
4995       BOOL full_repaint = FALSE;\r
4996 \r
4997       if (appData.animateDragging) {\r
4998         dragInfo.pos = pt;\r
4999       }\r
5000       if (appData.highlightDragging) {\r
5001         SetHighlights(fromX, fromY, x, y);\r
5002         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5003             full_repaint = TRUE;\r
5004         }\r
5005       }\r
5006       \r
5007       DrawPosition( full_repaint, NULL);\r
5008       \r
5009       dragInfo.lastpos = dragInfo.pos;\r
5010     }\r
5011     break;\r
5012 \r
5013   case WM_MOUSEWHEEL: // [DM]\r
5014     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5015        /* Mouse Wheel is being rolled forward\r
5016         * Play moves forward\r
5017         */\r
5018        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5019                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5020        /* Mouse Wheel is being rolled backward\r
5021         * Play moves backward\r
5022         */\r
5023        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5024                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5025     }\r
5026     break;\r
5027 \r
5028   case WM_MBUTTONDOWN:\r
5029   case WM_RBUTTONDOWN:\r
5030     ErrorPopDown();\r
5031     ReleaseCapture();\r
5032     fromX = fromY = -1;\r
5033     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5034     dragInfo.start.x = dragInfo.start.y = -1;\r
5035     dragInfo.from = dragInfo.start;\r
5036     dragInfo.lastpos = dragInfo.pos;\r
5037     if (appData.highlightDragging) {\r
5038       ClearHighlights();\r
5039     }\r
5040     if(y == -2) {\r
5041       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5042       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5043           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5044       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5045           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5046       }\r
5047     }\r
5048     DrawPosition(TRUE, NULL);\r
5049 \r
5050     switch (gameMode) {\r
5051     case EditPosition:\r
5052     case IcsExamining:\r
5053       if (x < 0 || y < 0) break;\r
5054       fromX = x;\r
5055       fromY = y;\r
5056       if (message == WM_MBUTTONDOWN) {\r
5057         buttonCount = 3;  /* even if system didn't think so */\r
5058         if (wParam & MK_SHIFT) \r
5059           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5060         else\r
5061           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5062       } else { /* message == WM_RBUTTONDOWN */\r
5063         /* Just have one menu, on the right button.  Windows users don't\r
5064            think to try the middle one, and sometimes other software steals\r
5065            it, or it doesn't really exist. */\r
5066         if(gameInfo.variant != VariantShogi)\r
5067             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5068         else\r
5069             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5070       }\r
5071       break;\r
5072     case IcsPlayingWhite:\r
5073     case IcsPlayingBlack:\r
5074     case EditGame:\r
5075     case MachinePlaysWhite:\r
5076     case MachinePlaysBlack:\r
5077       if (appData.testLegality &&\r
5078           gameInfo.variant != VariantBughouse &&\r
5079           gameInfo.variant != VariantCrazyhouse) break;\r
5080       if (x < 0 || y < 0) break;\r
5081       fromX = x;\r
5082       fromY = y;\r
5083       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5084       SetupDropMenu(hmenu);\r
5085       MenuPopup(hwnd, pt, hmenu, -1);\r
5086       break;\r
5087     default:\r
5088       break;\r
5089     }\r
5090     break;\r
5091   }\r
5092 \r
5093   recursive--;\r
5094 }\r
5095 \r
5096 /* Preprocess messages for buttons in main window */\r
5097 LRESULT CALLBACK\r
5098 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5099 {\r
5100   int id = GetWindowLong(hwnd, GWL_ID);\r
5101   int i, dir;\r
5102 \r
5103   for (i=0; i<N_BUTTONS; i++) {\r
5104     if (buttonDesc[i].id == id) break;\r
5105   }\r
5106   if (i == N_BUTTONS) return 0;\r
5107   switch (message) {\r
5108   case WM_KEYDOWN:\r
5109     switch (wParam) {\r
5110     case VK_LEFT:\r
5111     case VK_RIGHT:\r
5112       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5113       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5114       return TRUE;\r
5115     }\r
5116     break;\r
5117   case WM_CHAR:\r
5118     switch (wParam) {\r
5119     case '\r':\r
5120       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5121       return TRUE;\r
5122     default:\r
5123       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5124         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5125         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5126         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5127         SetFocus(h);\r
5128         SendMessage(h, WM_CHAR, wParam, lParam);\r
5129         return TRUE;\r
5130       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5131         PopUpMoveDialog((char)wParam);\r
5132       }\r
5133       break;\r
5134     }\r
5135     break;\r
5136   }\r
5137   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5138 }\r
5139 \r
5140 /* Process messages for Promotion dialog box */\r
5141 LRESULT CALLBACK\r
5142 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5143 {\r
5144   char promoChar;\r
5145 \r
5146   switch (message) {\r
5147   case WM_INITDIALOG: /* message: initialize dialog box */\r
5148     /* Center the dialog over the application window */\r
5149     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5150     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5151       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5152        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5153                SW_SHOW : SW_HIDE);\r
5154     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5155     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5156        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5157          PieceToChar(WhiteAngel) != '~') ||\r
5158         (PieceToChar(BlackAngel) >= 'A' &&\r
5159          PieceToChar(BlackAngel) != '~')   ) ?\r
5160                SW_SHOW : SW_HIDE);\r
5161     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5162        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5163          PieceToChar(WhiteMarshall) != '~') ||\r
5164         (PieceToChar(BlackMarshall) >= 'A' &&\r
5165          PieceToChar(BlackMarshall) != '~')   ) ?\r
5166                SW_SHOW : SW_HIDE);\r
5167     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5168     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5169        gameInfo.variant != VariantShogi ?\r
5170                SW_SHOW : SW_HIDE);\r
5171     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5172        gameInfo.variant != VariantShogi ?\r
5173                SW_SHOW : SW_HIDE);\r
5174     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5175        gameInfo.variant == VariantShogi ?\r
5176                SW_SHOW : SW_HIDE);\r
5177     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5178        gameInfo.variant == VariantShogi ?\r
5179                SW_SHOW : SW_HIDE);\r
5180     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5181        gameInfo.variant == VariantSuper ?\r
5182                SW_SHOW : SW_HIDE);\r
5183     return TRUE;\r
5184 \r
5185   case WM_COMMAND: /* message: received a command */\r
5186     switch (LOWORD(wParam)) {\r
5187     case IDCANCEL:\r
5188       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5189       ClearHighlights();\r
5190       DrawPosition(FALSE, NULL);\r
5191       return TRUE;\r
5192     case PB_King:\r
5193       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5194       break;\r
5195     case PB_Queen:\r
5196       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5197       break;\r
5198     case PB_Rook:\r
5199       promoChar = PieceToChar(BlackRook);\r
5200       break;\r
5201     case PB_Bishop:\r
5202       promoChar = PieceToChar(BlackBishop);\r
5203       break;\r
5204     case PB_Chancellor:\r
5205       promoChar = PieceToChar(BlackMarshall);\r
5206       break;\r
5207     case PB_Archbishop:\r
5208       promoChar = PieceToChar(BlackAngel);\r
5209       break;\r
5210     case PB_Knight:\r
5211       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5212       break;\r
5213     default:\r
5214       return FALSE;\r
5215     }\r
5216     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5217     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5218        only show the popup when we are already sure the move is valid or\r
5219        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5220        will figure out it is a promotion from the promoChar. */\r
5221     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
5222     fromX = fromY = -1;\r
5223     if (!appData.highlightLastMove) {\r
5224       ClearHighlights();\r
5225       DrawPosition(FALSE, NULL);\r
5226     }\r
5227     return TRUE;\r
5228   }\r
5229   return FALSE;\r
5230 }\r
5231 \r
5232 /* Pop up promotion dialog */\r
5233 VOID\r
5234 PromotionPopup(HWND hwnd)\r
5235 {\r
5236   FARPROC lpProc;\r
5237 \r
5238   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5239   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5240     hwnd, (DLGPROC)lpProc);\r
5241   FreeProcInstance(lpProc);\r
5242 }\r
5243 \r
5244 void\r
5245 PromotionPopUp()\r
5246 {\r
5247   DrawPosition(TRUE, NULL);\r
5248   PromotionPopup(hwndMain);\r
5249 }\r
5250 \r
5251 /* Toggle ShowThinking */\r
5252 VOID\r
5253 ToggleShowThinking()\r
5254 {\r
5255   appData.showThinking = !appData.showThinking;\r
5256   ShowThinkingEvent();\r
5257 }\r
5258 \r
5259 VOID\r
5260 LoadGameDialog(HWND hwnd, char* title)\r
5261 {\r
5262   UINT number = 0;\r
5263   FILE *f;\r
5264   char fileTitle[MSG_SIZ];\r
5265   f = OpenFileDialog(hwnd, "rb", "",\r
5266                      appData.oldSaveStyle ? "gam" : "pgn",\r
5267                      GAME_FILT,\r
5268                      title, &number, fileTitle, NULL);\r
5269   if (f != NULL) {\r
5270     cmailMsgLoaded = FALSE;\r
5271     if (number == 0) {\r
5272       int error = GameListBuild(f);\r
5273       if (error) {\r
5274         DisplayError("Cannot build game list", error);\r
5275       } else if (!ListEmpty(&gameList) &&\r
5276                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5277         GameListPopUp(f, fileTitle);\r
5278         return;\r
5279       }\r
5280       GameListDestroy();\r
5281       number = 1;\r
5282     }\r
5283     LoadGame(f, number, fileTitle, FALSE);\r
5284   }\r
5285 }\r
5286 \r
5287 int get_term_width()\r
5288 {\r
5289     HDC hdc;\r
5290     TEXTMETRIC tm;\r
5291     RECT rc;\r
5292     HFONT hfont, hold_font;\r
5293     LOGFONT lf;\r
5294     HWND hText;\r
5295 \r
5296     if (hwndConsole)\r
5297         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5298     else\r
5299         return 79;\r
5300 \r
5301     // get the text metrics\r
5302     hdc = GetDC(hText);\r
5303     lf = font[boardSize][CONSOLE_FONT]->lf;\r
5304     if (consoleCF.dwEffects & CFE_BOLD)\r
5305         lf.lfWeight = FW_BOLD;\r
5306     if (consoleCF.dwEffects & CFE_ITALIC)\r
5307         lf.lfItalic = TRUE;\r
5308     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
5309         lf.lfStrikeOut = TRUE;\r
5310     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
5311         lf.lfUnderline = TRUE;\r
5312     hfont = CreateFontIndirect(&lf);\r
5313     hold_font = SelectObject(hdc, hfont);\r
5314     GetTextMetrics(hdc, &tm);\r
5315     SelectObject(hdc, hold_font);\r
5316     DeleteObject(hfont);\r
5317     ReleaseDC(hText, hdc);\r
5318 \r
5319     // get the rectangle\r
5320     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
5321 \r
5322     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
5323 }\r
5324 \r
5325 void UpdateICSWidth(HWND hText)\r
5326 {\r
5327     LONG old_width, new_width;\r
5328 \r
5329     new_width = get_term_width(hText, FALSE);\r
5330     old_width = GetWindowLong(hText, GWL_USERDATA);\r
5331     if (new_width != old_width)\r
5332     {\r
5333         ics_update_width(new_width);\r
5334         SetWindowLong(hText, GWL_USERDATA, new_width);\r
5335     }\r
5336 }\r
5337 \r
5338 VOID\r
5339 ChangedConsoleFont()\r
5340 {\r
5341   CHARFORMAT cfmt;\r
5342   CHARRANGE tmpsel, sel;\r
5343   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5344   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5345   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5346   PARAFORMAT paraf;\r
5347 \r
5348   cfmt.cbSize = sizeof(CHARFORMAT);\r
5349   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5350   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5351   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5352    * size.  This was undocumented in the version of MSVC++ that I had\r
5353    * when I wrote the code, but is apparently documented now.\r
5354    */\r
5355   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5356   cfmt.bCharSet = f->lf.lfCharSet;\r
5357   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5358   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5359   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5360   /* Why are the following seemingly needed too? */\r
5361   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5362   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5363   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5364   tmpsel.cpMin = 0;\r
5365   tmpsel.cpMax = -1; /*999999?*/\r
5366   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5367   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5368   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5369    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5370    */\r
5371   paraf.cbSize = sizeof(paraf);\r
5372   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5373   paraf.dxStartIndent = 0;\r
5374   paraf.dxOffset = WRAP_INDENT;\r
5375   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5376   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5377   UpdateICSWidth(hText);\r
5378 }\r
5379 \r
5380 /*---------------------------------------------------------------------------*\\r
5381  *\r
5382  * Window Proc for main window\r
5383  *\r
5384 \*---------------------------------------------------------------------------*/\r
5385 \r
5386 /* Process messages for main window, etc. */\r
5387 LRESULT CALLBACK\r
5388 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5389 {\r
5390   FARPROC lpProc;\r
5391   int wmId, wmEvent;\r
5392   char *defName;\r
5393   FILE *f;\r
5394   UINT number;\r
5395   char fileTitle[MSG_SIZ];\r
5396   char buf[MSG_SIZ];\r
5397   static SnapData sd;\r
5398 \r
5399   switch (message) {\r
5400 \r
5401   case WM_PAINT: /* message: repaint portion of window */\r
5402     PaintProc(hwnd);\r
5403     break;\r
5404 \r
5405   case WM_ERASEBKGND:\r
5406     if (IsIconic(hwnd)) {\r
5407       /* Cheat; change the message */\r
5408       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5409     } else {\r
5410       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5411     }\r
5412     break;\r
5413 \r
5414   case WM_LBUTTONDOWN:\r
5415   case WM_MBUTTONDOWN:\r
5416   case WM_RBUTTONDOWN:\r
5417   case WM_LBUTTONUP:\r
5418   case WM_MBUTTONUP:\r
5419   case WM_RBUTTONUP:\r
5420   case WM_MOUSEMOVE:\r
5421   case WM_MOUSEWHEEL:\r
5422     MouseEvent(hwnd, message, wParam, lParam);\r
5423     break;\r
5424 \r
5425   JAWS_KB_NAVIGATION\r
5426 \r
5427   case WM_CHAR:\r
5428     \r
5429     JAWS_ALT_INTERCEPT\r
5430 \r
5431     if (appData.icsActive && ((char)wParam == '\r' || (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9'))) { \r
5432         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5433         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5434         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5435         SetFocus(h);\r
5436         SendMessage(h, message, wParam, lParam);\r
5437     } else if(lParam != KF_REPEAT) {\r
5438         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5439                 PopUpMoveDialog((char)wParam);\r
5440         } else if((char)wParam == 003) CopyGameToClipboard();\r
5441          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5442     }\r
5443 \r
5444     break;\r
5445 \r
5446   case WM_PALETTECHANGED:\r
5447     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5448       int nnew;\r
5449       HDC hdc = GetDC(hwndMain);\r
5450       SelectPalette(hdc, hPal, TRUE);\r
5451       nnew = RealizePalette(hdc);\r
5452       if (nnew > 0) {\r
5453         paletteChanged = TRUE;\r
5454         InvalidateRect(hwnd, &boardRect, FALSE);\r
5455       }\r
5456       ReleaseDC(hwnd, hdc);\r
5457     }\r
5458     break;\r
5459 \r
5460   case WM_QUERYNEWPALETTE:\r
5461     if (!appData.monoMode /*&& paletteChanged*/) {\r
5462       int nnew;\r
5463       HDC hdc = GetDC(hwndMain);\r
5464       paletteChanged = FALSE;\r
5465       SelectPalette(hdc, hPal, FALSE);\r
5466       nnew = RealizePalette(hdc);\r
5467       if (nnew > 0) {\r
5468         InvalidateRect(hwnd, &boardRect, FALSE);\r
5469       }\r
5470       ReleaseDC(hwnd, hdc);\r
5471       return TRUE;\r
5472     }\r
5473     return FALSE;\r
5474 \r
5475   case WM_COMMAND: /* message: command from application menu */\r
5476     wmId    = LOWORD(wParam);\r
5477     wmEvent = HIWORD(wParam);\r
5478 \r
5479     switch (wmId) {\r
5480     case IDM_NewGame:\r
5481       ResetGameEvent();\r
5482       SAY("new game enter a move to play against the computer with white");\r
5483       break;\r
5484 \r
5485     case IDM_NewGameFRC:\r
5486       if( NewGameFRC() == 0 ) {\r
5487         ResetGameEvent();\r
5488       }\r
5489       break;\r
5490 \r
5491     case IDM_NewVariant:\r
5492       NewVariantPopup(hwnd);\r
5493       break;\r
5494 \r
5495     case IDM_LoadGame:\r
5496       LoadGameDialog(hwnd, "Load Game from File");\r
5497       break;\r
5498 \r
5499     case IDM_LoadNextGame:\r
5500       ReloadGame(1);\r
5501       break;\r
5502 \r
5503     case IDM_LoadPrevGame:\r
5504       ReloadGame(-1);\r
5505       break;\r
5506 \r
5507     case IDM_ReloadGame:\r
5508       ReloadGame(0);\r
5509       break;\r
5510 \r
5511     case IDM_LoadPosition:\r
5512       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5513         Reset(FALSE, TRUE);\r
5514       }\r
5515       number = 1;\r
5516       f = OpenFileDialog(hwnd, "rb", "",\r
5517                          appData.oldSaveStyle ? "pos" : "fen",\r
5518                          POSITION_FILT,\r
5519                          "Load Position from File", &number, fileTitle, NULL);\r
5520       if (f != NULL) {\r
5521         LoadPosition(f, number, fileTitle);\r
5522       }\r
5523       break;\r
5524 \r
5525     case IDM_LoadNextPosition:\r
5526       ReloadPosition(1);\r
5527       break;\r
5528 \r
5529     case IDM_LoadPrevPosition:\r
5530       ReloadPosition(-1);\r
5531       break;\r
5532 \r
5533     case IDM_ReloadPosition:\r
5534       ReloadPosition(0);\r
5535       break;\r
5536 \r
5537     case IDM_SaveGame:\r
5538       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5539       f = OpenFileDialog(hwnd, "a", defName,\r
5540                          appData.oldSaveStyle ? "gam" : "pgn",\r
5541                          GAME_FILT,\r
5542                          "Save Game to File", NULL, fileTitle, NULL);\r
5543       if (f != NULL) {\r
5544         SaveGame(f, 0, "");\r
5545       }\r
5546       break;\r
5547 \r
5548     case IDM_SavePosition:\r
5549       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5550       f = OpenFileDialog(hwnd, "a", defName,\r
5551                          appData.oldSaveStyle ? "pos" : "fen",\r
5552                          POSITION_FILT,\r
5553                          "Save Position to File", NULL, fileTitle, NULL);\r
5554       if (f != NULL) {\r
5555         SavePosition(f, 0, "");\r
5556       }\r
5557       break;\r
5558 \r
5559     case IDM_SaveDiagram:\r
5560       defName = "diagram";\r
5561       f = OpenFileDialog(hwnd, "wb", defName,\r
5562                          "bmp",\r
5563                          DIAGRAM_FILT,\r
5564                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5565       if (f != NULL) {\r
5566         SaveDiagram(f);\r
5567       }\r
5568       break;\r
5569 \r
5570     case IDM_CopyGame:\r
5571       CopyGameToClipboard();\r
5572       break;\r
5573 \r
5574     case IDM_PasteGame:\r
5575       PasteGameFromClipboard();\r
5576       break;\r
5577 \r
5578     case IDM_CopyGameListToClipboard:\r
5579       CopyGameListToClipboard();\r
5580       break;\r
5581 \r
5582     /* [AS] Autodetect FEN or PGN data */\r
5583     case IDM_PasteAny:\r
5584       PasteGameOrFENFromClipboard();\r
5585       break;\r
5586 \r
5587     /* [AS] Move history */\r
5588     case IDM_ShowMoveHistory:\r
5589         if( MoveHistoryIsUp() ) {\r
5590             MoveHistoryPopDown();\r
5591         }\r
5592         else {\r
5593             MoveHistoryPopUp();\r
5594         }\r
5595         break;\r
5596 \r
5597     /* [AS] Eval graph */\r
5598     case IDM_ShowEvalGraph:\r
5599         if( EvalGraphIsUp() ) {\r
5600             EvalGraphPopDown();\r
5601         }\r
5602         else {\r
5603             EvalGraphPopUp();\r
5604             SetFocus(hwndMain);\r
5605         }\r
5606         break;\r
5607 \r
5608     /* [AS] Engine output */\r
5609     case IDM_ShowEngineOutput:\r
5610         if( EngineOutputIsUp() ) {\r
5611             EngineOutputPopDown();\r
5612         }\r
5613         else {\r
5614             EngineOutputPopUp();\r
5615         }\r
5616         break;\r
5617 \r
5618     /* [AS] User adjudication */\r
5619     case IDM_UserAdjudication_White:\r
5620         UserAdjudicationEvent( +1 );\r
5621         break;\r
5622 \r
5623     case IDM_UserAdjudication_Black:\r
5624         UserAdjudicationEvent( -1 );\r
5625         break;\r
5626 \r
5627     case IDM_UserAdjudication_Draw:\r
5628         UserAdjudicationEvent( 0 );\r
5629         break;\r
5630 \r
5631     /* [AS] Game list options dialog */\r
5632     case IDM_GameListOptions:\r
5633       GameListOptions();\r
5634       break;\r
5635 \r
5636     case IDM_NewChat:\r
5637       ChatPopUp();\r
5638       break;\r
5639 \r
5640     case IDM_CopyPosition:\r
5641       CopyFENToClipboard();\r
5642       break;\r
5643 \r
5644     case IDM_PastePosition:\r
5645       PasteFENFromClipboard();\r
5646       break;\r
5647 \r
5648     case IDM_MailMove:\r
5649       MailMoveEvent();\r
5650       break;\r
5651 \r
5652     case IDM_ReloadCMailMsg:\r
5653       Reset(TRUE, TRUE);\r
5654       ReloadCmailMsgEvent(FALSE);\r
5655       break;\r
5656 \r
5657     case IDM_Minimize:\r
5658       ShowWindow(hwnd, SW_MINIMIZE);\r
5659       break;\r
5660 \r
5661     case IDM_Exit:\r
5662       ExitEvent(0);\r
5663       break;\r
5664 \r
5665     case IDM_MachineWhite:\r
5666       MachineWhiteEvent();\r
5667       /*\r
5668        * refresh the tags dialog only if it's visible\r
5669        */\r
5670       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5671           char *tags;\r
5672           tags = PGNTags(&gameInfo);\r
5673           TagsPopUp(tags, CmailMsg());\r
5674           free(tags);\r
5675       }\r
5676       SAY("computer starts playing white");\r
5677       break;\r
5678 \r
5679     case IDM_MachineBlack:\r
5680       MachineBlackEvent();\r
5681       /*\r
5682        * refresh the tags dialog only if it's visible\r
5683        */\r
5684       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5685           char *tags;\r
5686           tags = PGNTags(&gameInfo);\r
5687           TagsPopUp(tags, CmailMsg());\r
5688           free(tags);\r
5689       }\r
5690       SAY("computer starts playing black");\r
5691       break;\r
5692 \r
5693     case IDM_TwoMachines:\r
5694       TwoMachinesEvent();\r
5695       /*\r
5696        * refresh the tags dialog only if it's visible\r
5697        */\r
5698       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5699           char *tags;\r
5700           tags = PGNTags(&gameInfo);\r
5701           TagsPopUp(tags, CmailMsg());\r
5702           free(tags);\r
5703       }\r
5704       SAY("programs start playing each other");\r
5705       break;\r
5706 \r
5707     case IDM_AnalysisMode:\r
5708       if (!first.analysisSupport) {\r
5709         sprintf(buf, "%s does not support analysis", first.tidy);\r
5710         DisplayError(buf, 0);\r
5711       } else {\r
5712         SAY("analyzing current position");\r
5713         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5714         if (appData.icsActive) {\r
5715                if (gameMode != IcsObserving) {\r
5716                        sprintf(buf, "You are not observing a game");\r
5717                        DisplayError(buf, 0);\r
5718                        /* secure check */\r
5719                        if (appData.icsEngineAnalyze) {\r
5720                                if (appData.debugMode) \r
5721                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5722                                ExitAnalyzeMode();\r
5723                                ModeHighlight();\r
5724                                break;\r
5725                        }\r
5726                        break;\r
5727                } else {\r
5728                        /* if enable, user want disable icsEngineAnalyze */\r
5729                        if (appData.icsEngineAnalyze) {\r
5730                                ExitAnalyzeMode();\r
5731                                ModeHighlight();\r
5732                                break;\r
5733                        }\r
5734                        appData.icsEngineAnalyze = TRUE;\r
5735                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5736                }\r
5737         } \r
5738         if (!appData.showThinking) ToggleShowThinking();\r
5739         AnalyzeModeEvent();\r
5740       }\r
5741       break;\r
5742 \r
5743     case IDM_AnalyzeFile:\r
5744       if (!first.analysisSupport) {\r
5745         char buf[MSG_SIZ];\r
5746         sprintf(buf, "%s does not support analysis", first.tidy);\r
5747         DisplayError(buf, 0);\r
5748       } else {\r
5749         if (!appData.showThinking) ToggleShowThinking();\r
5750         AnalyzeFileEvent();\r
5751         LoadGameDialog(hwnd, "Analyze Game from File");\r
5752         AnalysisPeriodicEvent(1);\r
5753       }\r
5754       break;\r
5755 \r
5756     case IDM_IcsClient:\r
5757       IcsClientEvent();\r
5758       break;\r
5759 \r
5760     case IDM_EditGame:\r
5761       EditGameEvent();\r
5762       SAY("edit game");\r
5763       break;\r
5764 \r
5765     case IDM_EditPosition:\r
5766       EditPositionEvent();\r
5767       SAY("to set up a position type a FEN");\r
5768       break;\r
5769 \r
5770     case IDM_Training:\r
5771       TrainingEvent();\r
5772       break;\r
5773 \r
5774     case IDM_ShowGameList:\r
5775       ShowGameListProc();\r
5776       break;\r
5777 \r
5778     case IDM_EditTags:\r
5779       EditTagsProc();\r
5780       break;\r
5781 \r
5782     case IDM_EditComment:\r
5783       if (commentDialogUp && editComment) {\r
5784         CommentPopDown();\r
5785       } else {\r
5786         EditCommentEvent();\r
5787       }\r
5788       break;\r
5789 \r
5790     case IDM_Pause:\r
5791       PauseEvent();\r
5792       break;\r
5793 \r
5794     case IDM_Accept:\r
5795       AcceptEvent();\r
5796       break;\r
5797 \r
5798     case IDM_Decline:\r
5799       DeclineEvent();\r
5800       break;\r
5801 \r
5802     case IDM_Rematch:\r
5803       RematchEvent();\r
5804       break;\r
5805 \r
5806     case IDM_CallFlag:\r
5807       CallFlagEvent();\r
5808       break;\r
5809 \r
5810     case IDM_Draw:\r
5811       DrawEvent();\r
5812       break;\r
5813 \r
5814     case IDM_Adjourn:\r
5815       AdjournEvent();\r
5816       break;\r
5817 \r
5818     case IDM_Abort:\r
5819       AbortEvent();\r
5820       break;\r
5821 \r
5822     case IDM_Resign:\r
5823       ResignEvent();\r
5824       break;\r
5825 \r
5826     case IDM_StopObserving:\r
5827       StopObservingEvent();\r
5828       break;\r
5829 \r
5830     case IDM_StopExamining:\r
5831       StopExaminingEvent();\r
5832       break;\r
5833 \r
5834     case IDM_TypeInMove:\r
5835       PopUpMoveDialog('\000');\r
5836       break;\r
5837 \r
5838     case IDM_TypeInName:\r
5839       PopUpNameDialog('\000');\r
5840       break;\r
5841 \r
5842     case IDM_Backward:\r
5843       BackwardEvent();\r
5844       SetFocus(hwndMain);\r
5845       break;\r
5846 \r
5847     JAWS_MENU_ITEMS\r
5848 \r
5849     case IDM_Forward:\r
5850       ForwardEvent();\r
5851       SetFocus(hwndMain);\r
5852       break;\r
5853 \r
5854     case IDM_ToStart:\r
5855       ToStartEvent();\r
5856       SetFocus(hwndMain);\r
5857       break;\r
5858 \r
5859     case IDM_ToEnd:\r
5860       ToEndEvent();\r
5861       SetFocus(hwndMain);\r
5862       break;\r
5863 \r
5864     case IDM_Revert:\r
5865       RevertEvent();\r
5866       break;\r
5867 \r
5868     case IDM_TruncateGame:\r
5869       TruncateGameEvent();\r
5870       break;\r
5871 \r
5872     case IDM_MoveNow:\r
5873       MoveNowEvent();\r
5874       break;\r
5875 \r
5876     case IDM_RetractMove:\r
5877       RetractMoveEvent();\r
5878       break;\r
5879 \r
5880     case IDM_FlipView:\r
5881       flipView = !flipView;\r
5882       DrawPosition(FALSE, NULL);\r
5883       break;\r
5884 \r
5885     case IDM_FlipClock:\r
5886       flipClock = !flipClock;\r
5887       DisplayBothClocks();\r
5888       DrawPosition(FALSE, NULL);\r
5889       break;\r
5890 \r
5891     case IDM_MuteSounds:\r
5892       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5893       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5894                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5895       break;\r
5896 \r
5897     case IDM_GeneralOptions:\r
5898       GeneralOptionsPopup(hwnd);\r
5899       DrawPosition(TRUE, NULL);\r
5900       break;\r
5901 \r
5902     case IDM_BoardOptions:\r
5903       BoardOptionsPopup(hwnd);\r
5904       break;\r
5905 \r
5906     case IDM_EnginePlayOptions:\r
5907       EnginePlayOptionsPopup(hwnd);\r
5908       break;\r
5909 \r
5910     case IDM_Engine1Options:\r
5911       EngineOptionsPopup(hwnd, &first);\r
5912       break;\r
5913 \r
5914     case IDM_Engine2Options:\r
5915       EngineOptionsPopup(hwnd, &second);\r
5916       break;\r
5917 \r
5918     case IDM_OptionsUCI:\r
5919       UciOptionsPopup(hwnd);\r
5920       break;\r
5921 \r
5922     case IDM_IcsOptions:\r
5923       IcsOptionsPopup(hwnd);\r
5924       break;\r
5925 \r
5926     case IDM_Fonts:\r
5927       FontsOptionsPopup(hwnd);\r
5928       break;\r
5929 \r
5930     case IDM_Sounds:\r
5931       SoundOptionsPopup(hwnd);\r
5932       break;\r
5933 \r
5934     case IDM_CommPort:\r
5935       CommPortOptionsPopup(hwnd);\r
5936       break;\r
5937 \r
5938     case IDM_LoadOptions:\r
5939       LoadOptionsPopup(hwnd);\r
5940       break;\r
5941 \r
5942     case IDM_SaveOptions:\r
5943       SaveOptionsPopup(hwnd);\r
5944       break;\r
5945 \r
5946     case IDM_TimeControl:\r
5947       TimeControlOptionsPopup(hwnd);\r
5948       break;\r
5949 \r
5950     case IDM_SaveSettings:\r
5951       SaveSettings(settingsFileName);\r
5952       break;\r
5953 \r
5954     case IDM_SaveSettingsOnExit:\r
5955       saveSettingsOnExit = !saveSettingsOnExit;\r
5956       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5957                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5958                                          MF_CHECKED : MF_UNCHECKED));\r
5959       break;\r
5960 \r
5961     case IDM_Hint:\r
5962       HintEvent();\r
5963       break;\r
5964 \r
5965     case IDM_Book:\r
5966       BookEvent();\r
5967       break;\r
5968 \r
5969     case IDM_AboutGame:\r
5970       AboutGameEvent();\r
5971       break;\r
5972 \r
5973     case IDM_Debug:\r
5974       appData.debugMode = !appData.debugMode;\r
5975       if (appData.debugMode) {\r
5976         char dir[MSG_SIZ];\r
5977         GetCurrentDirectory(MSG_SIZ, dir);\r
5978         SetCurrentDirectory(installDir);\r
5979         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5980         SetCurrentDirectory(dir);\r
5981         setbuf(debugFP, NULL);\r
5982       } else {\r
5983         fclose(debugFP);\r
5984         debugFP = NULL;\r
5985       }\r
5986       break;\r
5987 \r
5988     case IDM_HELPCONTENTS:\r
5989       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5990           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5991           MessageBox (GetFocus(),\r
5992                     "Unable to activate help",\r
5993                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5994       }\r
5995       break;\r
5996 \r
5997     case IDM_HELPSEARCH:\r
5998         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5999             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6000         MessageBox (GetFocus(),\r
6001                     "Unable to activate help",\r
6002                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6003       }\r
6004       break;\r
6005 \r
6006     case IDM_HELPHELP:\r
6007       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6008         MessageBox (GetFocus(),\r
6009                     "Unable to activate help",\r
6010                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6011       }\r
6012       break;\r
6013 \r
6014     case IDM_ABOUT:\r
6015       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6016       DialogBox(hInst, \r
6017         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6018         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6019       FreeProcInstance(lpProc);\r
6020       break;\r
6021 \r
6022     case IDM_DirectCommand1:\r
6023       AskQuestionEvent("Direct Command",\r
6024                        "Send to chess program:", "", "1");\r
6025       break;\r
6026     case IDM_DirectCommand2:\r
6027       AskQuestionEvent("Direct Command",\r
6028                        "Send to second chess program:", "", "2");\r
6029       break;\r
6030 \r
6031     case EP_WhitePawn:\r
6032       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6033       fromX = fromY = -1;\r
6034       break;\r
6035 \r
6036     case EP_WhiteKnight:\r
6037       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6038       fromX = fromY = -1;\r
6039       break;\r
6040 \r
6041     case EP_WhiteBishop:\r
6042       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6043       fromX = fromY = -1;\r
6044       break;\r
6045 \r
6046     case EP_WhiteRook:\r
6047       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6048       fromX = fromY = -1;\r
6049       break;\r
6050 \r
6051     case EP_WhiteQueen:\r
6052       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6053       fromX = fromY = -1;\r
6054       break;\r
6055 \r
6056     case EP_WhiteFerz:\r
6057       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6058       fromX = fromY = -1;\r
6059       break;\r
6060 \r
6061     case EP_WhiteWazir:\r
6062       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6063       fromX = fromY = -1;\r
6064       break;\r
6065 \r
6066     case EP_WhiteAlfil:\r
6067       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6068       fromX = fromY = -1;\r
6069       break;\r
6070 \r
6071     case EP_WhiteCannon:\r
6072       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6073       fromX = fromY = -1;\r
6074       break;\r
6075 \r
6076     case EP_WhiteCardinal:\r
6077       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6078       fromX = fromY = -1;\r
6079       break;\r
6080 \r
6081     case EP_WhiteMarshall:\r
6082       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6083       fromX = fromY = -1;\r
6084       break;\r
6085 \r
6086     case EP_WhiteKing:\r
6087       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6088       fromX = fromY = -1;\r
6089       break;\r
6090 \r
6091     case EP_BlackPawn:\r
6092       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6093       fromX = fromY = -1;\r
6094       break;\r
6095 \r
6096     case EP_BlackKnight:\r
6097       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6098       fromX = fromY = -1;\r
6099       break;\r
6100 \r
6101     case EP_BlackBishop:\r
6102       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6103       fromX = fromY = -1;\r
6104       break;\r
6105 \r
6106     case EP_BlackRook:\r
6107       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6108       fromX = fromY = -1;\r
6109       break;\r
6110 \r
6111     case EP_BlackQueen:\r
6112       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6113       fromX = fromY = -1;\r
6114       break;\r
6115 \r
6116     case EP_BlackFerz:\r
6117       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6118       fromX = fromY = -1;\r
6119       break;\r
6120 \r
6121     case EP_BlackWazir:\r
6122       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6123       fromX = fromY = -1;\r
6124       break;\r
6125 \r
6126     case EP_BlackAlfil:\r
6127       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6128       fromX = fromY = -1;\r
6129       break;\r
6130 \r
6131     case EP_BlackCannon:\r
6132       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6133       fromX = fromY = -1;\r
6134       break;\r
6135 \r
6136     case EP_BlackCardinal:\r
6137       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6138       fromX = fromY = -1;\r
6139       break;\r
6140 \r
6141     case EP_BlackMarshall:\r
6142       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6143       fromX = fromY = -1;\r
6144       break;\r
6145 \r
6146     case EP_BlackKing:\r
6147       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6148       fromX = fromY = -1;\r
6149       break;\r
6150 \r
6151     case EP_EmptySquare:\r
6152       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6153       fromX = fromY = -1;\r
6154       break;\r
6155 \r
6156     case EP_ClearBoard:\r
6157       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6158       fromX = fromY = -1;\r
6159       break;\r
6160 \r
6161     case EP_White:\r
6162       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6163       fromX = fromY = -1;\r
6164       break;\r
6165 \r
6166     case EP_Black:\r
6167       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6168       fromX = fromY = -1;\r
6169       break;\r
6170 \r
6171     case EP_Promote:\r
6172       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6173       fromX = fromY = -1;\r
6174       break;\r
6175 \r
6176     case EP_Demote:\r
6177       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6178       fromX = fromY = -1;\r
6179       break;\r
6180 \r
6181     case DP_Pawn:\r
6182       DropMenuEvent(WhitePawn, fromX, fromY);\r
6183       fromX = fromY = -1;\r
6184       break;\r
6185 \r
6186     case DP_Knight:\r
6187       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6188       fromX = fromY = -1;\r
6189       break;\r
6190 \r
6191     case DP_Bishop:\r
6192       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6193       fromX = fromY = -1;\r
6194       break;\r
6195 \r
6196     case DP_Rook:\r
6197       DropMenuEvent(WhiteRook, fromX, fromY);\r
6198       fromX = fromY = -1;\r
6199       break;\r
6200 \r
6201     case DP_Queen:\r
6202       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6203       fromX = fromY = -1;\r
6204       break;\r
6205 \r
6206     default:\r
6207       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6208     }\r
6209     break;\r
6210 \r
6211   case WM_TIMER:\r
6212     switch (wParam) {\r
6213     case CLOCK_TIMER_ID:\r
6214       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6215       clockTimerEvent = 0;\r
6216       DecrementClocks(); /* call into back end */\r
6217       break;\r
6218     case LOAD_GAME_TIMER_ID:\r
6219       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6220       loadGameTimerEvent = 0;\r
6221       AutoPlayGameLoop(); /* call into back end */\r
6222       break;\r
6223     case ANALYSIS_TIMER_ID:\r
6224       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6225                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6226         AnalysisPeriodicEvent(0);\r
6227       } else {\r
6228         KillTimer(hwnd, analysisTimerEvent);\r
6229         analysisTimerEvent = 0;\r
6230       }\r
6231       break;\r
6232     case DELAYED_TIMER_ID:\r
6233       KillTimer(hwnd, delayedTimerEvent);\r
6234       delayedTimerEvent = 0;\r
6235       delayedTimerCallback();\r
6236       break;\r
6237     }\r
6238     break;\r
6239 \r
6240   case WM_USER_Input:\r
6241     InputEvent(hwnd, message, wParam, lParam);\r
6242     break;\r
6243 \r
6244   /* [AS] Also move "attached" child windows */\r
6245   case WM_WINDOWPOSCHANGING:\r
6246 \r
6247     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6248         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6249 \r
6250         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6251             /* Window is moving */\r
6252             RECT rcMain;\r
6253 \r
6254 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6255             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6256             rcMain.right  = boardX + winWidth;\r
6257             rcMain.top    = boardY;\r
6258             rcMain.bottom = boardY + winHeight;\r
6259             \r
6260             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6261             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6262             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6263             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6264             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6265             boardX = lpwp->x;\r
6266             boardY = lpwp->y;\r
6267         }\r
6268     }\r
6269     break;\r
6270 \r
6271   /* [AS] Snapping */\r
6272   case WM_ENTERSIZEMOVE:\r
6273     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6274     if (hwnd == hwndMain) {\r
6275       doingSizing = TRUE;\r
6276       lastSizing = 0;\r
6277     }\r
6278     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6279     break;\r
6280 \r
6281   case WM_SIZING:\r
6282     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6283     if (hwnd == hwndMain) {\r
6284       lastSizing = wParam;\r
6285     }\r
6286     break;\r
6287 \r
6288   case WM_MOVING:\r
6289     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6290       return OnMoving( &sd, hwnd, wParam, lParam );\r
6291 \r
6292   case WM_EXITSIZEMOVE:\r
6293     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6294     if (hwnd == hwndMain) {\r
6295       RECT client;\r
6296       doingSizing = FALSE;\r
6297       InvalidateRect(hwnd, &boardRect, FALSE);\r
6298       GetClientRect(hwnd, &client);\r
6299       ResizeBoard(client.right, client.bottom, lastSizing);\r
6300       lastSizing = 0;\r
6301       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6302     }\r
6303     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6304     break;\r
6305 \r
6306   case WM_DESTROY: /* message: window being destroyed */\r
6307     PostQuitMessage(0);\r
6308     break;\r
6309 \r
6310   case WM_CLOSE:\r
6311     if (hwnd == hwndMain) {\r
6312       ExitEvent(0);\r
6313     }\r
6314     break;\r
6315 \r
6316   default:      /* Passes it on if unprocessed */\r
6317     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6318   }\r
6319   return 0;\r
6320 }\r
6321 \r
6322 /*---------------------------------------------------------------------------*\\r
6323  *\r
6324  * Misc utility routines\r
6325  *\r
6326 \*---------------------------------------------------------------------------*/\r
6327 \r
6328 /*\r
6329  * Decent random number generator, at least not as bad as Windows\r
6330  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6331  */\r
6332 unsigned int randstate;\r
6333 \r
6334 int\r
6335 myrandom(void)\r
6336 {\r
6337   randstate = randstate * 1664525 + 1013904223;\r
6338   return (int) randstate & 0x7fffffff;\r
6339 }\r
6340 \r
6341 void\r
6342 mysrandom(unsigned int seed)\r
6343 {\r
6344   randstate = seed;\r
6345 }\r
6346 \r
6347 \r
6348 /* \r
6349  * returns TRUE if user selects a different color, FALSE otherwise \r
6350  */\r
6351 \r
6352 BOOL\r
6353 ChangeColor(HWND hwnd, COLORREF *which)\r
6354 {\r
6355   static BOOL firstTime = TRUE;\r
6356   static DWORD customColors[16];\r
6357   CHOOSECOLOR cc;\r
6358   COLORREF newcolor;\r
6359   int i;\r
6360   ColorClass ccl;\r
6361 \r
6362   if (firstTime) {\r
6363     /* Make initial colors in use available as custom colors */\r
6364     /* Should we put the compiled-in defaults here instead? */\r
6365     i = 0;\r
6366     customColors[i++] = lightSquareColor & 0xffffff;\r
6367     customColors[i++] = darkSquareColor & 0xffffff;\r
6368     customColors[i++] = whitePieceColor & 0xffffff;\r
6369     customColors[i++] = blackPieceColor & 0xffffff;\r
6370     customColors[i++] = highlightSquareColor & 0xffffff;\r
6371     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6372 \r
6373     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6374       customColors[i++] = textAttribs[ccl].color;\r
6375     }\r
6376     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6377     firstTime = FALSE;\r
6378   }\r
6379 \r
6380   cc.lStructSize = sizeof(cc);\r
6381   cc.hwndOwner = hwnd;\r
6382   cc.hInstance = NULL;\r
6383   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6384   cc.lpCustColors = (LPDWORD) customColors;\r
6385   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6386 \r
6387   if (!ChooseColor(&cc)) return FALSE;\r
6388 \r
6389   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6390   if (newcolor == *which) return FALSE;\r
6391   *which = newcolor;\r
6392   return TRUE;\r
6393 \r
6394   /*\r
6395   InitDrawingColors();\r
6396   InvalidateRect(hwnd, &boardRect, FALSE);\r
6397   */\r
6398 }\r
6399 \r
6400 BOOLEAN\r
6401 MyLoadSound(MySound *ms)\r
6402 {\r
6403   BOOL ok = FALSE;\r
6404   struct stat st;\r
6405   FILE *f;\r
6406 \r
6407   if (ms->data) free(ms->data);\r
6408   ms->data = NULL;\r
6409 \r
6410   switch (ms->name[0]) {\r
6411   case NULLCHAR:\r
6412     /* Silence */\r
6413     ok = TRUE;\r
6414     break;\r
6415   case '$':\r
6416     /* System sound from Control Panel.  Don't preload here. */\r
6417     ok = TRUE;\r
6418     break;\r
6419   case '!':\r
6420     if (ms->name[1] == NULLCHAR) {\r
6421       /* "!" alone = silence */\r
6422       ok = TRUE;\r
6423     } else {\r
6424       /* Builtin wave resource.  Error if not found. */\r
6425       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6426       if (h == NULL) break;\r
6427       ms->data = (void *)LoadResource(hInst, h);\r
6428       if (h == NULL) break;\r
6429       ok = TRUE;\r
6430     }\r
6431     break;\r
6432   default:\r
6433     /* .wav file.  Error if not found. */\r
6434     f = fopen(ms->name, "rb");\r
6435     if (f == NULL) break;\r
6436     if (fstat(fileno(f), &st) < 0) break;\r
6437     ms->data = malloc(st.st_size);\r
6438     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6439     fclose(f);\r
6440     ok = TRUE;\r
6441     break;\r
6442   }\r
6443   if (!ok) {\r
6444     char buf[MSG_SIZ];\r
6445     sprintf(buf, "Error loading sound %s", ms->name);\r
6446     DisplayError(buf, GetLastError());\r
6447   }\r
6448   return ok;\r
6449 }\r
6450 \r
6451 BOOLEAN\r
6452 MyPlaySound(MySound *ms)\r
6453 {\r
6454   BOOLEAN ok = FALSE;\r
6455 \r
6456   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6457   switch (ms->name[0]) {\r
6458   case NULLCHAR:\r
6459         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6460     /* Silence */\r
6461     ok = TRUE;\r
6462     break;\r
6463   case '$':\r
6464     /* System sound from Control Panel (deprecated feature).\r
6465        "$" alone or an unset sound name gets default beep (still in use). */\r
6466     if (ms->name[1]) {\r
6467       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6468     }\r
6469     if (!ok) ok = MessageBeep(MB_OK);\r
6470     break; \r
6471   case '!':\r
6472     /* Builtin wave resource, or "!" alone for silence */\r
6473     if (ms->name[1]) {\r
6474       if (ms->data == NULL) return FALSE;\r
6475       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6476     } else {\r
6477       ok = TRUE;\r
6478     }\r
6479     break;\r
6480   default:\r
6481     /* .wav file.  Error if not found. */\r
6482     if (ms->data == NULL) return FALSE;\r
6483     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6484     break;\r
6485   }\r
6486   /* Don't print an error: this can happen innocently if the sound driver\r
6487      is busy; for instance, if another instance of WinBoard is playing\r
6488      a sound at about the same time. */\r
6489   return ok;\r
6490 }\r
6491 \r
6492 \r
6493 LRESULT CALLBACK\r
6494 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6495 {\r
6496   BOOL ok;\r
6497   OPENFILENAME *ofn;\r
6498   static UINT *number; /* gross that this is static */\r
6499 \r
6500   switch (message) {\r
6501   case WM_INITDIALOG: /* message: initialize dialog box */\r
6502     /* Center the dialog over the application window */\r
6503     ofn = (OPENFILENAME *) lParam;\r
6504     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6505       number = (UINT *) ofn->lCustData;\r
6506       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6507     } else {\r
6508       number = NULL;\r
6509     }\r
6510     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6511     return FALSE;  /* Allow for further processing */\r
6512 \r
6513   case WM_COMMAND:\r
6514     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6515       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6516     }\r
6517     return FALSE;  /* Allow for further processing */\r
6518   }\r
6519   return FALSE;\r
6520 }\r
6521 \r
6522 UINT APIENTRY\r
6523 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6524 {\r
6525   static UINT *number;\r
6526   OPENFILENAME *ofname;\r
6527   OFNOTIFY *ofnot;\r
6528   switch (uiMsg) {\r
6529   case WM_INITDIALOG:\r
6530     ofname = (OPENFILENAME *)lParam;\r
6531     number = (UINT *)(ofname->lCustData);\r
6532     break;\r
6533   case WM_NOTIFY:\r
6534     ofnot = (OFNOTIFY *)lParam;\r
6535     if (ofnot->hdr.code == CDN_FILEOK) {\r
6536       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6537     }\r
6538     break;\r
6539   }\r
6540   return 0;\r
6541 }\r
6542 \r
6543 \r
6544 FILE *\r
6545 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6546                char *nameFilt, char *dlgTitle, UINT *number,\r
6547                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6548 {\r
6549   OPENFILENAME openFileName;\r
6550   char buf1[MSG_SIZ];\r
6551   FILE *f;\r
6552 \r
6553   if (fileName == NULL) fileName = buf1;\r
6554   if (defName == NULL) {\r
6555     strcpy(fileName, "*.");\r
6556     strcat(fileName, defExt);\r
6557   } else {\r
6558     strcpy(fileName, defName);\r
6559   }\r
6560   if (fileTitle) strcpy(fileTitle, "");\r
6561   if (number) *number = 0;\r
6562 \r
6563   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6564   openFileName.hwndOwner         = hwnd;\r
6565   openFileName.hInstance         = (HANDLE) hInst;\r
6566   openFileName.lpstrFilter       = nameFilt;\r
6567   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6568   openFileName.nMaxCustFilter    = 0L;\r
6569   openFileName.nFilterIndex      = 1L;\r
6570   openFileName.lpstrFile         = fileName;\r
6571   openFileName.nMaxFile          = MSG_SIZ;\r
6572   openFileName.lpstrFileTitle    = fileTitle;\r
6573   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6574   openFileName.lpstrInitialDir   = NULL;\r
6575   openFileName.lpstrTitle        = dlgTitle;\r
6576   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6577     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6578     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6579     | (oldDialog ? 0 : OFN_EXPLORER);\r
6580   openFileName.nFileOffset       = 0;\r
6581   openFileName.nFileExtension    = 0;\r
6582   openFileName.lpstrDefExt       = defExt;\r
6583   openFileName.lCustData         = (LONG) number;\r
6584   openFileName.lpfnHook          = oldDialog ?\r
6585     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6586   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6587 \r
6588   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6589                         GetOpenFileName(&openFileName)) {\r
6590     /* open the file */\r
6591     f = fopen(openFileName.lpstrFile, write);\r
6592     if (f == NULL) {\r
6593       MessageBox(hwnd, "File open failed", NULL,\r
6594                  MB_OK|MB_ICONEXCLAMATION);\r
6595       return NULL;\r
6596     }\r
6597   } else {\r
6598     int err = CommDlgExtendedError();\r
6599     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6600     return FALSE;\r
6601   }\r
6602   return f;\r
6603 }\r
6604 \r
6605 \r
6606 \r
6607 VOID APIENTRY\r
6608 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6609 {\r
6610   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6611 \r
6612   /*\r
6613    * Get the first pop-up menu in the menu template. This is the\r
6614    * menu that TrackPopupMenu displays.\r
6615    */\r
6616   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6617 \r
6618   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6619 \r
6620   /*\r
6621    * TrackPopup uses screen coordinates, so convert the\r
6622    * coordinates of the mouse click to screen coordinates.\r
6623    */\r
6624   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6625 \r
6626   /* Draw and track the floating pop-up menu. */\r
6627   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6628                  pt.x, pt.y, 0, hwnd, NULL);\r
6629 \r
6630   /* Destroy the menu.*/\r
6631   DestroyMenu(hmenu);\r
6632 }\r
6633    \r
6634 typedef struct {\r
6635   HWND hDlg, hText;\r
6636   int sizeX, sizeY, newSizeX, newSizeY;\r
6637   HDWP hdwp;\r
6638 } ResizeEditPlusButtonsClosure;\r
6639 \r
6640 BOOL CALLBACK\r
6641 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6642 {\r
6643   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6644   RECT rect;\r
6645   POINT pt;\r
6646 \r
6647   if (hChild == cl->hText) return TRUE;\r
6648   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6649   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6650   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6651   ScreenToClient(cl->hDlg, &pt);\r
6652   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6653     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6654   return TRUE;\r
6655 }\r
6656 \r
6657 /* Resize a dialog that has a (rich) edit field filling most of\r
6658    the top, with a row of buttons below */\r
6659 VOID\r
6660 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6661 {\r
6662   RECT rectText;\r
6663   int newTextHeight, newTextWidth;\r
6664   ResizeEditPlusButtonsClosure cl;\r
6665   \r
6666   /*if (IsIconic(hDlg)) return;*/\r
6667   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6668   \r
6669   cl.hdwp = BeginDeferWindowPos(8);\r
6670 \r
6671   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6672   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6673   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6674   if (newTextHeight < 0) {\r
6675     newSizeY += -newTextHeight;\r
6676     newTextHeight = 0;\r
6677   }\r
6678   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6679     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6680 \r
6681   cl.hDlg = hDlg;\r
6682   cl.hText = hText;\r
6683   cl.sizeX = sizeX;\r
6684   cl.sizeY = sizeY;\r
6685   cl.newSizeX = newSizeX;\r
6686   cl.newSizeY = newSizeY;\r
6687   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6688 \r
6689   EndDeferWindowPos(cl.hdwp);\r
6690 }\r
6691 \r
6692 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6693 {\r
6694     RECT    rChild, rParent;\r
6695     int     wChild, hChild, wParent, hParent;\r
6696     int     wScreen, hScreen, xNew, yNew;\r
6697     HDC     hdc;\r
6698 \r
6699     /* Get the Height and Width of the child window */\r
6700     GetWindowRect (hwndChild, &rChild);\r
6701     wChild = rChild.right - rChild.left;\r
6702     hChild = rChild.bottom - rChild.top;\r
6703 \r
6704     /* Get the Height and Width of the parent window */\r
6705     GetWindowRect (hwndParent, &rParent);\r
6706     wParent = rParent.right - rParent.left;\r
6707     hParent = rParent.bottom - rParent.top;\r
6708 \r
6709     /* Get the display limits */\r
6710     hdc = GetDC (hwndChild);\r
6711     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6712     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6713     ReleaseDC(hwndChild, hdc);\r
6714 \r
6715     /* Calculate new X position, then adjust for screen */\r
6716     xNew = rParent.left + ((wParent - wChild) /2);\r
6717     if (xNew < 0) {\r
6718         xNew = 0;\r
6719     } else if ((xNew+wChild) > wScreen) {\r
6720         xNew = wScreen - wChild;\r
6721     }\r
6722 \r
6723     /* Calculate new Y position, then adjust for screen */\r
6724     if( mode == 0 ) {\r
6725         yNew = rParent.top  + ((hParent - hChild) /2);\r
6726     }\r
6727     else {\r
6728         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6729     }\r
6730 \r
6731     if (yNew < 0) {\r
6732         yNew = 0;\r
6733     } else if ((yNew+hChild) > hScreen) {\r
6734         yNew = hScreen - hChild;\r
6735     }\r
6736 \r
6737     /* Set it, and return */\r
6738     return SetWindowPos (hwndChild, NULL,\r
6739                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6740 }\r
6741 \r
6742 /* Center one window over another */\r
6743 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6744 {\r
6745     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6746 }\r
6747 \r
6748 /*---------------------------------------------------------------------------*\\r
6749  *\r
6750  * Startup Dialog functions\r
6751  *\r
6752 \*---------------------------------------------------------------------------*/\r
6753 void\r
6754 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6755 {\r
6756   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6757 \r
6758   while (*cd != NULL) {\r
6759     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6760     cd++;\r
6761   }\r
6762 }\r
6763 \r
6764 void\r
6765 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6766 {\r
6767   char buf1[ARG_MAX];\r
6768   int len;\r
6769 \r
6770   if (str[0] == '@') {\r
6771     FILE* f = fopen(str + 1, "r");\r
6772     if (f == NULL) {\r
6773       DisplayFatalError(str + 1, errno, 2);\r
6774       return;\r
6775     }\r
6776     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6777     fclose(f);\r
6778     buf1[len] = NULLCHAR;\r
6779     str = buf1;\r
6780   }\r
6781 \r
6782   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6783 \r
6784   for (;;) {\r
6785     char buf[MSG_SIZ];\r
6786     char *end = strchr(str, '\n');\r
6787     if (end == NULL) return;\r
6788     memcpy(buf, str, end - str);\r
6789     buf[end - str] = NULLCHAR;\r
6790     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6791     str = end + 1;\r
6792   }\r
6793 }\r
6794 \r
6795 void\r
6796 SetStartupDialogEnables(HWND hDlg)\r
6797 {\r
6798   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6799     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6800     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6801   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6802     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6803   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6804     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6805   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6806     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6807   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6808     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6809     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6810     IsDlgButtonChecked(hDlg, OPT_View));\r
6811 }\r
6812 \r
6813 char *\r
6814 QuoteForFilename(char *filename)\r
6815 {\r
6816   int dquote, space;\r
6817   dquote = strchr(filename, '"') != NULL;\r
6818   space = strchr(filename, ' ') != NULL;\r
6819   if (dquote || space) {\r
6820     if (dquote) {\r
6821       return "'";\r
6822     } else {\r
6823       return "\"";\r
6824     }\r
6825   } else {\r
6826     return "";\r
6827   }\r
6828 }\r
6829 \r
6830 VOID\r
6831 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6832 {\r
6833   char buf[MSG_SIZ];\r
6834   char *q;\r
6835 \r
6836   InitComboStringsFromOption(hwndCombo, nthnames);\r
6837   q = QuoteForFilename(nthcp);\r
6838   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6839   if (*nthdir != NULLCHAR) {\r
6840     q = QuoteForFilename(nthdir);\r
6841     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6842   }\r
6843   if (*nthcp == NULLCHAR) {\r
6844     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6845   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6846     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6847     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6848   }\r
6849 }\r
6850 \r
6851 LRESULT CALLBACK\r
6852 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6853 {\r
6854   char buf[MSG_SIZ];\r
6855   HANDLE hwndCombo;\r
6856   char *p;\r
6857 \r
6858   switch (message) {\r
6859   case WM_INITDIALOG:\r
6860     /* Center the dialog */\r
6861     CenterWindow (hDlg, GetDesktopWindow());\r
6862     /* Initialize the dialog items */\r
6863     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6864                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6865                   firstChessProgramNames);\r
6866     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6867                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6868                   secondChessProgramNames);\r
6869     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6870     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6871     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6872     if (*appData.icsHelper != NULLCHAR) {\r
6873       char *q = QuoteForFilename(appData.icsHelper);\r
6874       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6875     }\r
6876     if (*appData.icsHost == NULLCHAR) {\r
6877       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6878       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6879     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6880       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6881       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6882     }\r
6883 \r
6884     if (appData.icsActive) {\r
6885       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6886     }\r
6887     else if (appData.noChessProgram) {\r
6888       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6889     }\r
6890     else {\r
6891       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6892     }\r
6893 \r
6894     SetStartupDialogEnables(hDlg);\r
6895     return TRUE;\r
6896 \r
6897   case WM_COMMAND:\r
6898     switch (LOWORD(wParam)) {\r
6899     case IDOK:\r
6900       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6901         strcpy(buf, "/fcp=");\r
6902         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6903         p = buf;\r
6904         ParseArgs(StringGet, &p);\r
6905         strcpy(buf, "/scp=");\r
6906         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6907         p = buf;\r
6908         ParseArgs(StringGet, &p);\r
6909         appData.noChessProgram = FALSE;\r
6910         appData.icsActive = FALSE;\r
6911       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6912         strcpy(buf, "/ics /icshost=");\r
6913         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6914         p = buf;\r
6915         ParseArgs(StringGet, &p);\r
6916         if (appData.zippyPlay) {\r
6917           strcpy(buf, "/fcp=");\r
6918           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6919           p = buf;\r
6920           ParseArgs(StringGet, &p);\r
6921         }\r
6922       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6923         appData.noChessProgram = TRUE;\r
6924         appData.icsActive = FALSE;\r
6925       } else {\r
6926         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6927                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6928         return TRUE;\r
6929       }\r
6930       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6931         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6932         p = buf;\r
6933         ParseArgs(StringGet, &p);\r
6934       }\r
6935       EndDialog(hDlg, TRUE);\r
6936       return TRUE;\r
6937 \r
6938     case IDCANCEL:\r
6939       ExitEvent(0);\r
6940       return TRUE;\r
6941 \r
6942     case IDM_HELPCONTENTS:\r
6943       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6944         MessageBox (GetFocus(),\r
6945                     "Unable to activate help",\r
6946                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6947       }\r
6948       break;\r
6949 \r
6950     default:\r
6951       SetStartupDialogEnables(hDlg);\r
6952       break;\r
6953     }\r
6954     break;\r
6955   }\r
6956   return FALSE;\r
6957 }\r
6958 \r
6959 /*---------------------------------------------------------------------------*\\r
6960  *\r
6961  * About box dialog functions\r
6962  *\r
6963 \*---------------------------------------------------------------------------*/\r
6964 \r
6965 /* Process messages for "About" dialog box */\r
6966 LRESULT CALLBACK\r
6967 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6968 {\r
6969   switch (message) {\r
6970   case WM_INITDIALOG: /* message: initialize dialog box */\r
6971     /* Center the dialog over the application window */\r
6972     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6973     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6974     JAWS_COPYRIGHT\r
6975     return (TRUE);\r
6976 \r
6977   case WM_COMMAND: /* message: received a command */\r
6978     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6979         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6980       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6981       return (TRUE);\r
6982     }\r
6983     break;\r
6984   }\r
6985   return (FALSE);\r
6986 }\r
6987 \r
6988 /*---------------------------------------------------------------------------*\\r
6989  *\r
6990  * Comment Dialog functions\r
6991  *\r
6992 \*---------------------------------------------------------------------------*/\r
6993 \r
6994 LRESULT CALLBACK\r
6995 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6996 {\r
6997   static HANDLE hwndText = NULL;\r
6998   int len, newSizeX, newSizeY, flags;\r
6999   static int sizeX, sizeY;\r
7000   char *str;\r
7001   RECT rect;\r
7002   MINMAXINFO *mmi;\r
7003 \r
7004   switch (message) {\r
7005   case WM_INITDIALOG: /* message: initialize dialog box */\r
7006     /* Initialize the dialog items */\r
7007     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7008     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7009     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7010     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7011     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7012     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7013     SetWindowText(hDlg, commentTitle);\r
7014     if (editComment) {\r
7015       SetFocus(hwndText);\r
7016     } else {\r
7017       SetFocus(GetDlgItem(hDlg, IDOK));\r
7018     }\r
7019     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7020                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7021                 MAKELPARAM(FALSE, 0));\r
7022     /* Size and position the dialog */\r
7023     if (!commentDialog) {\r
7024       commentDialog = hDlg;\r
7025       flags = SWP_NOZORDER;\r
7026       GetClientRect(hDlg, &rect);\r
7027       sizeX = rect.right;\r
7028       sizeY = rect.bottom;\r
7029       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7030           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7031         WINDOWPLACEMENT wp;\r
7032         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7033         wp.length = sizeof(WINDOWPLACEMENT);\r
7034         wp.flags = 0;\r
7035         wp.showCmd = SW_SHOW;\r
7036         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7037         wp.rcNormalPosition.left = commentX;\r
7038         wp.rcNormalPosition.right = commentX + commentW;\r
7039         wp.rcNormalPosition.top = commentY;\r
7040         wp.rcNormalPosition.bottom = commentY + commentH;\r
7041         SetWindowPlacement(hDlg, &wp);\r
7042 \r
7043         GetClientRect(hDlg, &rect);\r
7044         newSizeX = rect.right;\r
7045         newSizeY = rect.bottom;\r
7046         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7047                               newSizeX, newSizeY);\r
7048         sizeX = newSizeX;\r
7049         sizeY = newSizeY;\r
7050       }\r
7051     }\r
7052     return FALSE;\r
7053 \r
7054   case WM_COMMAND: /* message: received a command */\r
7055     switch (LOWORD(wParam)) {\r
7056     case IDOK:\r
7057       if (editComment) {\r
7058         char *p, *q;\r
7059         /* Read changed options from the dialog box */\r
7060         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7061         len = GetWindowTextLength(hwndText);\r
7062         str = (char *) malloc(len + 1);\r
7063         GetWindowText(hwndText, str, len + 1);\r
7064         p = q = str;\r
7065         while (*q) {\r
7066           if (*q == '\r')\r
7067             q++;\r
7068           else\r
7069             *p++ = *q++;\r
7070         }\r
7071         *p = NULLCHAR;\r
7072         ReplaceComment(commentIndex, str);\r
7073         free(str);\r
7074       }\r
7075       CommentPopDown();\r
7076       return TRUE;\r
7077 \r
7078     case IDCANCEL:\r
7079     case OPT_CancelComment:\r
7080       CommentPopDown();\r
7081       return TRUE;\r
7082 \r
7083     case OPT_ClearComment:\r
7084       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7085       break;\r
7086 \r
7087     case OPT_EditComment:\r
7088       EditCommentEvent();\r
7089       return TRUE;\r
7090 \r
7091     default:\r
7092       break;\r
7093     }\r
7094     break;\r
7095 \r
7096   case WM_SIZE:\r
7097     newSizeX = LOWORD(lParam);\r
7098     newSizeY = HIWORD(lParam);\r
7099     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7100     sizeX = newSizeX;\r
7101     sizeY = newSizeY;\r
7102     break;\r
7103 \r
7104   case WM_GETMINMAXINFO:\r
7105     /* Prevent resizing window too small */\r
7106     mmi = (MINMAXINFO *) lParam;\r
7107     mmi->ptMinTrackSize.x = 100;\r
7108     mmi->ptMinTrackSize.y = 100;\r
7109     break;\r
7110   }\r
7111   return FALSE;\r
7112 }\r
7113 \r
7114 VOID\r
7115 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7116 {\r
7117   FARPROC lpProc;\r
7118   char *p, *q;\r
7119 \r
7120   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7121 \r
7122   if (str == NULL) str = "";\r
7123   p = (char *) malloc(2 * strlen(str) + 2);\r
7124   q = p;\r
7125   while (*str) {\r
7126     if (*str == '\n') *q++ = '\r';\r
7127     *q++ = *str++;\r
7128   }\r
7129   *q = NULLCHAR;\r
7130   if (commentText != NULL) free(commentText);\r
7131 \r
7132   commentIndex = index;\r
7133   commentTitle = title;\r
7134   commentText = p;\r
7135   editComment = edit;\r
7136 \r
7137   if (commentDialog) {\r
7138     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7139     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7140   } else {\r
7141     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7142     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7143                  hwndMain, (DLGPROC)lpProc);\r
7144     FreeProcInstance(lpProc);\r
7145   }\r
7146   commentDialogUp = TRUE;\r
7147 }\r
7148 \r
7149 \r
7150 /*---------------------------------------------------------------------------*\\r
7151  *\r
7152  * Type-in move dialog functions\r
7153  * \r
7154 \*---------------------------------------------------------------------------*/\r
7155 \r
7156 LRESULT CALLBACK\r
7157 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7158 {\r
7159   char move[MSG_SIZ];\r
7160   HWND hInput;\r
7161   ChessMove moveType;\r
7162   int fromX, fromY, toX, toY;\r
7163   char promoChar;\r
7164 \r
7165   switch (message) {\r
7166   case WM_INITDIALOG:\r
7167     move[0] = (char) lParam;\r
7168     move[1] = NULLCHAR;\r
7169     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7170     hInput = GetDlgItem(hDlg, OPT_Move);\r
7171     SetWindowText(hInput, move);\r
7172     SetFocus(hInput);\r
7173     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7174     return FALSE;\r
7175 \r
7176   case WM_COMMAND:\r
7177     switch (LOWORD(wParam)) {\r
7178     case IDOK:\r
7179       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7180       { int n; Board board;\r
7181         // [HGM] FENedit\r
7182         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7183                 EditPositionPasteFEN(move);\r
7184                 EndDialog(hDlg, TRUE);\r
7185                 return TRUE;\r
7186         }\r
7187         // [HGM] movenum: allow move number to be typed in any mode\r
7188         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7189           currentMove = 2*n-1;\r
7190           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7191           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7192           EndDialog(hDlg, TRUE);\r
7193           DrawPosition(TRUE, boards[currentMove]);\r
7194           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7195           else DisplayMessage("", "");\r
7196           return TRUE;\r
7197         }\r
7198       }\r
7199       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7200         gameMode != Training) {\r
7201         DisplayMoveError("Displayed move is not current");\r
7202       } else {\r
7203 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7204         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7205           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7206         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7207         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7208           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7209           if (gameMode != Training)\r
7210               forwardMostMove = currentMove;\r
7211           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7212         } else {\r
7213           DisplayMoveError("Could not parse move");\r
7214         }\r
7215       }\r
7216       EndDialog(hDlg, TRUE);\r
7217       return TRUE;\r
7218     case IDCANCEL:\r
7219       EndDialog(hDlg, FALSE);\r
7220       return TRUE;\r
7221     default:\r
7222       break;\r
7223     }\r
7224     break;\r
7225   }\r
7226   return FALSE;\r
7227 }\r
7228 \r
7229 VOID\r
7230 PopUpMoveDialog(char firstchar)\r
7231 {\r
7232     FARPROC lpProc;\r
7233     \r
7234     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7235         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7236         gameMode == AnalyzeMode || gameMode == EditGame || \r
7237         gameMode == EditPosition || gameMode == IcsExamining ||\r
7238         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7239         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7240                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7241                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7242         gameMode == Training) {\r
7243       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7244       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7245         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7246       FreeProcInstance(lpProc);\r
7247     }\r
7248 }\r
7249 \r
7250 /*---------------------------------------------------------------------------*\\r
7251  *\r
7252  * Type-in name dialog functions\r
7253  * \r
7254 \*---------------------------------------------------------------------------*/\r
7255 \r
7256 LRESULT CALLBACK\r
7257 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7258 {\r
7259   char move[MSG_SIZ];\r
7260   HWND hInput;\r
7261 \r
7262   switch (message) {\r
7263   case WM_INITDIALOG:\r
7264     move[0] = (char) lParam;\r
7265     move[1] = NULLCHAR;\r
7266     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7267     hInput = GetDlgItem(hDlg, OPT_Name);\r
7268     SetWindowText(hInput, move);\r
7269     SetFocus(hInput);\r
7270     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7271     return FALSE;\r
7272 \r
7273   case WM_COMMAND:\r
7274     switch (LOWORD(wParam)) {\r
7275     case IDOK:\r
7276       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7277       appData.userName = strdup(move);\r
7278       SetUserLogo();\r
7279 \r
7280       EndDialog(hDlg, TRUE);\r
7281       return TRUE;\r
7282     case IDCANCEL:\r
7283       EndDialog(hDlg, FALSE);\r
7284       return TRUE;\r
7285     default:\r
7286       break;\r
7287     }\r
7288     break;\r
7289   }\r
7290   return FALSE;\r
7291 }\r
7292 \r
7293 VOID\r
7294 PopUpNameDialog(char firstchar)\r
7295 {\r
7296     FARPROC lpProc;\r
7297     \r
7298       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7299       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7300         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7301       FreeProcInstance(lpProc);\r
7302 }\r
7303 \r
7304 /*---------------------------------------------------------------------------*\\r
7305  *\r
7306  *  Error dialogs\r
7307  * \r
7308 \*---------------------------------------------------------------------------*/\r
7309 \r
7310 /* Nonmodal error box */\r
7311 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7312                              WPARAM wParam, LPARAM lParam);\r
7313 \r
7314 VOID\r
7315 ErrorPopUp(char *title, char *content)\r
7316 {\r
7317   FARPROC lpProc;\r
7318   char *p, *q;\r
7319   BOOLEAN modal = hwndMain == NULL;\r
7320 \r
7321   p = content;\r
7322   q = errorMessage;\r
7323   while (*p) {\r
7324     if (*p == '\n') {\r
7325       if (modal) {\r
7326         *q++ = ' ';\r
7327         p++;\r
7328       } else {\r
7329         *q++ = '\r';\r
7330         *q++ = *p++;\r
7331       }\r
7332     } else {\r
7333       *q++ = *p++;\r
7334     }\r
7335   }\r
7336   *q = NULLCHAR;\r
7337   strncpy(errorTitle, title, sizeof(errorTitle));\r
7338   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7339   \r
7340   if (modal) {\r
7341     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7342   } else {\r
7343     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7344     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7345                  hwndMain, (DLGPROC)lpProc);\r
7346     FreeProcInstance(lpProc);\r
7347   }\r
7348 }\r
7349 \r
7350 VOID\r
7351 ErrorPopDown()\r
7352 {\r
7353   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7354   if (errorDialog == NULL) return;\r
7355   DestroyWindow(errorDialog);\r
7356   errorDialog = NULL;\r
7357 }\r
7358 \r
7359 LRESULT CALLBACK\r
7360 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7361 {\r
7362   HANDLE hwndText;\r
7363   RECT rChild;\r
7364 \r
7365   switch (message) {\r
7366   case WM_INITDIALOG:\r
7367     GetWindowRect(hDlg, &rChild);\r
7368 \r
7369     /*\r
7370     SetWindowPos(hDlg, NULL, rChild.left,\r
7371       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7372       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7373     */\r
7374 \r
7375     /* \r
7376         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7377         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7378         and it doesn't work when you resize the dialog.\r
7379         For now, just give it a default position.\r
7380     */\r
7381     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7382 \r
7383     errorDialog = hDlg;\r
7384     SetWindowText(hDlg, errorTitle);\r
7385     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7386     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7387     return FALSE;\r
7388 \r
7389   case WM_COMMAND:\r
7390     switch (LOWORD(wParam)) {\r
7391     case IDOK:\r
7392     case IDCANCEL:\r
7393       if (errorDialog == hDlg) errorDialog = NULL;\r
7394       DestroyWindow(hDlg);\r
7395       return TRUE;\r
7396 \r
7397     default:\r
7398       break;\r
7399     }\r
7400     break;\r
7401   }\r
7402   return FALSE;\r
7403 }\r
7404 \r
7405 #ifdef GOTHIC\r
7406 HWND gothicDialog = NULL;\r
7407 \r
7408 LRESULT CALLBACK\r
7409 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7410 {\r
7411   HANDLE hwndText;\r
7412   RECT rChild;\r
7413   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7414 \r
7415   switch (message) {\r
7416   case WM_INITDIALOG:\r
7417     GetWindowRect(hDlg, &rChild);\r
7418 \r
7419     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7420                                                              SWP_NOZORDER);\r
7421 \r
7422     /* \r
7423         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7424         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7425         and it doesn't work when you resize the dialog.\r
7426         For now, just give it a default position.\r
7427     */\r
7428     gothicDialog = hDlg;\r
7429     SetWindowText(hDlg, errorTitle);\r
7430     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7431     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7432     return FALSE;\r
7433 \r
7434   case WM_COMMAND:\r
7435     switch (LOWORD(wParam)) {\r
7436     case IDOK:\r
7437     case IDCANCEL:\r
7438       if (errorDialog == hDlg) errorDialog = NULL;\r
7439       DestroyWindow(hDlg);\r
7440       return TRUE;\r
7441 \r
7442     default:\r
7443       break;\r
7444     }\r
7445     break;\r
7446   }\r
7447   return FALSE;\r
7448 }\r
7449 \r
7450 VOID\r
7451 GothicPopUp(char *title, VariantClass variant)\r
7452 {\r
7453   FARPROC lpProc;\r
7454   static char *lastTitle;\r
7455 \r
7456   strncpy(errorTitle, title, sizeof(errorTitle));\r
7457   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7458 \r
7459   if(lastTitle != title && gothicDialog != NULL) {\r
7460     DestroyWindow(gothicDialog);\r
7461     gothicDialog = NULL;\r
7462   }\r
7463   if(variant != VariantNormal && gothicDialog == NULL) {\r
7464     title = lastTitle;\r
7465     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7466     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7467                  hwndMain, (DLGPROC)lpProc);\r
7468     FreeProcInstance(lpProc);\r
7469   }\r
7470 }\r
7471 #endif\r
7472 \r
7473 /*---------------------------------------------------------------------------*\\r
7474  *\r
7475  *  Ics Interaction console functions\r
7476  *\r
7477 \*---------------------------------------------------------------------------*/\r
7478 \r
7479 #define HISTORY_SIZE 64\r
7480 static char *history[HISTORY_SIZE];\r
7481 int histIn = 0, histP = 0;\r
7482 \r
7483 VOID\r
7484 SaveInHistory(char *cmd)\r
7485 {\r
7486   if (history[histIn] != NULL) {\r
7487     free(history[histIn]);\r
7488     history[histIn] = NULL;\r
7489   }\r
7490   if (*cmd == NULLCHAR) return;\r
7491   history[histIn] = StrSave(cmd);\r
7492   histIn = (histIn + 1) % HISTORY_SIZE;\r
7493   if (history[histIn] != NULL) {\r
7494     free(history[histIn]);\r
7495     history[histIn] = NULL;\r
7496   }\r
7497   histP = histIn;\r
7498 }\r
7499 \r
7500 char *\r
7501 PrevInHistory(char *cmd)\r
7502 {\r
7503   int newhp;\r
7504   if (histP == histIn) {\r
7505     if (history[histIn] != NULL) free(history[histIn]);\r
7506     history[histIn] = StrSave(cmd);\r
7507   }\r
7508   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7509   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7510   histP = newhp;\r
7511   return history[histP];\r
7512 }\r
7513 \r
7514 char *\r
7515 NextInHistory()\r
7516 {\r
7517   if (histP == histIn) return NULL;\r
7518   histP = (histP + 1) % HISTORY_SIZE;\r
7519   return history[histP];\r
7520 }\r
7521 \r
7522 typedef struct {\r
7523   char *item;\r
7524   char *command;\r
7525   BOOLEAN getname;\r
7526   BOOLEAN immediate;\r
7527 } IcsTextMenuEntry;\r
7528 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7529 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7530 \r
7531 void\r
7532 ParseIcsTextMenu(char *icsTextMenuString)\r
7533 {\r
7534 //  int flags = 0;\r
7535   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7536   char *p = icsTextMenuString;\r
7537   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7538     free(e->item);\r
7539     e->item = NULL;\r
7540     if (e->command != NULL) {\r
7541       free(e->command);\r
7542       e->command = NULL;\r
7543     }\r
7544     e++;\r
7545   }\r
7546   e = icsTextMenuEntry;\r
7547   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7548     if (*p == ';' || *p == '\n') {\r
7549       e->item = strdup("-");\r
7550       e->command = NULL;\r
7551       p++;\r
7552     } else if (*p == '-') {\r
7553       e->item = strdup("-");\r
7554       e->command = NULL;\r
7555       p++;\r
7556       if (*p) p++;\r
7557     } else {\r
7558       char *q, *r, *s, *t;\r
7559       char c;\r
7560       q = strchr(p, ',');\r
7561       if (q == NULL) break;\r
7562       *q = NULLCHAR;\r
7563       r = strchr(q + 1, ',');\r
7564       if (r == NULL) break;\r
7565       *r = NULLCHAR;\r
7566       s = strchr(r + 1, ',');\r
7567       if (s == NULL) break;\r
7568       *s = NULLCHAR;\r
7569       c = ';';\r
7570       t = strchr(s + 1, c);\r
7571       if (t == NULL) {\r
7572         c = '\n';\r
7573         t = strchr(s + 1, c);\r
7574       }\r
7575       if (t != NULL) *t = NULLCHAR;\r
7576       e->item = strdup(p);\r
7577       e->command = strdup(q + 1);\r
7578       e->getname = *(r + 1) != '0';\r
7579       e->immediate = *(s + 1) != '0';\r
7580       *q = ',';\r
7581       *r = ',';\r
7582       *s = ',';\r
7583       if (t == NULL) break;\r
7584       *t = c;\r
7585       p = t + 1;\r
7586     }\r
7587     e++;\r
7588   } \r
7589 }\r
7590 \r
7591 HMENU\r
7592 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7593 {\r
7594   HMENU hmenu, h;\r
7595   int i = 0;\r
7596   hmenu = LoadMenu(hInst, "TextMenu");\r
7597   h = GetSubMenu(hmenu, 0);\r
7598   while (e->item) {\r
7599     if (strcmp(e->item, "-") == 0) {\r
7600       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7601     } else {\r
7602       if (e->item[0] == '|') {\r
7603         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7604                    IDM_CommandX + i, &e->item[1]);\r
7605       } else {\r
7606         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7607       }\r
7608     }\r
7609     e++;\r
7610     i++;\r
7611   } \r
7612   return hmenu;\r
7613 }\r
7614 \r
7615 WNDPROC consoleTextWindowProc;\r
7616 \r
7617 void\r
7618 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7619 {\r
7620   char buf[MSG_SIZ], name[MSG_SIZ];\r
7621   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7622   CHARRANGE sel;\r
7623 \r
7624   if (!getname) {\r
7625     SetWindowText(hInput, command);\r
7626     if (immediate) {\r
7627       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7628     } else {\r
7629       sel.cpMin = 999999;\r
7630       sel.cpMax = 999999;\r
7631       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7632       SetFocus(hInput);\r
7633     }\r
7634     return;\r
7635   }    \r
7636   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7637   if (sel.cpMin == sel.cpMax) {\r
7638     /* Expand to surrounding word */\r
7639     TEXTRANGE tr;\r
7640     do {\r
7641       tr.chrg.cpMax = sel.cpMin;\r
7642       tr.chrg.cpMin = --sel.cpMin;\r
7643       if (sel.cpMin < 0) break;\r
7644       tr.lpstrText = name;\r
7645       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7646     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7647     sel.cpMin++;\r
7648 \r
7649     do {\r
7650       tr.chrg.cpMin = sel.cpMax;\r
7651       tr.chrg.cpMax = ++sel.cpMax;\r
7652       tr.lpstrText = name;\r
7653       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7654     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7655     sel.cpMax--;\r
7656 \r
7657     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7658       MessageBeep(MB_ICONEXCLAMATION);\r
7659       return;\r
7660     }\r
7661     tr.chrg = sel;\r
7662     tr.lpstrText = name;\r
7663     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7664   } else {\r
7665     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7666       MessageBeep(MB_ICONEXCLAMATION);\r
7667       return;\r
7668     }\r
7669     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7670   }\r
7671   if (immediate) {\r
7672     sprintf(buf, "%s %s", command, name);\r
7673     SetWindowText(hInput, buf);\r
7674     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7675   } else {\r
7676     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7677     SetWindowText(hInput, buf);\r
7678     sel.cpMin = 999999;\r
7679     sel.cpMax = 999999;\r
7680     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7681     SetFocus(hInput);\r
7682   }\r
7683 }\r
7684 \r
7685 LRESULT CALLBACK \r
7686 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7687 {\r
7688   HWND hInput;\r
7689   CHARRANGE sel;\r
7690 \r
7691   switch (message) {\r
7692   case WM_KEYDOWN:\r
7693     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7694     switch (wParam) {\r
7695     case VK_PRIOR:\r
7696       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7697       return 0;\r
7698     case VK_NEXT:\r
7699       sel.cpMin = 999999;\r
7700       sel.cpMax = 999999;\r
7701       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7702       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7703       return 0;\r
7704     }\r
7705     break;\r
7706   case WM_CHAR:\r
7707    if(wParam != '\022') {\r
7708     if (wParam == '\t') {\r
7709       if (GetKeyState(VK_SHIFT) < 0) {\r
7710         /* shifted */\r
7711         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7712         if (buttonDesc[0].hwnd) {\r
7713           SetFocus(buttonDesc[0].hwnd);\r
7714         } else {\r
7715           SetFocus(hwndMain);\r
7716         }\r
7717       } else {\r
7718         /* unshifted */\r
7719         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7720       }\r
7721     } else {\r
7722       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7723       JAWS_DELETE( SetFocus(hInput); )\r
7724       SendMessage(hInput, message, wParam, lParam);\r
7725     }\r
7726     return 0;\r
7727    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7728   case WM_RBUTTONUP:\r
7729     if (GetKeyState(VK_SHIFT) & ~1) {\r
7730       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7731         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7732     } else {\r
7733       POINT pt;\r
7734       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7735       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7736       if (sel.cpMin == sel.cpMax) {\r
7737         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7738         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7739       }\r
7740       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7741         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7742       }\r
7743       pt.x = LOWORD(lParam);\r
7744       pt.y = HIWORD(lParam);\r
7745       MenuPopup(hwnd, pt, hmenu, -1);\r
7746     }\r
7747     return 0;\r
7748   case WM_PASTE:\r
7749     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7750     SetFocus(hInput);\r
7751     return SendMessage(hInput, message, wParam, lParam);\r
7752   case WM_MBUTTONDOWN:\r
7753     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7754   case WM_RBUTTONDOWN:\r
7755     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7756       /* Move selection here if it was empty */\r
7757       POINT pt;\r
7758       pt.x = LOWORD(lParam);\r
7759       pt.y = HIWORD(lParam);\r
7760       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7761       if (sel.cpMin == sel.cpMax) {\r
7762         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7763         sel.cpMax = sel.cpMin;\r
7764         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7765       }\r
7766       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7767     }\r
7768     return 0;\r
7769   case WM_COMMAND:\r
7770     switch (LOWORD(wParam)) {\r
7771     case IDM_QuickPaste:\r
7772       {\r
7773         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7774         if (sel.cpMin == sel.cpMax) {\r
7775           MessageBeep(MB_ICONEXCLAMATION);\r
7776           return 0;\r
7777         }\r
7778         SendMessage(hwnd, WM_COPY, 0, 0);\r
7779         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7780         SendMessage(hInput, WM_PASTE, 0, 0);\r
7781         SetFocus(hInput);\r
7782         return 0;\r
7783       }\r
7784     case IDM_Cut:\r
7785       SendMessage(hwnd, WM_CUT, 0, 0);\r
7786       return 0;\r
7787     case IDM_Paste:\r
7788       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7789       return 0;\r
7790     case IDM_Copy:\r
7791       SendMessage(hwnd, WM_COPY, 0, 0);\r
7792       return 0;\r
7793     default:\r
7794       {\r
7795         int i = LOWORD(wParam) - IDM_CommandX;\r
7796         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7797             icsTextMenuEntry[i].command != NULL) {\r
7798           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7799                    icsTextMenuEntry[i].getname,\r
7800                    icsTextMenuEntry[i].immediate);\r
7801           return 0;\r
7802         }\r
7803       }\r
7804       break;\r
7805     }\r
7806     break;\r
7807   }\r
7808   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7809 }\r
7810 \r
7811 WNDPROC consoleInputWindowProc;\r
7812 \r
7813 LRESULT CALLBACK\r
7814 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7815 {\r
7816   char buf[MSG_SIZ];\r
7817   char *p;\r
7818   static BOOL sendNextChar = FALSE;\r
7819   static BOOL quoteNextChar = FALSE;\r
7820   InputSource *is = consoleInputSource;\r
7821   CHARFORMAT cf;\r
7822   CHARRANGE sel;\r
7823 \r
7824   switch (message) {\r
7825   case WM_CHAR:\r
7826     if (!appData.localLineEditing || sendNextChar) {\r
7827       is->buf[0] = (CHAR) wParam;\r
7828       is->count = 1;\r
7829       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7830       sendNextChar = FALSE;\r
7831       return 0;\r
7832     }\r
7833     if (quoteNextChar) {\r
7834       buf[0] = (char) wParam;\r
7835       buf[1] = NULLCHAR;\r
7836       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7837       quoteNextChar = FALSE;\r
7838       return 0;\r
7839     }\r
7840     switch (wParam) {\r
7841     case '\r':   /* Enter key */\r
7842       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7843       if (consoleEcho) SaveInHistory(is->buf);\r
7844       is->buf[is->count++] = '\n';\r
7845       is->buf[is->count] = NULLCHAR;\r
7846       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7847       if (consoleEcho) {\r
7848         ConsoleOutput(is->buf, is->count, TRUE);\r
7849       } else if (appData.localLineEditing) {\r
7850         ConsoleOutput("\n", 1, TRUE);\r
7851       }\r
7852       /* fall thru */\r
7853     case '\033': /* Escape key */\r
7854       SetWindowText(hwnd, "");\r
7855       cf.cbSize = sizeof(CHARFORMAT);\r
7856       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7857       if (consoleEcho) {\r
7858         cf.crTextColor = textAttribs[ColorNormal].color;\r
7859       } else {\r
7860         cf.crTextColor = COLOR_ECHOOFF;\r
7861       }\r
7862       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7863       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7864       return 0;\r
7865     case '\t':   /* Tab key */\r
7866       if (GetKeyState(VK_SHIFT) < 0) {\r
7867         /* shifted */\r
7868         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7869       } else {\r
7870         /* unshifted */\r
7871         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7872         if (buttonDesc[0].hwnd) {\r
7873           SetFocus(buttonDesc[0].hwnd);\r
7874         } else {\r
7875           SetFocus(hwndMain);\r
7876         }\r
7877       }\r
7878       return 0;\r
7879     case '\023': /* Ctrl+S */\r
7880       sendNextChar = TRUE;\r
7881       return 0;\r
7882     case '\021': /* Ctrl+Q */\r
7883       quoteNextChar = TRUE;\r
7884       return 0;\r
7885     JAWS_REPLAY\r
7886     default:\r
7887       break;\r
7888     }\r
7889     break;\r
7890   case WM_KEYDOWN:\r
7891     switch (wParam) {\r
7892     case VK_UP:\r
7893       GetWindowText(hwnd, buf, MSG_SIZ);\r
7894       p = PrevInHistory(buf);\r
7895       if (p != NULL) {\r
7896         SetWindowText(hwnd, p);\r
7897         sel.cpMin = 999999;\r
7898         sel.cpMax = 999999;\r
7899         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7900         return 0;\r
7901       }\r
7902       break;\r
7903     case VK_DOWN:\r
7904       p = NextInHistory();\r
7905       if (p != NULL) {\r
7906         SetWindowText(hwnd, p);\r
7907         sel.cpMin = 999999;\r
7908         sel.cpMax = 999999;\r
7909         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7910         return 0;\r
7911       }\r
7912       break;\r
7913     case VK_HOME:\r
7914     case VK_END:\r
7915       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7916       /* fall thru */\r
7917     case VK_PRIOR:\r
7918     case VK_NEXT:\r
7919       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7920       return 0;\r
7921     }\r
7922     break;\r
7923   case WM_MBUTTONDOWN:\r
7924     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7925       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7926     break;\r
7927   case WM_RBUTTONUP:\r
7928     if (GetKeyState(VK_SHIFT) & ~1) {\r
7929       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7930         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7931     } else {\r
7932       POINT pt;\r
7933       HMENU hmenu;\r
7934       hmenu = LoadMenu(hInst, "InputMenu");\r
7935       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7936       if (sel.cpMin == sel.cpMax) {\r
7937         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7938         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7939       }\r
7940       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7941         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7942       }\r
7943       pt.x = LOWORD(lParam);\r
7944       pt.y = HIWORD(lParam);\r
7945       MenuPopup(hwnd, pt, hmenu, -1);\r
7946     }\r
7947     return 0;\r
7948   case WM_COMMAND:\r
7949     switch (LOWORD(wParam)) { \r
7950     case IDM_Undo:\r
7951       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7952       return 0;\r
7953     case IDM_SelectAll:\r
7954       sel.cpMin = 0;\r
7955       sel.cpMax = -1; /*999999?*/\r
7956       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7957       return 0;\r
7958     case IDM_Cut:\r
7959       SendMessage(hwnd, WM_CUT, 0, 0);\r
7960       return 0;\r
7961     case IDM_Paste:\r
7962       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7963       return 0;\r
7964     case IDM_Copy:\r
7965       SendMessage(hwnd, WM_COPY, 0, 0);\r
7966       return 0;\r
7967     }\r
7968     break;\r
7969   }\r
7970   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7971 }\r
7972 \r
7973 #define CO_MAX  100000\r
7974 #define CO_TRIM   1000\r
7975 \r
7976 LRESULT CALLBACK\r
7977 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7978 {\r
7979   static SnapData sd;\r
7980   HWND hText, hInput;\r
7981   RECT rect;\r
7982   static int sizeX, sizeY;\r
7983   int newSizeX, newSizeY;\r
7984   MINMAXINFO *mmi;\r
7985   WORD wMask;\r
7986 \r
7987   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7988   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7989 \r
7990   switch (message) {\r
7991   case WM_NOTIFY:\r
7992     if (((NMHDR*)lParam)->code == EN_LINK)\r
7993     {\r
7994       ENLINK *pLink = (ENLINK*)lParam;\r
7995       if (pLink->msg == WM_LBUTTONUP)\r
7996       {\r
7997         TEXTRANGE tr;\r
7998 \r
7999         tr.chrg = pLink->chrg;\r
8000         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
8001         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
8002         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
8003         free(tr.lpstrText);\r
8004       }\r
8005     }\r
8006     break;\r
8007   case WM_INITDIALOG: /* message: initialize dialog box */\r
8008     hwndConsole = hDlg;\r
8009     SetFocus(hInput);\r
8010     consoleTextWindowProc = (WNDPROC)\r
8011       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8012     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8013     consoleInputWindowProc = (WNDPROC)\r
8014       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8015     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8016     Colorize(ColorNormal, TRUE);\r
8017     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8018     ChangedConsoleFont();\r
8019     GetClientRect(hDlg, &rect);\r
8020     sizeX = rect.right;\r
8021     sizeY = rect.bottom;\r
8022     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8023         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8024       WINDOWPLACEMENT wp;\r
8025       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8026       wp.length = sizeof(WINDOWPLACEMENT);\r
8027       wp.flags = 0;\r
8028       wp.showCmd = SW_SHOW;\r
8029       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8030       wp.rcNormalPosition.left = wpConsole.x;\r
8031       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8032       wp.rcNormalPosition.top = wpConsole.y;\r
8033       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8034       SetWindowPlacement(hDlg, &wp);\r
8035     }\r
8036 \r
8037    // [HGM] Chessknight's change 2004-07-13\r
8038    else { /* Determine Defaults */\r
8039        WINDOWPLACEMENT wp;\r
8040        wpConsole.x = winWidth + 1;\r
8041        wpConsole.y = boardY;\r
8042        wpConsole.width = screenWidth -  winWidth;\r
8043        wpConsole.height = winHeight;\r
8044        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8045        wp.length = sizeof(WINDOWPLACEMENT);\r
8046        wp.flags = 0;\r
8047        wp.showCmd = SW_SHOW;\r
8048        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8049        wp.rcNormalPosition.left = wpConsole.x;\r
8050        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8051        wp.rcNormalPosition.top = wpConsole.y;\r
8052        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8053        SetWindowPlacement(hDlg, &wp);\r
8054     }\r
8055 \r
8056    // Allow hText to highlight URLs and send notifications on them\r
8057    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
8058    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
8059    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
8060    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
8061 \r
8062     return FALSE;\r
8063 \r
8064   case WM_SETFOCUS:\r
8065     SetFocus(hInput);\r
8066     return 0;\r
8067 \r
8068   case WM_CLOSE:\r
8069     ExitEvent(0);\r
8070     /* not reached */\r
8071     break;\r
8072 \r
8073   case WM_SIZE:\r
8074     if (IsIconic(hDlg)) break;\r
8075     newSizeX = LOWORD(lParam);\r
8076     newSizeY = HIWORD(lParam);\r
8077     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8078       RECT rectText, rectInput;\r
8079       POINT pt;\r
8080       int newTextHeight, newTextWidth;\r
8081       GetWindowRect(hText, &rectText);\r
8082       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8083       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8084       if (newTextHeight < 0) {\r
8085         newSizeY += -newTextHeight;\r
8086         newTextHeight = 0;\r
8087       }\r
8088       SetWindowPos(hText, NULL, 0, 0,\r
8089         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8090       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8091       pt.x = rectInput.left;\r
8092       pt.y = rectInput.top + newSizeY - sizeY;\r
8093       ScreenToClient(hDlg, &pt);\r
8094       SetWindowPos(hInput, NULL, \r
8095         pt.x, pt.y, /* needs client coords */   \r
8096         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8097         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8098     }\r
8099     sizeX = newSizeX;\r
8100     sizeY = newSizeY;\r
8101     break;\r
8102 \r
8103   case WM_GETMINMAXINFO:\r
8104     /* Prevent resizing window too small */\r
8105     mmi = (MINMAXINFO *) lParam;\r
8106     mmi->ptMinTrackSize.x = 100;\r
8107     mmi->ptMinTrackSize.y = 100;\r
8108     break;\r
8109 \r
8110   /* [AS] Snapping */\r
8111   case WM_ENTERSIZEMOVE:\r
8112     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8113 \r
8114   case WM_SIZING:\r
8115     return OnSizing( &sd, hDlg, wParam, lParam );\r
8116 \r
8117   case WM_MOVING:\r
8118     return OnMoving( &sd, hDlg, wParam, lParam );\r
8119 \r
8120   case WM_EXITSIZEMOVE:\r
8121         UpdateICSWidth(hText);\r
8122     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8123   }\r
8124 \r
8125   return DefWindowProc(hDlg, message, wParam, lParam);\r
8126 }\r
8127 \r
8128 \r
8129 VOID\r
8130 ConsoleCreate()\r
8131 {\r
8132   HWND hCons;\r
8133   if (hwndConsole) return;\r
8134   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8135   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8136 }\r
8137 \r
8138 \r
8139 VOID\r
8140 ConsoleOutput(char* data, int length, int forceVisible)\r
8141 {\r
8142   HWND hText;\r
8143   int trim, exlen;\r
8144   char *p, *q;\r
8145   char buf[CO_MAX+1];\r
8146   POINT pEnd;\r
8147   RECT rect;\r
8148   static int delayLF = 0;\r
8149   CHARRANGE savesel, sel;\r
8150 \r
8151   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8152   p = data;\r
8153   q = buf;\r
8154   if (delayLF) {\r
8155     *q++ = '\r';\r
8156     *q++ = '\n';\r
8157     delayLF = 0;\r
8158   }\r
8159   while (length--) {\r
8160     if (*p == '\n') {\r
8161       if (*++p) {\r
8162         *q++ = '\r';\r
8163         *q++ = '\n';\r
8164       } else {\r
8165         delayLF = 1;\r
8166       }\r
8167     } else if (*p == '\007') {\r
8168        MyPlaySound(&sounds[(int)SoundBell]);\r
8169        p++;\r
8170     } else {\r
8171       *q++ = *p++;\r
8172     }\r
8173   }\r
8174   *q = NULLCHAR;\r
8175   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8176   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8177   /* Save current selection */\r
8178   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8179   exlen = GetWindowTextLength(hText);\r
8180   /* Find out whether current end of text is visible */\r
8181   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8182   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8183   /* Trim existing text if it's too long */\r
8184   if (exlen + (q - buf) > CO_MAX) {\r
8185     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8186     sel.cpMin = 0;\r
8187     sel.cpMax = trim;\r
8188     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8189     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8190     exlen -= trim;\r
8191     savesel.cpMin -= trim;\r
8192     savesel.cpMax -= trim;\r
8193     if (exlen < 0) exlen = 0;\r
8194     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8195     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8196   }\r
8197   /* Append the new text */\r
8198   sel.cpMin = exlen;\r
8199   sel.cpMax = exlen;\r
8200   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8201   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8202   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8203   if (forceVisible || exlen == 0 ||\r
8204       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8205        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8206     /* Scroll to make new end of text visible if old end of text\r
8207        was visible or new text is an echo of user typein */\r
8208     sel.cpMin = 9999999;\r
8209     sel.cpMax = 9999999;\r
8210     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8211     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8212     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8213     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8214   }\r
8215   if (savesel.cpMax == exlen || forceVisible) {\r
8216     /* Move insert point to new end of text if it was at the old\r
8217        end of text or if the new text is an echo of user typein */\r
8218     sel.cpMin = 9999999;\r
8219     sel.cpMax = 9999999;\r
8220     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8221   } else {\r
8222     /* Restore previous selection */\r
8223     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8224   }\r
8225   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8226 }\r
8227 \r
8228 /*---------*/\r
8229 \r
8230 \r
8231 void\r
8232 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8233 {\r
8234   char buf[100];\r
8235   char *str;\r
8236   COLORREF oldFg, oldBg;\r
8237   HFONT oldFont;\r
8238   RECT rect;\r
8239 \r
8240   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8241 \r
8242   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8243   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8244   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8245 \r
8246   rect.left = x;\r
8247   rect.right = x + squareSize;\r
8248   rect.top  = y;\r
8249   rect.bottom = y + squareSize;\r
8250   str = buf;\r
8251 \r
8252   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8253                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8254              y, ETO_CLIPPED|ETO_OPAQUE,\r
8255              &rect, str, strlen(str), NULL);\r
8256 \r
8257   (void) SetTextColor(hdc, oldFg);\r
8258   (void) SetBkColor(hdc, oldBg);\r
8259   (void) SelectObject(hdc, oldFont);\r
8260 }\r
8261 \r
8262 void\r
8263 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8264               RECT *rect, char *color, char *flagFell)\r
8265 {\r
8266   char buf[100];\r
8267   char *str;\r
8268   COLORREF oldFg, oldBg;\r
8269   HFONT oldFont;\r
8270 \r
8271   if (appData.clockMode) {\r
8272     if (tinyLayout)\r
8273       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8274     else\r
8275       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8276     str = buf;\r
8277   } else {\r
8278     str = color;\r
8279   }\r
8280 \r
8281   if (highlight) {\r
8282     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8283     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8284   } else {\r
8285     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8286     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8287   }\r
8288   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8289 \r
8290   JAWS_SILENCE\r
8291 \r
8292   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8293              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8294              rect, str, strlen(str), NULL);\r
8295   if(logoHeight > 0 && appData.clockMode) {\r
8296       RECT r;\r
8297       sprintf(buf, "%s %s", buf+7, flagFell);\r
8298       r.top = rect->top + logoHeight/2;\r
8299       r.left = rect->left;\r
8300       r.right = rect->right;\r
8301       r.bottom = rect->bottom;\r
8302       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8303                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8304                  &r, str, strlen(str), NULL);\r
8305   }\r
8306   (void) SetTextColor(hdc, oldFg);\r
8307   (void) SetBkColor(hdc, oldBg);\r
8308   (void) SelectObject(hdc, oldFont);\r
8309 }\r
8310 \r
8311 \r
8312 int\r
8313 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8314            OVERLAPPED *ovl)\r
8315 {\r
8316   int ok, err;\r
8317 \r
8318   /* [AS]  */\r
8319   if( count <= 0 ) {\r
8320     if (appData.debugMode) {\r
8321       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8322     }\r
8323 \r
8324     return ERROR_INVALID_USER_BUFFER;\r
8325   }\r
8326 \r
8327   ResetEvent(ovl->hEvent);\r
8328   ovl->Offset = ovl->OffsetHigh = 0;\r
8329   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8330   if (ok) {\r
8331     err = NO_ERROR;\r
8332   } else {\r
8333     err = GetLastError();\r
8334     if (err == ERROR_IO_PENDING) {\r
8335       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8336       if (ok)\r
8337         err = NO_ERROR;\r
8338       else\r
8339         err = GetLastError();\r
8340     }\r
8341   }\r
8342   return err;\r
8343 }\r
8344 \r
8345 int\r
8346 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8347             OVERLAPPED *ovl)\r
8348 {\r
8349   int ok, err;\r
8350 \r
8351   ResetEvent(ovl->hEvent);\r
8352   ovl->Offset = ovl->OffsetHigh = 0;\r
8353   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8354   if (ok) {\r
8355     err = NO_ERROR;\r
8356   } else {\r
8357     err = GetLastError();\r
8358     if (err == ERROR_IO_PENDING) {\r
8359       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8360       if (ok)\r
8361         err = NO_ERROR;\r
8362       else\r
8363         err = GetLastError();\r
8364     }\r
8365   }\r
8366   return err;\r
8367 }\r
8368 \r
8369 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8370 void CheckForInputBufferFull( InputSource * is )\r
8371 {\r
8372     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8373         /* Look for end of line */\r
8374         char * p = is->buf;\r
8375         \r
8376         while( p < is->next && *p != '\n' ) {\r
8377             p++;\r
8378         }\r
8379 \r
8380         if( p >= is->next ) {\r
8381             if (appData.debugMode) {\r
8382                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8383             }\r
8384 \r
8385             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8386             is->count = (DWORD) -1;\r
8387             is->next = is->buf;\r
8388         }\r
8389     }\r
8390 }\r
8391 \r
8392 DWORD\r
8393 InputThread(LPVOID arg)\r
8394 {\r
8395   InputSource *is;\r
8396   OVERLAPPED ovl;\r
8397 \r
8398   is = (InputSource *) arg;\r
8399   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8400   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8401   while (is->hThread != NULL) {\r
8402     is->error = DoReadFile(is->hFile, is->next,\r
8403                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8404                            &is->count, &ovl);\r
8405     if (is->error == NO_ERROR) {\r
8406       is->next += is->count;\r
8407     } else {\r
8408       if (is->error == ERROR_BROKEN_PIPE) {\r
8409         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8410         is->count = 0;\r
8411       } else {\r
8412         is->count = (DWORD) -1;\r
8413         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8414         break; \r
8415       }\r
8416     }\r
8417 \r
8418     CheckForInputBufferFull( is );\r
8419 \r
8420     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8421 \r
8422     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8423 \r
8424     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8425   }\r
8426 \r
8427   CloseHandle(ovl.hEvent);\r
8428   CloseHandle(is->hFile);\r
8429 \r
8430   if (appData.debugMode) {\r
8431     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8432   }\r
8433 \r
8434   return 0;\r
8435 }\r
8436 \r
8437 \r
8438 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8439 DWORD\r
8440 NonOvlInputThread(LPVOID arg)\r
8441 {\r
8442   InputSource *is;\r
8443   char *p, *q;\r
8444   int i;\r
8445   char prev;\r
8446 \r
8447   is = (InputSource *) arg;\r
8448   while (is->hThread != NULL) {\r
8449     is->error = ReadFile(is->hFile, is->next,\r
8450                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8451                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8452     if (is->error == NO_ERROR) {\r
8453       /* Change CRLF to LF */\r
8454       if (is->next > is->buf) {\r
8455         p = is->next - 1;\r
8456         i = is->count + 1;\r
8457       } else {\r
8458         p = is->next;\r
8459         i = is->count;\r
8460       }\r
8461       q = p;\r
8462       prev = NULLCHAR;\r
8463       while (i > 0) {\r
8464         if (prev == '\r' && *p == '\n') {\r
8465           *(q-1) = '\n';\r
8466           is->count--;\r
8467         } else { \r
8468           *q++ = *p;\r
8469         }\r
8470         prev = *p++;\r
8471         i--;\r
8472       }\r
8473       *q = NULLCHAR;\r
8474       is->next = q;\r
8475     } else {\r
8476       if (is->error == ERROR_BROKEN_PIPE) {\r
8477         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8478         is->count = 0; \r
8479       } else {\r
8480         is->count = (DWORD) -1;\r
8481       }\r
8482     }\r
8483 \r
8484     CheckForInputBufferFull( is );\r
8485 \r
8486     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8487 \r
8488     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8489 \r
8490     if (is->count < 0) break;  /* Quit on error */\r
8491   }\r
8492   CloseHandle(is->hFile);\r
8493   return 0;\r
8494 }\r
8495 \r
8496 DWORD\r
8497 SocketInputThread(LPVOID arg)\r
8498 {\r
8499   InputSource *is;\r
8500 \r
8501   is = (InputSource *) arg;\r
8502   while (is->hThread != NULL) {\r
8503     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8504     if ((int)is->count == SOCKET_ERROR) {\r
8505       is->count = (DWORD) -1;\r
8506       is->error = WSAGetLastError();\r
8507     } else {\r
8508       is->error = NO_ERROR;\r
8509       is->next += is->count;\r
8510       if (is->count == 0 && is->second == is) {\r
8511         /* End of file on stderr; quit with no message */\r
8512         break;\r
8513       }\r
8514     }\r
8515     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8516 \r
8517     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8518 \r
8519     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8520   }\r
8521   return 0;\r
8522 }\r
8523 \r
8524 VOID\r
8525 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8526 {\r
8527   InputSource *is;\r
8528 \r
8529   is = (InputSource *) lParam;\r
8530   if (is->lineByLine) {\r
8531     /* Feed in lines one by one */\r
8532     char *p = is->buf;\r
8533     char *q = p;\r
8534     while (q < is->next) {\r
8535       if (*q++ == '\n') {\r
8536         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8537         p = q;\r
8538       }\r
8539     }\r
8540     \r
8541     /* Move any partial line to the start of the buffer */\r
8542     q = is->buf;\r
8543     while (p < is->next) {\r
8544       *q++ = *p++;\r
8545     }\r
8546     is->next = q;\r
8547 \r
8548     if (is->error != NO_ERROR || is->count == 0) {\r
8549       /* Notify backend of the error.  Note: If there was a partial\r
8550          line at the end, it is not flushed through. */\r
8551       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8552     }\r
8553   } else {\r
8554     /* Feed in the whole chunk of input at once */\r
8555     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8556     is->next = is->buf;\r
8557   }\r
8558 }\r
8559 \r
8560 /*---------------------------------------------------------------------------*\\r
8561  *\r
8562  *  Menu enables. Used when setting various modes.\r
8563  *\r
8564 \*---------------------------------------------------------------------------*/\r
8565 \r
8566 typedef struct {\r
8567   int item;\r
8568   int flags;\r
8569 } Enables;\r
8570 \r
8571 VOID\r
8572 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8573 {\r
8574   while (enab->item > 0) {\r
8575     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8576     enab++;\r
8577   }\r
8578 }\r
8579 \r
8580 Enables gnuEnables[] = {\r
8581   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8582   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8583   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8584   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8585   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8586   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8587   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8588   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8589   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8590   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8591   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8592   { -1, -1 }\r
8593 };\r
8594 \r
8595 Enables icsEnables[] = {\r
8596   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8597   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8598   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8599   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8600   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8601   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8602   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8603   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8604   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8605   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8606   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8607   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8608   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8609   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8610   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8611   { -1, -1 }\r
8612 };\r
8613 \r
8614 #ifdef ZIPPY\r
8615 Enables zippyEnables[] = {\r
8616   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8617   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8618   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8619   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8620   { -1, -1 }\r
8621 };\r
8622 #endif\r
8623 \r
8624 Enables ncpEnables[] = {\r
8625   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8626   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8627   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8628   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8629   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8630   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8631   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8632   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8633   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8634   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8635   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8636   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8637   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8638   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8639   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8640   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8641   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8642   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8643   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8644   { -1, -1 }\r
8645 };\r
8646 \r
8647 Enables trainingOnEnables[] = {\r
8648   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8649   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8650   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8651   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8652   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8653   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8654   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8655   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8656   { -1, -1 }\r
8657 };\r
8658 \r
8659 Enables trainingOffEnables[] = {\r
8660   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8661   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8662   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8663   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8664   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8665   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8666   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8667   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8668   { -1, -1 }\r
8669 };\r
8670 \r
8671 /* These modify either ncpEnables or gnuEnables */\r
8672 Enables cmailEnables[] = {\r
8673   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8674   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8675   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8676   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8677   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8678   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8679   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8680   { -1, -1 }\r
8681 };\r
8682 \r
8683 Enables machineThinkingEnables[] = {\r
8684   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8685   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8686   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8687   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8688   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8689   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8690   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8691   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8692   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8693   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8694   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8695   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8696   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8697   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8698   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8699   { -1, -1 }\r
8700 };\r
8701 \r
8702 Enables userThinkingEnables[] = {\r
8703   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8704   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8705   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8706   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8707   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8708   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8709   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8710   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8711   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8712   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8713   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8714   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8715   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8716   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8717   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8718   { -1, -1 }\r
8719 };\r
8720 \r
8721 /*---------------------------------------------------------------------------*\\r
8722  *\r
8723  *  Front-end interface functions exported by XBoard.\r
8724  *  Functions appear in same order as prototypes in frontend.h.\r
8725  * \r
8726 \*---------------------------------------------------------------------------*/\r
8727 VOID\r
8728 ModeHighlight()\r
8729 {\r
8730   static UINT prevChecked = 0;\r
8731   static int prevPausing = 0;\r
8732   UINT nowChecked;\r
8733 \r
8734   if (pausing != prevPausing) {\r
8735     prevPausing = pausing;\r
8736     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8737                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8738     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8739   }\r
8740 \r
8741   switch (gameMode) {\r
8742   case BeginningOfGame:\r
8743     if (appData.icsActive)\r
8744       nowChecked = IDM_IcsClient;\r
8745     else if (appData.noChessProgram)\r
8746       nowChecked = IDM_EditGame;\r
8747     else\r
8748       nowChecked = IDM_MachineBlack;\r
8749     break;\r
8750   case MachinePlaysBlack:\r
8751     nowChecked = IDM_MachineBlack;\r
8752     break;\r
8753   case MachinePlaysWhite:\r
8754     nowChecked = IDM_MachineWhite;\r
8755     break;\r
8756   case TwoMachinesPlay:\r
8757     nowChecked = IDM_TwoMachines;\r
8758     break;\r
8759   case AnalyzeMode:\r
8760     nowChecked = IDM_AnalysisMode;\r
8761     break;\r
8762   case AnalyzeFile:\r
8763     nowChecked = IDM_AnalyzeFile;\r
8764     break;\r
8765   case EditGame:\r
8766     nowChecked = IDM_EditGame;\r
8767     break;\r
8768   case PlayFromGameFile:\r
8769     nowChecked = IDM_LoadGame;\r
8770     break;\r
8771   case EditPosition:\r
8772     nowChecked = IDM_EditPosition;\r
8773     break;\r
8774   case Training:\r
8775     nowChecked = IDM_Training;\r
8776     break;\r
8777   case IcsPlayingWhite:\r
8778   case IcsPlayingBlack:\r
8779   case IcsObserving:\r
8780   case IcsIdle:\r
8781     nowChecked = IDM_IcsClient;\r
8782     break;\r
8783   default:\r
8784   case EndOfGame:\r
8785     nowChecked = 0;\r
8786     break;\r
8787   }\r
8788   if (prevChecked != 0)\r
8789     (void) CheckMenuItem(GetMenu(hwndMain),\r
8790                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8791   if (nowChecked != 0)\r
8792     (void) CheckMenuItem(GetMenu(hwndMain),\r
8793                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8794 \r
8795   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8796     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8797                           MF_BYCOMMAND|MF_ENABLED);\r
8798   } else {\r
8799     (void) EnableMenuItem(GetMenu(hwndMain), \r
8800                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8801   }\r
8802 \r
8803   prevChecked = nowChecked;\r
8804 \r
8805   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8806   if (appData.icsActive) {\r
8807        if (appData.icsEngineAnalyze) {\r
8808                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8809                        MF_BYCOMMAND|MF_CHECKED);\r
8810        } else {\r
8811                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8812                        MF_BYCOMMAND|MF_UNCHECKED);\r
8813        }\r
8814   }\r
8815 }\r
8816 \r
8817 VOID\r
8818 SetICSMode()\r
8819 {\r
8820   HMENU hmenu = GetMenu(hwndMain);\r
8821   SetMenuEnables(hmenu, icsEnables);\r
8822   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8823     MF_BYPOSITION|MF_ENABLED);\r
8824 #ifdef ZIPPY\r
8825   if (appData.zippyPlay) {\r
8826     SetMenuEnables(hmenu, zippyEnables);\r
8827     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8828          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8829           MF_BYCOMMAND|MF_ENABLED);\r
8830   }\r
8831 #endif\r
8832 }\r
8833 \r
8834 VOID\r
8835 SetGNUMode()\r
8836 {\r
8837   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8838 }\r
8839 \r
8840 VOID\r
8841 SetNCPMode()\r
8842 {\r
8843   HMENU hmenu = GetMenu(hwndMain);\r
8844   SetMenuEnables(hmenu, ncpEnables);\r
8845   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8846     MF_BYPOSITION|MF_GRAYED);\r
8847     DrawMenuBar(hwndMain);\r
8848 }\r
8849 \r
8850 VOID\r
8851 SetCmailMode()\r
8852 {\r
8853   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8854 }\r
8855 \r
8856 VOID \r
8857 SetTrainingModeOn()\r
8858 {\r
8859   int i;\r
8860   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8861   for (i = 0; i < N_BUTTONS; i++) {\r
8862     if (buttonDesc[i].hwnd != NULL)\r
8863       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8864   }\r
8865   CommentPopDown();\r
8866 }\r
8867 \r
8868 VOID SetTrainingModeOff()\r
8869 {\r
8870   int i;\r
8871   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8872   for (i = 0; i < N_BUTTONS; i++) {\r
8873     if (buttonDesc[i].hwnd != NULL)\r
8874       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8875   }\r
8876 }\r
8877 \r
8878 \r
8879 VOID\r
8880 SetUserThinkingEnables()\r
8881 {\r
8882   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8883 }\r
8884 \r
8885 VOID\r
8886 SetMachineThinkingEnables()\r
8887 {\r
8888   HMENU hMenu = GetMenu(hwndMain);\r
8889   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8890 \r
8891   SetMenuEnables(hMenu, machineThinkingEnables);\r
8892 \r
8893   if (gameMode == MachinePlaysBlack) {\r
8894     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8895   } else if (gameMode == MachinePlaysWhite) {\r
8896     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8897   } else if (gameMode == TwoMachinesPlay) {\r
8898     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8899   }\r
8900 }\r
8901 \r
8902 \r
8903 VOID\r
8904 DisplayTitle(char *str)\r
8905 {\r
8906   char title[MSG_SIZ], *host;\r
8907   if (str[0] != NULLCHAR) {\r
8908     strcpy(title, str);\r
8909   } else if (appData.icsActive) {\r
8910     if (appData.icsCommPort[0] != NULLCHAR)\r
8911       host = "ICS";\r
8912     else \r
8913       host = appData.icsHost;\r
8914     sprintf(title, "%s: %s", szTitle, host);\r
8915   } else if (appData.noChessProgram) {\r
8916     strcpy(title, szTitle);\r
8917   } else {\r
8918     strcpy(title, szTitle);\r
8919     strcat(title, ": ");\r
8920     strcat(title, first.tidy);\r
8921   }\r
8922   SetWindowText(hwndMain, title);\r
8923 }\r
8924 \r
8925 \r
8926 VOID\r
8927 DisplayMessage(char *str1, char *str2)\r
8928 {\r
8929   HDC hdc;\r
8930   HFONT oldFont;\r
8931   int remain = MESSAGE_TEXT_MAX - 1;\r
8932   int len;\r
8933 \r
8934   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8935   messageText[0] = NULLCHAR;\r
8936   if (*str1) {\r
8937     len = strlen(str1);\r
8938     if (len > remain) len = remain;\r
8939     strncpy(messageText, str1, len);\r
8940     messageText[len] = NULLCHAR;\r
8941     remain -= len;\r
8942   }\r
8943   if (*str2 && remain >= 2) {\r
8944     if (*str1) {\r
8945       strcat(messageText, "  ");\r
8946       remain -= 2;\r
8947     }\r
8948     len = strlen(str2);\r
8949     if (len > remain) len = remain;\r
8950     strncat(messageText, str2, len);\r
8951   }\r
8952   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8953 \r
8954   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8955 \r
8956   SAYMACHINEMOVE();\r
8957 \r
8958   hdc = GetDC(hwndMain);\r
8959   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8960   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8961              &messageRect, messageText, strlen(messageText), NULL);\r
8962   (void) SelectObject(hdc, oldFont);\r
8963   (void) ReleaseDC(hwndMain, hdc);\r
8964 }\r
8965 \r
8966 VOID\r
8967 DisplayError(char *str, int error)\r
8968 {\r
8969   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8970   int len;\r
8971 \r
8972   if (error == 0) {\r
8973     strcpy(buf, str);\r
8974   } else {\r
8975     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8976                         NULL, error, LANG_NEUTRAL,\r
8977                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8978     if (len > 0) {\r
8979       sprintf(buf, "%s:\n%s", str, buf2);\r
8980     } else {\r
8981       ErrorMap *em = errmap;\r
8982       while (em->err != 0 && em->err != error) em++;\r
8983       if (em->err != 0) {\r
8984         sprintf(buf, "%s:\n%s", str, em->msg);\r
8985       } else {\r
8986         sprintf(buf, "%s:\nError code %d", str, error);\r
8987       }\r
8988     }\r
8989   }\r
8990   \r
8991   ErrorPopUp("Error", buf);\r
8992 }\r
8993 \r
8994 \r
8995 VOID\r
8996 DisplayMoveError(char *str)\r
8997 {\r
8998   fromX = fromY = -1;\r
8999   ClearHighlights();\r
9000   DrawPosition(FALSE, NULL);\r
9001   if (appData.popupMoveErrors) {\r
9002     ErrorPopUp("Error", str);\r
9003   } else {\r
9004     DisplayMessage(str, "");\r
9005     moveErrorMessageUp = TRUE;\r
9006   }\r
9007 }\r
9008 \r
9009 VOID\r
9010 DisplayFatalError(char *str, int error, int exitStatus)\r
9011 {\r
9012   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9013   int len;\r
9014   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9015 \r
9016   if (error != 0) {\r
9017     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9018                         NULL, error, LANG_NEUTRAL,\r
9019                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9020     if (len > 0) {\r
9021       sprintf(buf, "%s:\n%s", str, buf2);\r
9022     } else {\r
9023       ErrorMap *em = errmap;\r
9024       while (em->err != 0 && em->err != error) em++;\r
9025       if (em->err != 0) {\r
9026         sprintf(buf, "%s:\n%s", str, em->msg);\r
9027       } else {\r
9028         sprintf(buf, "%s:\nError code %d", str, error);\r
9029       }\r
9030     }\r
9031     str = buf;\r
9032   }\r
9033   if (appData.debugMode) {\r
9034     fprintf(debugFP, "%s: %s\n", label, str);\r
9035   }\r
9036   if (appData.popupExitMessage) {\r
9037     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9038                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9039   }\r
9040   ExitEvent(exitStatus);\r
9041 }\r
9042 \r
9043 \r
9044 VOID\r
9045 DisplayInformation(char *str)\r
9046 {\r
9047   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9048 }\r
9049 \r
9050 \r
9051 VOID\r
9052 DisplayNote(char *str)\r
9053 {\r
9054   ErrorPopUp("Note", str);\r
9055 }\r
9056 \r
9057 \r
9058 typedef struct {\r
9059   char *title, *question, *replyPrefix;\r
9060   ProcRef pr;\r
9061 } QuestionParams;\r
9062 \r
9063 LRESULT CALLBACK\r
9064 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9065 {\r
9066   static QuestionParams *qp;\r
9067   char reply[MSG_SIZ];\r
9068   int len, err;\r
9069 \r
9070   switch (message) {\r
9071   case WM_INITDIALOG:\r
9072     qp = (QuestionParams *) lParam;\r
9073     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9074     SetWindowText(hDlg, qp->title);\r
9075     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9076     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9077     return FALSE;\r
9078 \r
9079   case WM_COMMAND:\r
9080     switch (LOWORD(wParam)) {\r
9081     case IDOK:\r
9082       strcpy(reply, qp->replyPrefix);\r
9083       if (*reply) strcat(reply, " ");\r
9084       len = strlen(reply);\r
9085       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9086       strcat(reply, "\n");\r
9087       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9088       EndDialog(hDlg, TRUE);\r
9089       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9090       return TRUE;\r
9091     case IDCANCEL:\r
9092       EndDialog(hDlg, FALSE);\r
9093       return TRUE;\r
9094     default:\r
9095       break;\r
9096     }\r
9097     break;\r
9098   }\r
9099   return FALSE;\r
9100 }\r
9101 \r
9102 VOID\r
9103 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9104 {\r
9105     QuestionParams qp;\r
9106     FARPROC lpProc;\r
9107     \r
9108     qp.title = title;\r
9109     qp.question = question;\r
9110     qp.replyPrefix = replyPrefix;\r
9111     qp.pr = pr;\r
9112     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9113     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9114       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9115     FreeProcInstance(lpProc);\r
9116 }\r
9117 \r
9118 /* [AS] Pick FRC position */\r
9119 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9120 {\r
9121     static int * lpIndexFRC;\r
9122     BOOL index_is_ok;\r
9123     char buf[16];\r
9124 \r
9125     switch( message )\r
9126     {\r
9127     case WM_INITDIALOG:\r
9128         lpIndexFRC = (int *) lParam;\r
9129 \r
9130         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9131 \r
9132         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9133         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9134         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9135         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9136 \r
9137         break;\r
9138 \r
9139     case WM_COMMAND:\r
9140         switch( LOWORD(wParam) ) {\r
9141         case IDOK:\r
9142             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9143             EndDialog( hDlg, 0 );\r
9144             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9145             return TRUE;\r
9146         case IDCANCEL:\r
9147             EndDialog( hDlg, 1 );   \r
9148             return TRUE;\r
9149         case IDC_NFG_Edit:\r
9150             if( HIWORD(wParam) == EN_CHANGE ) {\r
9151                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9152 \r
9153                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9154             }\r
9155             return TRUE;\r
9156         case IDC_NFG_Random:\r
9157             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9158             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9159             return TRUE;\r
9160         }\r
9161 \r
9162         break;\r
9163     }\r
9164 \r
9165     return FALSE;\r
9166 }\r
9167 \r
9168 int NewGameFRC()\r
9169 {\r
9170     int result;\r
9171     int index = appData.defaultFrcPosition;\r
9172     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9173 \r
9174     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9175 \r
9176     if( result == 0 ) {\r
9177         appData.defaultFrcPosition = index;\r
9178     }\r
9179 \r
9180     return result;\r
9181 }\r
9182 \r
9183 /* [AS] Game list options */\r
9184 typedef struct {\r
9185     char id;\r
9186     char * name;\r
9187 } GLT_Item;\r
9188 \r
9189 static GLT_Item GLT_ItemInfo[] = {\r
9190     { GLT_EVENT,      "Event" },\r
9191     { GLT_SITE,       "Site" },\r
9192     { GLT_DATE,       "Date" },\r
9193     { GLT_ROUND,      "Round" },\r
9194     { GLT_PLAYERS,    "Players" },\r
9195     { GLT_RESULT,     "Result" },\r
9196     { GLT_WHITE_ELO,  "White Rating" },\r
9197     { GLT_BLACK_ELO,  "Black Rating" },\r
9198     { GLT_TIME_CONTROL,"Time Control" },\r
9199     { GLT_VARIANT,    "Variant" },\r
9200     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9201     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9202     { 0, 0 }\r
9203 };\r
9204 \r
9205 const char * GLT_FindItem( char id )\r
9206 {\r
9207     const char * result = 0;\r
9208 \r
9209     GLT_Item * list = GLT_ItemInfo;\r
9210 \r
9211     while( list->id != 0 ) {\r
9212         if( list->id == id ) {\r
9213             result = list->name;\r
9214             break;\r
9215         }\r
9216 \r
9217         list++;\r
9218     }\r
9219 \r
9220     return result;\r
9221 }\r
9222 \r
9223 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9224 {\r
9225     const char * name = GLT_FindItem( id );\r
9226 \r
9227     if( name != 0 ) {\r
9228         if( index >= 0 ) {\r
9229             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9230         }\r
9231         else {\r
9232             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9233         }\r
9234     }\r
9235 }\r
9236 \r
9237 void GLT_TagsToList( HWND hDlg, char * tags )\r
9238 {\r
9239     char * pc = tags;\r
9240 \r
9241     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9242 \r
9243     while( *pc ) {\r
9244         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9245         pc++;\r
9246     }\r
9247 \r
9248     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9249 \r
9250     pc = GLT_ALL_TAGS;\r
9251 \r
9252     while( *pc ) {\r
9253         if( strchr( tags, *pc ) == 0 ) {\r
9254             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9255         }\r
9256         pc++;\r
9257     }\r
9258 \r
9259     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9260 }\r
9261 \r
9262 char GLT_ListItemToTag( HWND hDlg, int index )\r
9263 {\r
9264     char result = '\0';\r
9265     char name[128];\r
9266 \r
9267     GLT_Item * list = GLT_ItemInfo;\r
9268 \r
9269     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9270         while( list->id != 0 ) {\r
9271             if( strcmp( list->name, name ) == 0 ) {\r
9272                 result = list->id;\r
9273                 break;\r
9274             }\r
9275 \r
9276             list++;\r
9277         }\r
9278     }\r
9279 \r
9280     return result;\r
9281 }\r
9282 \r
9283 void GLT_MoveSelection( HWND hDlg, int delta )\r
9284 {\r
9285     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9286     int idx2 = idx1 + delta;\r
9287     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9288 \r
9289     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9290         char buf[128];\r
9291 \r
9292         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9293         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9294         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9295         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9296     }\r
9297 }\r
9298 \r
9299 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9300 {\r
9301     static char glt[64];\r
9302     static char * lpUserGLT;\r
9303 \r
9304     switch( message )\r
9305     {\r
9306     case WM_INITDIALOG:\r
9307         lpUserGLT = (char *) lParam;\r
9308         \r
9309         strcpy( glt, lpUserGLT );\r
9310 \r
9311         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9312 \r
9313         /* Initialize list */\r
9314         GLT_TagsToList( hDlg, glt );\r
9315 \r
9316         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9317 \r
9318         break;\r
9319 \r
9320     case WM_COMMAND:\r
9321         switch( LOWORD(wParam) ) {\r
9322         case IDOK:\r
9323             {\r
9324                 char * pc = lpUserGLT;\r
9325                 int idx = 0;\r
9326 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9327                 char id;\r
9328 \r
9329                 do {\r
9330                     id = GLT_ListItemToTag( hDlg, idx );\r
9331 \r
9332                     *pc++ = id;\r
9333                     idx++;\r
9334                 } while( id != '\0' );\r
9335             }\r
9336             EndDialog( hDlg, 0 );\r
9337             return TRUE;\r
9338         case IDCANCEL:\r
9339             EndDialog( hDlg, 1 );\r
9340             return TRUE;\r
9341 \r
9342         case IDC_GLT_Default:\r
9343             strcpy( glt, GLT_DEFAULT_TAGS );\r
9344             GLT_TagsToList( hDlg, glt );\r
9345             return TRUE;\r
9346 \r
9347         case IDC_GLT_Restore:\r
9348             strcpy( glt, lpUserGLT );\r
9349             GLT_TagsToList( hDlg, glt );\r
9350             return TRUE;\r
9351 \r
9352         case IDC_GLT_Up:\r
9353             GLT_MoveSelection( hDlg, -1 );\r
9354             return TRUE;\r
9355 \r
9356         case IDC_GLT_Down:\r
9357             GLT_MoveSelection( hDlg, +1 );\r
9358             return TRUE;\r
9359         }\r
9360 \r
9361         break;\r
9362     }\r
9363 \r
9364     return FALSE;\r
9365 }\r
9366 \r
9367 int GameListOptions()\r
9368 {\r
9369     char glt[64];\r
9370     int result;\r
9371     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9372 \r
9373     strcpy( glt, appData.gameListTags );\r
9374 \r
9375     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9376 \r
9377     if( result == 0 ) {\r
9378         /* [AS] Memory leak here! */\r
9379         appData.gameListTags = strdup( glt ); \r
9380     }\r
9381 \r
9382     return result;\r
9383 }\r
9384 \r
9385 \r
9386 VOID\r
9387 DisplayIcsInteractionTitle(char *str)\r
9388 {\r
9389   char consoleTitle[MSG_SIZ];\r
9390 \r
9391   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9392   SetWindowText(hwndConsole, consoleTitle);\r
9393 }\r
9394 \r
9395 void\r
9396 DrawPosition(int fullRedraw, Board board)\r
9397 {\r
9398   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9399 }\r
9400 \r
9401 void NotifyFrontendLogin()\r
9402 {\r
9403         if (hwndConsole)\r
9404                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
9405 }\r
9406 \r
9407 VOID\r
9408 ResetFrontEnd()\r
9409 {\r
9410   fromX = fromY = -1;\r
9411   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9412     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9413     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9414     dragInfo.lastpos = dragInfo.pos;\r
9415     dragInfo.start.x = dragInfo.start.y = -1;\r
9416     dragInfo.from = dragInfo.start;\r
9417     ReleaseCapture();\r
9418     DrawPosition(TRUE, NULL);\r
9419   }\r
9420 }\r
9421 \r
9422 \r
9423 VOID\r
9424 CommentPopUp(char *title, char *str)\r
9425 {\r
9426   HWND hwnd = GetActiveWindow();\r
9427   EitherCommentPopUp(0, title, str, FALSE);\r
9428   SAY(str);\r
9429   SetActiveWindow(hwnd);\r
9430 }\r
9431 \r
9432 VOID\r
9433 CommentPopDown(void)\r
9434 {\r
9435   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9436   if (commentDialog) {\r
9437     ShowWindow(commentDialog, SW_HIDE);\r
9438   }\r
9439   commentDialogUp = FALSE;\r
9440 }\r
9441 \r
9442 VOID\r
9443 EditCommentPopUp(int index, char *title, char *str)\r
9444 {\r
9445   EitherCommentPopUp(index, title, str, TRUE);\r
9446 }\r
9447 \r
9448 \r
9449 VOID\r
9450 RingBell()\r
9451 {\r
9452   MyPlaySound(&sounds[(int)SoundMove]);\r
9453 }\r
9454 \r
9455 VOID PlayIcsWinSound()\r
9456 {\r
9457   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9458 }\r
9459 \r
9460 VOID PlayIcsLossSound()\r
9461 {\r
9462   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9463 }\r
9464 \r
9465 VOID PlayIcsDrawSound()\r
9466 {\r
9467   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9468 }\r
9469 \r
9470 VOID PlayIcsUnfinishedSound()\r
9471 {\r
9472   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9473 }\r
9474 \r
9475 VOID\r
9476 PlayAlarmSound()\r
9477 {\r
9478   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9479 }\r
9480 \r
9481 \r
9482 VOID\r
9483 EchoOn()\r
9484 {\r
9485   HWND hInput;\r
9486   consoleEcho = TRUE;\r
9487   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9488   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9489   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9490 }\r
9491 \r
9492 \r
9493 VOID\r
9494 EchoOff()\r
9495 {\r
9496   CHARFORMAT cf;\r
9497   HWND hInput;\r
9498   consoleEcho = FALSE;\r
9499   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9500   /* This works OK: set text and background both to the same color */\r
9501   cf = consoleCF;\r
9502   cf.crTextColor = COLOR_ECHOOFF;\r
9503   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9504   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9505 }\r
9506 \r
9507 /* No Raw()...? */\r
9508 \r
9509 void Colorize(ColorClass cc, int continuation)\r
9510 {\r
9511   currentColorClass = cc;\r
9512   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9513   consoleCF.crTextColor = textAttribs[cc].color;\r
9514   consoleCF.dwEffects = textAttribs[cc].effects;\r
9515   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9516 }\r
9517 \r
9518 char *\r
9519 UserName()\r
9520 {\r
9521   static char buf[MSG_SIZ];\r
9522   DWORD bufsiz = MSG_SIZ;\r
9523 \r
9524   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9525         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9526   }\r
9527   if (!GetUserName(buf, &bufsiz)) {\r
9528     /*DisplayError("Error getting user name", GetLastError());*/\r
9529     strcpy(buf, "User");\r
9530   }\r
9531   return buf;\r
9532 }\r
9533 \r
9534 char *\r
9535 HostName()\r
9536 {\r
9537   static char buf[MSG_SIZ];\r
9538   DWORD bufsiz = MSG_SIZ;\r
9539 \r
9540   if (!GetComputerName(buf, &bufsiz)) {\r
9541     /*DisplayError("Error getting host name", GetLastError());*/\r
9542     strcpy(buf, "Unknown");\r
9543   }\r
9544   return buf;\r
9545 }\r
9546 \r
9547 \r
9548 int\r
9549 ClockTimerRunning()\r
9550 {\r
9551   return clockTimerEvent != 0;\r
9552 }\r
9553 \r
9554 int\r
9555 StopClockTimer()\r
9556 {\r
9557   if (clockTimerEvent == 0) return FALSE;\r
9558   KillTimer(hwndMain, clockTimerEvent);\r
9559   clockTimerEvent = 0;\r
9560   return TRUE;\r
9561 }\r
9562 \r
9563 void\r
9564 StartClockTimer(long millisec)\r
9565 {\r
9566   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9567                              (UINT) millisec, NULL);\r
9568 }\r
9569 \r
9570 void\r
9571 DisplayWhiteClock(long timeRemaining, int highlight)\r
9572 {\r
9573   HDC hdc;\r
9574   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9575 \r
9576   if(appData.noGUI) return;\r
9577   hdc = GetDC(hwndMain);\r
9578   if (!IsIconic(hwndMain)) {\r
9579     DisplayAClock(hdc, timeRemaining, highlight, \r
9580                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9581   }\r
9582   if (highlight && iconCurrent == iconBlack) {\r
9583     iconCurrent = iconWhite;\r
9584     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9585     if (IsIconic(hwndMain)) {\r
9586       DrawIcon(hdc, 2, 2, iconCurrent);\r
9587     }\r
9588   }\r
9589   (void) ReleaseDC(hwndMain, hdc);\r
9590   if (hwndConsole)\r
9591     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9592 }\r
9593 \r
9594 void\r
9595 DisplayBlackClock(long timeRemaining, int highlight)\r
9596 {\r
9597   HDC hdc;\r
9598   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9599 \r
9600   if(appData.noGUI) return;\r
9601   hdc = GetDC(hwndMain);\r
9602   if (!IsIconic(hwndMain)) {\r
9603     DisplayAClock(hdc, timeRemaining, highlight, \r
9604                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9605   }\r
9606   if (highlight && iconCurrent == iconWhite) {\r
9607     iconCurrent = iconBlack;\r
9608     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9609     if (IsIconic(hwndMain)) {\r
9610       DrawIcon(hdc, 2, 2, iconCurrent);\r
9611     }\r
9612   }\r
9613   (void) ReleaseDC(hwndMain, hdc);\r
9614   if (hwndConsole)\r
9615     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9616 }\r
9617 \r
9618 \r
9619 int\r
9620 LoadGameTimerRunning()\r
9621 {\r
9622   return loadGameTimerEvent != 0;\r
9623 }\r
9624 \r
9625 int\r
9626 StopLoadGameTimer()\r
9627 {\r
9628   if (loadGameTimerEvent == 0) return FALSE;\r
9629   KillTimer(hwndMain, loadGameTimerEvent);\r
9630   loadGameTimerEvent = 0;\r
9631   return TRUE;\r
9632 }\r
9633 \r
9634 void\r
9635 StartLoadGameTimer(long millisec)\r
9636 {\r
9637   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9638                                 (UINT) millisec, NULL);\r
9639 }\r
9640 \r
9641 void\r
9642 AutoSaveGame()\r
9643 {\r
9644   char *defName;\r
9645   FILE *f;\r
9646   char fileTitle[MSG_SIZ];\r
9647 \r
9648   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9649   f = OpenFileDialog(hwndMain, "a", defName,\r
9650                      appData.oldSaveStyle ? "gam" : "pgn",\r
9651                      GAME_FILT, \r
9652                      "Save Game to File", NULL, fileTitle, NULL);\r
9653   if (f != NULL) {\r
9654     SaveGame(f, 0, "");\r
9655     fclose(f);\r
9656   }\r
9657 }\r
9658 \r
9659 \r
9660 void\r
9661 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9662 {\r
9663   if (delayedTimerEvent != 0) {\r
9664     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9665       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9666     }\r
9667     KillTimer(hwndMain, delayedTimerEvent);\r
9668     delayedTimerEvent = 0;\r
9669     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9670     delayedTimerCallback();\r
9671   }\r
9672   delayedTimerCallback = cb;\r
9673   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9674                                 (UINT) millisec, NULL);\r
9675 }\r
9676 \r
9677 DelayedEventCallback\r
9678 GetDelayedEvent()\r
9679 {\r
9680   if (delayedTimerEvent) {\r
9681     return delayedTimerCallback;\r
9682   } else {\r
9683     return NULL;\r
9684   }\r
9685 }\r
9686 \r
9687 void\r
9688 CancelDelayedEvent()\r
9689 {\r
9690   if (delayedTimerEvent) {\r
9691     KillTimer(hwndMain, delayedTimerEvent);\r
9692     delayedTimerEvent = 0;\r
9693   }\r
9694 }\r
9695 \r
9696 DWORD GetWin32Priority(int nice)\r
9697 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9698 /*\r
9699 REALTIME_PRIORITY_CLASS     0x00000100\r
9700 HIGH_PRIORITY_CLASS         0x00000080\r
9701 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9702 NORMAL_PRIORITY_CLASS       0x00000020\r
9703 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9704 IDLE_PRIORITY_CLASS         0x00000040\r
9705 */\r
9706         if (nice < -15) return 0x00000080;\r
9707         if (nice < 0)   return 0x00008000;\r
9708         if (nice == 0)  return 0x00000020;\r
9709         if (nice < 15)  return 0x00004000;\r
9710         return 0x00000040;\r
9711 }\r
9712 \r
9713 /* Start a child process running the given program.\r
9714    The process's standard output can be read from "from", and its\r
9715    standard input can be written to "to".\r
9716    Exit with fatal error if anything goes wrong.\r
9717    Returns an opaque pointer that can be used to destroy the process\r
9718    later.\r
9719 */\r
9720 int\r
9721 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9722 {\r
9723 #define BUFSIZE 4096\r
9724 \r
9725   HANDLE hChildStdinRd, hChildStdinWr,\r
9726     hChildStdoutRd, hChildStdoutWr;\r
9727   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9728   SECURITY_ATTRIBUTES saAttr;\r
9729   BOOL fSuccess;\r
9730   PROCESS_INFORMATION piProcInfo;\r
9731   STARTUPINFO siStartInfo;\r
9732   ChildProc *cp;\r
9733   char buf[MSG_SIZ];\r
9734   DWORD err;\r
9735 \r
9736   if (appData.debugMode) {\r
9737     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9738   }\r
9739 \r
9740   *pr = NoProc;\r
9741 \r
9742   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9743   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9744   saAttr.bInheritHandle = TRUE;\r
9745   saAttr.lpSecurityDescriptor = NULL;\r
9746 \r
9747   /*\r
9748    * The steps for redirecting child's STDOUT:\r
9749    *     1. Create anonymous pipe to be STDOUT for child.\r
9750    *     2. Create a noninheritable duplicate of read handle,\r
9751    *         and close the inheritable read handle.\r
9752    */\r
9753 \r
9754   /* Create a pipe for the child's STDOUT. */\r
9755   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9756     return GetLastError();\r
9757   }\r
9758 \r
9759   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9760   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9761                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9762                              FALSE,     /* not inherited */\r
9763                              DUPLICATE_SAME_ACCESS);\r
9764   if (! fSuccess) {\r
9765     return GetLastError();\r
9766   }\r
9767   CloseHandle(hChildStdoutRd);\r
9768 \r
9769   /*\r
9770    * The steps for redirecting child's STDIN:\r
9771    *     1. Create anonymous pipe to be STDIN for child.\r
9772    *     2. Create a noninheritable duplicate of write handle,\r
9773    *         and close the inheritable write handle.\r
9774    */\r
9775 \r
9776   /* Create a pipe for the child's STDIN. */\r
9777   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9778     return GetLastError();\r
9779   }\r
9780 \r
9781   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9782   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9783                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9784                              FALSE,     /* not inherited */\r
9785                              DUPLICATE_SAME_ACCESS);\r
9786   if (! fSuccess) {\r
9787     return GetLastError();\r
9788   }\r
9789   CloseHandle(hChildStdinWr);\r
9790 \r
9791   /* Arrange to (1) look in dir for the child .exe file, and\r
9792    * (2) have dir be the child's working directory.  Interpret\r
9793    * dir relative to the directory WinBoard loaded from. */\r
9794   GetCurrentDirectory(MSG_SIZ, buf);\r
9795   SetCurrentDirectory(installDir);\r
9796   SetCurrentDirectory(dir);\r
9797 \r
9798   /* Now create the child process. */\r
9799 \r
9800   siStartInfo.cb = sizeof(STARTUPINFO);\r
9801   siStartInfo.lpReserved = NULL;\r
9802   siStartInfo.lpDesktop = NULL;\r
9803   siStartInfo.lpTitle = NULL;\r
9804   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9805   siStartInfo.cbReserved2 = 0;\r
9806   siStartInfo.lpReserved2 = NULL;\r
9807   siStartInfo.hStdInput = hChildStdinRd;\r
9808   siStartInfo.hStdOutput = hChildStdoutWr;\r
9809   siStartInfo.hStdError = hChildStdoutWr;\r
9810 \r
9811   fSuccess = CreateProcess(NULL,\r
9812                            cmdLine,        /* command line */\r
9813                            NULL,           /* process security attributes */\r
9814                            NULL,           /* primary thread security attrs */\r
9815                            TRUE,           /* handles are inherited */\r
9816                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9817                            NULL,           /* use parent's environment */\r
9818                            NULL,\r
9819                            &siStartInfo, /* STARTUPINFO pointer */\r
9820                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9821 \r
9822   err = GetLastError();\r
9823   SetCurrentDirectory(buf); /* return to prev directory */\r
9824   if (! fSuccess) {\r
9825     return err;\r
9826   }\r
9827 \r
9828   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9829     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9830     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9831   }\r
9832 \r
9833   /* Close the handles we don't need in the parent */\r
9834   CloseHandle(piProcInfo.hThread);\r
9835   CloseHandle(hChildStdinRd);\r
9836   CloseHandle(hChildStdoutWr);\r
9837 \r
9838   /* Prepare return value */\r
9839   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9840   cp->kind = CPReal;\r
9841   cp->hProcess = piProcInfo.hProcess;\r
9842   cp->pid = piProcInfo.dwProcessId;\r
9843   cp->hFrom = hChildStdoutRdDup;\r
9844   cp->hTo = hChildStdinWrDup;\r
9845 \r
9846   *pr = (void *) cp;\r
9847 \r
9848   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9849      2000 where engines sometimes don't see the initial command(s)\r
9850      from WinBoard and hang.  I don't understand how that can happen,\r
9851      but the Sleep is harmless, so I've put it in.  Others have also\r
9852      reported what may be the same problem, so hopefully this will fix\r
9853      it for them too.  */\r
9854   Sleep(500);\r
9855 \r
9856   return NO_ERROR;\r
9857 }\r
9858 \r
9859 \r
9860 void\r
9861 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9862 {\r
9863   ChildProc *cp; int result;\r
9864 \r
9865   cp = (ChildProc *) pr;\r
9866   if (cp == NULL) return;\r
9867 \r
9868   switch (cp->kind) {\r
9869   case CPReal:\r
9870     /* TerminateProcess is considered harmful, so... */\r
9871     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9872     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9873     /* The following doesn't work because the chess program\r
9874        doesn't "have the same console" as WinBoard.  Maybe\r
9875        we could arrange for this even though neither WinBoard\r
9876        nor the chess program uses a console for stdio? */\r
9877     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9878 \r
9879     /* [AS] Special termination modes for misbehaving programs... */\r
9880     if( signal == 9 ) { \r
9881         result = TerminateProcess( cp->hProcess, 0 );\r
9882 \r
9883         if ( appData.debugMode) {\r
9884             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9885         }\r
9886     }\r
9887     else if( signal == 10 ) {\r
9888         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9889 \r
9890         if( dw != WAIT_OBJECT_0 ) {\r
9891             result = TerminateProcess( cp->hProcess, 0 );\r
9892 \r
9893             if ( appData.debugMode) {\r
9894                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9895             }\r
9896 \r
9897         }\r
9898     }\r
9899 \r
9900     CloseHandle(cp->hProcess);\r
9901     break;\r
9902 \r
9903   case CPComm:\r
9904     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9905     break;\r
9906 \r
9907   case CPSock:\r
9908     closesocket(cp->sock);\r
9909     WSACleanup();\r
9910     break;\r
9911 \r
9912   case CPRcmd:\r
9913     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9914     closesocket(cp->sock);\r
9915     closesocket(cp->sock2);\r
9916     WSACleanup();\r
9917     break;\r
9918   }\r
9919   free(cp);\r
9920 }\r
9921 \r
9922 void\r
9923 InterruptChildProcess(ProcRef pr)\r
9924 {\r
9925   ChildProc *cp;\r
9926 \r
9927   cp = (ChildProc *) pr;\r
9928   if (cp == NULL) return;\r
9929   switch (cp->kind) {\r
9930   case CPReal:\r
9931     /* The following doesn't work because the chess program\r
9932        doesn't "have the same console" as WinBoard.  Maybe\r
9933        we could arrange for this even though neither WinBoard\r
9934        nor the chess program uses a console for stdio */\r
9935     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9936     break;\r
9937 \r
9938   case CPComm:\r
9939   case CPSock:\r
9940     /* Can't interrupt */\r
9941     break;\r
9942 \r
9943   case CPRcmd:\r
9944     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9945     break;\r
9946   }\r
9947 }\r
9948 \r
9949 \r
9950 int\r
9951 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9952 {\r
9953   char cmdLine[MSG_SIZ];\r
9954 \r
9955   if (port[0] == NULLCHAR) {\r
9956     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9957   } else {\r
9958     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9959   }\r
9960   return StartChildProcess(cmdLine, "", pr);\r
9961 }\r
9962 \r
9963 \r
9964 /* Code to open TCP sockets */\r
9965 \r
9966 int\r
9967 OpenTCP(char *host, char *port, ProcRef *pr)\r
9968 {\r
9969   ChildProc *cp;\r
9970   int err;\r
9971   SOCKET s;\r
9972   struct sockaddr_in sa, mysa;\r
9973   struct hostent FAR *hp;\r
9974   unsigned short uport;\r
9975   WORD wVersionRequested;\r
9976   WSADATA wsaData;\r
9977 \r
9978   /* Initialize socket DLL */\r
9979   wVersionRequested = MAKEWORD(1, 1);\r
9980   err = WSAStartup(wVersionRequested, &wsaData);\r
9981   if (err != 0) return err;\r
9982 \r
9983   /* Make socket */\r
9984   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9985     err = WSAGetLastError();\r
9986     WSACleanup();\r
9987     return err;\r
9988   }\r
9989 \r
9990   /* Bind local address using (mostly) don't-care values.\r
9991    */\r
9992   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9993   mysa.sin_family = AF_INET;\r
9994   mysa.sin_addr.s_addr = INADDR_ANY;\r
9995   uport = (unsigned short) 0;\r
9996   mysa.sin_port = htons(uport);\r
9997   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9998       == SOCKET_ERROR) {\r
9999     err = WSAGetLastError();\r
10000     WSACleanup();\r
10001     return err;\r
10002   }\r
10003 \r
10004   /* Resolve remote host name */\r
10005   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10006   if (!(hp = gethostbyname(host))) {\r
10007     unsigned int b0, b1, b2, b3;\r
10008 \r
10009     err = WSAGetLastError();\r
10010 \r
10011     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10012       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10013       hp->h_addrtype = AF_INET;\r
10014       hp->h_length = 4;\r
10015       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10016       hp->h_addr_list[0] = (char *) malloc(4);\r
10017       hp->h_addr_list[0][0] = (char) b0;\r
10018       hp->h_addr_list[0][1] = (char) b1;\r
10019       hp->h_addr_list[0][2] = (char) b2;\r
10020       hp->h_addr_list[0][3] = (char) b3;\r
10021     } else {\r
10022       WSACleanup();\r
10023       return err;\r
10024     }\r
10025   }\r
10026   sa.sin_family = hp->h_addrtype;\r
10027   uport = (unsigned short) atoi(port);\r
10028   sa.sin_port = htons(uport);\r
10029   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10030 \r
10031   /* Make connection */\r
10032   if (connect(s, (struct sockaddr *) &sa,\r
10033               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10034     err = WSAGetLastError();\r
10035     WSACleanup();\r
10036     return err;\r
10037   }\r
10038 \r
10039   /* Prepare return value */\r
10040   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10041   cp->kind = CPSock;\r
10042   cp->sock = s;\r
10043   *pr = (ProcRef *) cp;\r
10044 \r
10045   return NO_ERROR;\r
10046 }\r
10047 \r
10048 int\r
10049 OpenCommPort(char *name, ProcRef *pr)\r
10050 {\r
10051   HANDLE h;\r
10052   COMMTIMEOUTS ct;\r
10053   ChildProc *cp;\r
10054   char fullname[MSG_SIZ];\r
10055 \r
10056   if (*name != '\\')\r
10057     sprintf(fullname, "\\\\.\\%s", name);\r
10058   else\r
10059     strcpy(fullname, name);\r
10060 \r
10061   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10062                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10063   if (h == (HANDLE) -1) {\r
10064     return GetLastError();\r
10065   }\r
10066   hCommPort = h;\r
10067 \r
10068   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10069 \r
10070   /* Accumulate characters until a 100ms pause, then parse */\r
10071   ct.ReadIntervalTimeout = 100;\r
10072   ct.ReadTotalTimeoutMultiplier = 0;\r
10073   ct.ReadTotalTimeoutConstant = 0;\r
10074   ct.WriteTotalTimeoutMultiplier = 0;\r
10075   ct.WriteTotalTimeoutConstant = 0;\r
10076   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10077 \r
10078   /* Prepare return value */\r
10079   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10080   cp->kind = CPComm;\r
10081   cp->hFrom = h;\r
10082   cp->hTo = h;\r
10083   *pr = (ProcRef *) cp;\r
10084 \r
10085   return NO_ERROR;\r
10086 }\r
10087 \r
10088 int\r
10089 OpenLoopback(ProcRef *pr)\r
10090 {\r
10091   DisplayFatalError("Not implemented", 0, 1);\r
10092   return NO_ERROR;\r
10093 }\r
10094 \r
10095 \r
10096 int\r
10097 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10098 {\r
10099   ChildProc *cp;\r
10100   int err;\r
10101   SOCKET s, s2, s3;\r
10102   struct sockaddr_in sa, mysa;\r
10103   struct hostent FAR *hp;\r
10104   unsigned short uport;\r
10105   WORD wVersionRequested;\r
10106   WSADATA wsaData;\r
10107   int fromPort;\r
10108   char stderrPortStr[MSG_SIZ];\r
10109 \r
10110   /* Initialize socket DLL */\r
10111   wVersionRequested = MAKEWORD(1, 1);\r
10112   err = WSAStartup(wVersionRequested, &wsaData);\r
10113   if (err != 0) return err;\r
10114 \r
10115   /* Resolve remote host name */\r
10116   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10117   if (!(hp = gethostbyname(host))) {\r
10118     unsigned int b0, b1, b2, b3;\r
10119 \r
10120     err = WSAGetLastError();\r
10121 \r
10122     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10123       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10124       hp->h_addrtype = AF_INET;\r
10125       hp->h_length = 4;\r
10126       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10127       hp->h_addr_list[0] = (char *) malloc(4);\r
10128       hp->h_addr_list[0][0] = (char) b0;\r
10129       hp->h_addr_list[0][1] = (char) b1;\r
10130       hp->h_addr_list[0][2] = (char) b2;\r
10131       hp->h_addr_list[0][3] = (char) b3;\r
10132     } else {\r
10133       WSACleanup();\r
10134       return err;\r
10135     }\r
10136   }\r
10137   sa.sin_family = hp->h_addrtype;\r
10138   uport = (unsigned short) 514;\r
10139   sa.sin_port = htons(uport);\r
10140   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10141 \r
10142   /* Bind local socket to unused "privileged" port address\r
10143    */\r
10144   s = INVALID_SOCKET;\r
10145   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10146   mysa.sin_family = AF_INET;\r
10147   mysa.sin_addr.s_addr = INADDR_ANY;\r
10148   for (fromPort = 1023;; fromPort--) {\r
10149     if (fromPort < 0) {\r
10150       WSACleanup();\r
10151       return WSAEADDRINUSE;\r
10152     }\r
10153     if (s == INVALID_SOCKET) {\r
10154       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10155         err = WSAGetLastError();\r
10156         WSACleanup();\r
10157         return err;\r
10158       }\r
10159     }\r
10160     uport = (unsigned short) fromPort;\r
10161     mysa.sin_port = htons(uport);\r
10162     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10163         == SOCKET_ERROR) {\r
10164       err = WSAGetLastError();\r
10165       if (err == WSAEADDRINUSE) continue;\r
10166       WSACleanup();\r
10167       return err;\r
10168     }\r
10169     if (connect(s, (struct sockaddr *) &sa,\r
10170       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10171       err = WSAGetLastError();\r
10172       if (err == WSAEADDRINUSE) {\r
10173         closesocket(s);\r
10174         s = -1;\r
10175         continue;\r
10176       }\r
10177       WSACleanup();\r
10178       return err;\r
10179     }\r
10180     break;\r
10181   }\r
10182 \r
10183   /* Bind stderr local socket to unused "privileged" port address\r
10184    */\r
10185   s2 = INVALID_SOCKET;\r
10186   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10187   mysa.sin_family = AF_INET;\r
10188   mysa.sin_addr.s_addr = INADDR_ANY;\r
10189   for (fromPort = 1023;; fromPort--) {\r
10190     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10191     if (fromPort < 0) {\r
10192       (void) closesocket(s);\r
10193       WSACleanup();\r
10194       return WSAEADDRINUSE;\r
10195     }\r
10196     if (s2 == INVALID_SOCKET) {\r
10197       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10198         err = WSAGetLastError();\r
10199         closesocket(s);\r
10200         WSACleanup();\r
10201         return err;\r
10202       }\r
10203     }\r
10204     uport = (unsigned short) fromPort;\r
10205     mysa.sin_port = htons(uport);\r
10206     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10207         == SOCKET_ERROR) {\r
10208       err = WSAGetLastError();\r
10209       if (err == WSAEADDRINUSE) continue;\r
10210       (void) closesocket(s);\r
10211       WSACleanup();\r
10212       return err;\r
10213     }\r
10214     if (listen(s2, 1) == SOCKET_ERROR) {\r
10215       err = WSAGetLastError();\r
10216       if (err == WSAEADDRINUSE) {\r
10217         closesocket(s2);\r
10218         s2 = INVALID_SOCKET;\r
10219         continue;\r
10220       }\r
10221       (void) closesocket(s);\r
10222       (void) closesocket(s2);\r
10223       WSACleanup();\r
10224       return err;\r
10225     }\r
10226     break;\r
10227   }\r
10228   prevStderrPort = fromPort; // remember port used\r
10229   sprintf(stderrPortStr, "%d", fromPort);\r
10230 \r
10231   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10232     err = WSAGetLastError();\r
10233     (void) closesocket(s);\r
10234     (void) closesocket(s2);\r
10235     WSACleanup();\r
10236     return err;\r
10237   }\r
10238 \r
10239   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10240     err = WSAGetLastError();\r
10241     (void) closesocket(s);\r
10242     (void) closesocket(s2);\r
10243     WSACleanup();\r
10244     return err;\r
10245   }\r
10246   if (*user == NULLCHAR) user = UserName();\r
10247   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10248     err = WSAGetLastError();\r
10249     (void) closesocket(s);\r
10250     (void) closesocket(s2);\r
10251     WSACleanup();\r
10252     return err;\r
10253   }\r
10254   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10255     err = WSAGetLastError();\r
10256     (void) closesocket(s);\r
10257     (void) closesocket(s2);\r
10258     WSACleanup();\r
10259     return err;\r
10260   }\r
10261 \r
10262   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10263     err = WSAGetLastError();\r
10264     (void) closesocket(s);\r
10265     (void) closesocket(s2);\r
10266     WSACleanup();\r
10267     return err;\r
10268   }\r
10269   (void) closesocket(s2);  /* Stop listening */\r
10270 \r
10271   /* Prepare return value */\r
10272   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10273   cp->kind = CPRcmd;\r
10274   cp->sock = s;\r
10275   cp->sock2 = s3;\r
10276   *pr = (ProcRef *) cp;\r
10277 \r
10278   return NO_ERROR;\r
10279 }\r
10280 \r
10281 \r
10282 InputSourceRef\r
10283 AddInputSource(ProcRef pr, int lineByLine,\r
10284                InputCallback func, VOIDSTAR closure)\r
10285 {\r
10286   InputSource *is, *is2 = NULL;\r
10287   ChildProc *cp = (ChildProc *) pr;\r
10288 \r
10289   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10290   is->lineByLine = lineByLine;\r
10291   is->func = func;\r
10292   is->closure = closure;\r
10293   is->second = NULL;\r
10294   is->next = is->buf;\r
10295   if (pr == NoProc) {\r
10296     is->kind = CPReal;\r
10297     consoleInputSource = is;\r
10298   } else {\r
10299     is->kind = cp->kind;\r
10300     /* \r
10301         [AS] Try to avoid a race condition if the thread is given control too early:\r
10302         we create all threads suspended so that the is->hThread variable can be\r
10303         safely assigned, then let the threads start with ResumeThread.\r
10304     */\r
10305     switch (cp->kind) {\r
10306     case CPReal:\r
10307       is->hFile = cp->hFrom;\r
10308       cp->hFrom = NULL; /* now owned by InputThread */\r
10309       is->hThread =\r
10310         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10311                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10312       break;\r
10313 \r
10314     case CPComm:\r
10315       is->hFile = cp->hFrom;\r
10316       cp->hFrom = NULL; /* now owned by InputThread */\r
10317       is->hThread =\r
10318         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10319                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10320       break;\r
10321 \r
10322     case CPSock:\r
10323       is->sock = cp->sock;\r
10324       is->hThread =\r
10325         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10326                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10327       break;\r
10328 \r
10329     case CPRcmd:\r
10330       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10331       *is2 = *is;\r
10332       is->sock = cp->sock;\r
10333       is->second = is2;\r
10334       is2->sock = cp->sock2;\r
10335       is2->second = is2;\r
10336       is->hThread =\r
10337         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10338                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10339       is2->hThread =\r
10340         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10341                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10342       break;\r
10343     }\r
10344 \r
10345     if( is->hThread != NULL ) {\r
10346         ResumeThread( is->hThread );\r
10347     }\r
10348 \r
10349     if( is2 != NULL && is2->hThread != NULL ) {\r
10350         ResumeThread( is2->hThread );\r
10351     }\r
10352   }\r
10353 \r
10354   return (InputSourceRef) is;\r
10355 }\r
10356 \r
10357 void\r
10358 RemoveInputSource(InputSourceRef isr)\r
10359 {\r
10360   InputSource *is;\r
10361 \r
10362   is = (InputSource *) isr;\r
10363   is->hThread = NULL;  /* tell thread to stop */\r
10364   CloseHandle(is->hThread);\r
10365   if (is->second != NULL) {\r
10366     is->second->hThread = NULL;\r
10367     CloseHandle(is->second->hThread);\r
10368   }\r
10369 }\r
10370 \r
10371 int no_wrap(char *message, int count)\r
10372 {\r
10373     ConsoleOutput(message, count, FALSE);\r
10374     return count;\r
10375 }\r
10376 \r
10377 int\r
10378 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10379 {\r
10380   DWORD dOutCount;\r
10381   int outCount = SOCKET_ERROR;\r
10382   ChildProc *cp = (ChildProc *) pr;\r
10383   static OVERLAPPED ovl;\r
10384   static int line = 0;\r
10385 \r
10386   if (pr == NoProc)\r
10387   {\r
10388     if (appData.noJoin || !appData.useInternalWrap)\r
10389       return no_wrap(message, count);\r
10390     else\r
10391     {\r
10392       int width = get_term_width();\r
10393       int len = wrap(NULL, message, count, width, &line);\r
10394       char *msg = malloc(len);\r
10395       int dbgchk;\r
10396 \r
10397       if (!msg)\r
10398         return no_wrap(message, count);\r
10399       else\r
10400       {\r
10401         dbgchk = wrap(msg, message, count, width, &line);\r
10402         if (dbgchk != len && appData.debugMode)\r
10403             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
10404         ConsoleOutput(msg, len, FALSE);\r
10405         free(msg);\r
10406         return len;\r
10407       }\r
10408     }\r
10409   }\r
10410 \r
10411   if (ovl.hEvent == NULL) {\r
10412     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10413   }\r
10414   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10415 \r
10416   switch (cp->kind) {\r
10417   case CPSock:\r
10418   case CPRcmd:\r
10419     outCount = send(cp->sock, message, count, 0);\r
10420     if (outCount == SOCKET_ERROR) {\r
10421       *outError = WSAGetLastError();\r
10422     } else {\r
10423       *outError = NO_ERROR;\r
10424     }\r
10425     break;\r
10426 \r
10427   case CPReal:\r
10428     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10429                   &dOutCount, NULL)) {\r
10430       *outError = NO_ERROR;\r
10431       outCount = (int) dOutCount;\r
10432     } else {\r
10433       *outError = GetLastError();\r
10434     }\r
10435     break;\r
10436 \r
10437   case CPComm:\r
10438     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10439                             &dOutCount, &ovl);\r
10440     if (*outError == NO_ERROR) {\r
10441       outCount = (int) dOutCount;\r
10442     }\r
10443     break;\r
10444   }\r
10445   return outCount;\r
10446 }\r
10447 \r
10448 int\r
10449 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10450                        long msdelay)\r
10451 {\r
10452   /* Ignore delay, not implemented for WinBoard */\r
10453   return OutputToProcess(pr, message, count, outError);\r
10454 }\r
10455 \r
10456 \r
10457 void\r
10458 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10459                         char *buf, int count, int error)\r
10460 {\r
10461   DisplayFatalError("Not implemented", 0, 1);\r
10462 }\r
10463 \r
10464 /* see wgamelist.c for Game List functions */\r
10465 /* see wedittags.c for Edit Tags functions */\r
10466 \r
10467 \r
10468 VOID\r
10469 ICSInitScript()\r
10470 {\r
10471   FILE *f;\r
10472   char buf[MSG_SIZ];\r
10473   char *dummy;\r
10474 \r
10475   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10476     f = fopen(buf, "r");\r
10477     if (f != NULL) {\r
10478       ProcessICSInitScript(f);\r
10479       fclose(f);\r
10480     }\r
10481   }\r
10482 }\r
10483 \r
10484 \r
10485 VOID\r
10486 StartAnalysisClock()\r
10487 {\r
10488   if (analysisTimerEvent) return;\r
10489   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10490                                         (UINT) 2000, NULL);\r
10491 }\r
10492 \r
10493 LRESULT CALLBACK\r
10494 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10495 {\r
10496   static HANDLE hwndText;\r
10497   RECT rect;\r
10498   static int sizeX, sizeY;\r
10499   int newSizeX, newSizeY, flags;\r
10500   MINMAXINFO *mmi;\r
10501 \r
10502   switch (message) {\r
10503   case WM_INITDIALOG: /* message: initialize dialog box */\r
10504     /* Initialize the dialog items */\r
10505     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10506     SetWindowText(hDlg, analysisTitle);\r
10507     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10508     /* Size and position the dialog */\r
10509     if (!analysisDialog) {\r
10510       analysisDialog = hDlg;\r
10511       flags = SWP_NOZORDER;\r
10512       GetClientRect(hDlg, &rect);\r
10513       sizeX = rect.right;\r
10514       sizeY = rect.bottom;\r
10515       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10516           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10517         WINDOWPLACEMENT wp;\r
10518         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10519         wp.length = sizeof(WINDOWPLACEMENT);\r
10520         wp.flags = 0;\r
10521         wp.showCmd = SW_SHOW;\r
10522         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10523         wp.rcNormalPosition.left = analysisX;\r
10524         wp.rcNormalPosition.right = analysisX + analysisW;\r
10525         wp.rcNormalPosition.top = analysisY;\r
10526         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10527         SetWindowPlacement(hDlg, &wp);\r
10528 \r
10529         GetClientRect(hDlg, &rect);\r
10530         newSizeX = rect.right;\r
10531         newSizeY = rect.bottom;\r
10532         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10533                               newSizeX, newSizeY);\r
10534         sizeX = newSizeX;\r
10535         sizeY = newSizeY;\r
10536       }\r
10537     }\r
10538     return FALSE;\r
10539 \r
10540   case WM_COMMAND: /* message: received a command */\r
10541     switch (LOWORD(wParam)) {\r
10542     case IDCANCEL:\r
10543       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10544           ExitAnalyzeMode();\r
10545           ModeHighlight();\r
10546           return TRUE;\r
10547       }\r
10548       EditGameEvent();\r
10549       return TRUE;\r
10550     default:\r
10551       break;\r
10552     }\r
10553     break;\r
10554 \r
10555   case WM_SIZE:\r
10556     newSizeX = LOWORD(lParam);\r
10557     newSizeY = HIWORD(lParam);\r
10558     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10559     sizeX = newSizeX;\r
10560     sizeY = newSizeY;\r
10561     break;\r
10562 \r
10563   case WM_GETMINMAXINFO:\r
10564     /* Prevent resizing window too small */\r
10565     mmi = (MINMAXINFO *) lParam;\r
10566     mmi->ptMinTrackSize.x = 100;\r
10567     mmi->ptMinTrackSize.y = 100;\r
10568     break;\r
10569   }\r
10570   return FALSE;\r
10571 }\r
10572 \r
10573 VOID\r
10574 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10575 {\r
10576   highlightInfo.sq[0].x = fromX;\r
10577   highlightInfo.sq[0].y = fromY;\r
10578   highlightInfo.sq[1].x = toX;\r
10579   highlightInfo.sq[1].y = toY;\r
10580 }\r
10581 \r
10582 VOID\r
10583 ClearHighlights()\r
10584 {\r
10585   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10586     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10587 }\r
10588 \r
10589 VOID\r
10590 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10591 {\r
10592   premoveHighlightInfo.sq[0].x = fromX;\r
10593   premoveHighlightInfo.sq[0].y = fromY;\r
10594   premoveHighlightInfo.sq[1].x = toX;\r
10595   premoveHighlightInfo.sq[1].y = toY;\r
10596 }\r
10597 \r
10598 VOID\r
10599 ClearPremoveHighlights()\r
10600 {\r
10601   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10602     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10603 }\r
10604 \r
10605 VOID\r
10606 ShutDownFrontEnd()\r
10607 {\r
10608   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10609   DeleteClipboardTempFiles();\r
10610 }\r
10611 \r
10612 void\r
10613 BoardToTop()\r
10614 {\r
10615     if (IsIconic(hwndMain))\r
10616       ShowWindow(hwndMain, SW_RESTORE);\r
10617 \r
10618     SetActiveWindow(hwndMain);\r
10619 }\r
10620 \r
10621 /*\r
10622  * Prototypes for animation support routines\r
10623  */\r
10624 static void ScreenSquare(int column, int row, POINT * pt);\r
10625 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10626      POINT frames[], int * nFrames);\r
10627 \r
10628 \r
10629 void\r
10630 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10631 {       // [HGM] atomic: animate blast wave\r
10632         int i;\r
10633 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10634         explodeInfo.fromX = fromX;\r
10635         explodeInfo.fromY = fromY;\r
10636         explodeInfo.toX = toX;\r
10637         explodeInfo.toY = toY;\r
10638         for(i=1; i<nFrames; i++) {\r
10639             explodeInfo.radius = (i*180)/(nFrames-1);\r
10640             DrawPosition(FALSE, NULL);\r
10641             Sleep(appData.animSpeed);\r
10642         }\r
10643         explodeInfo.radius = 0;\r
10644         DrawPosition(TRUE, NULL);\r
10645 }\r
10646 \r
10647 #define kFactor 4\r
10648 \r
10649 void\r
10650 AnimateMove(board, fromX, fromY, toX, toY)\r
10651      Board board;\r
10652      int fromX;\r
10653      int fromY;\r
10654      int toX;\r
10655      int toY;\r
10656 {\r
10657   ChessSquare piece;\r
10658   POINT start, finish, mid;\r
10659   POINT frames[kFactor * 2 + 1];\r
10660   int nFrames, n;\r
10661 \r
10662   if (!appData.animate) return;\r
10663   if (doingSizing) return;\r
10664   if (fromY < 0 || fromX < 0) return;\r
10665   piece = board[fromY][fromX];\r
10666   if (piece >= EmptySquare) return;\r
10667 \r
10668   ScreenSquare(fromX, fromY, &start);\r
10669   ScreenSquare(toX, toY, &finish);\r
10670 \r
10671   /* All pieces except knights move in straight line */\r
10672   if (piece != WhiteKnight && piece != BlackKnight) {\r
10673     mid.x = start.x + (finish.x - start.x) / 2;\r
10674     mid.y = start.y + (finish.y - start.y) / 2;\r
10675   } else {\r
10676     /* Knight: make diagonal movement then straight */\r
10677     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10678        mid.x = start.x + (finish.x - start.x) / 2;\r
10679        mid.y = finish.y;\r
10680      } else {\r
10681        mid.x = finish.x;\r
10682        mid.y = start.y + (finish.y - start.y) / 2;\r
10683      }\r
10684   }\r
10685   \r
10686   /* Don't use as many frames for very short moves */\r
10687   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10688     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10689   else\r
10690     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10691 \r
10692   animInfo.from.x = fromX;\r
10693   animInfo.from.y = fromY;\r
10694   animInfo.to.x = toX;\r
10695   animInfo.to.y = toY;\r
10696   animInfo.lastpos = start;\r
10697   animInfo.piece = piece;\r
10698   for (n = 0; n < nFrames; n++) {\r
10699     animInfo.pos = frames[n];\r
10700     DrawPosition(FALSE, NULL);\r
10701     animInfo.lastpos = animInfo.pos;\r
10702     Sleep(appData.animSpeed);\r
10703   }\r
10704   animInfo.pos = finish;\r
10705   DrawPosition(FALSE, NULL);\r
10706   animInfo.piece = EmptySquare;\r
10707   if(gameInfo.variant == VariantAtomic && \r
10708      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10709         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10710 }\r
10711 \r
10712 /*      Convert board position to corner of screen rect and color       */\r
10713 \r
10714 static void\r
10715 ScreenSquare(column, row, pt)\r
10716      int column; int row; POINT * pt;\r
10717 {\r
10718   if (flipView) {\r
10719     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10720     pt->y = lineGap + row * (squareSize + lineGap);\r
10721   } else {\r
10722     pt->x = lineGap + column * (squareSize + lineGap);\r
10723     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10724   }\r
10725 }\r
10726 \r
10727 /*      Generate a series of frame coords from start->mid->finish.\r
10728         The movement rate doubles until the half way point is\r
10729         reached, then halves back down to the final destination,\r
10730         which gives a nice slow in/out effect. The algorithmn\r
10731         may seem to generate too many intermediates for short\r
10732         moves, but remember that the purpose is to attract the\r
10733         viewers attention to the piece about to be moved and\r
10734         then to where it ends up. Too few frames would be less\r
10735         noticeable.                                             */\r
10736 \r
10737 static void\r
10738 Tween(start, mid, finish, factor, frames, nFrames)\r
10739      POINT * start; POINT * mid;\r
10740      POINT * finish; int factor;\r
10741      POINT frames[]; int * nFrames;\r
10742 {\r
10743   int n, fraction = 1, count = 0;\r
10744 \r
10745   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10746   for (n = 0; n < factor; n++)\r
10747     fraction *= 2;\r
10748   for (n = 0; n < factor; n++) {\r
10749     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10750     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10751     count ++;\r
10752     fraction = fraction / 2;\r
10753   }\r
10754   \r
10755   /* Midpoint */\r
10756   frames[count] = *mid;\r
10757   count ++;\r
10758   \r
10759   /* Slow out, stepping 1/2, then 1/4, ... */\r
10760   fraction = 2;\r
10761   for (n = 0; n < factor; n++) {\r
10762     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10763     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10764     count ++;\r
10765     fraction = fraction * 2;\r
10766   }\r
10767   *nFrames = count;\r
10768 }\r
10769 \r
10770 void\r
10771 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10772 {\r
10773     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10774 \r
10775     EvalGraphSet( first, last, current, pvInfoList );\r
10776 }\r
10777 \r
10778 void SetProgramStats( FrontEndProgramStats * stats )\r
10779 {\r
10780     EngineOutputUpdate( stats );\r
10781 }\r