fix engine-sync on move-number type-in bug
[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 int commentUp = 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 int 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, limit;\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 (commentUp && 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 (!commentUp) 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   commentUp = 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           ToNrEvent(2*n-1);\r
7190           EndDialog(hDlg, TRUE);\r
7191           return TRUE;\r
7192         }\r
7193       }\r
7194       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7195         gameMode != Training) {\r
7196         DisplayMoveError("Displayed move is not current");\r
7197       } else {\r
7198 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7199         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7200           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7201         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7202         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7203           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7204           if (gameMode != Training)\r
7205               forwardMostMove = currentMove;\r
7206           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7207         } else {\r
7208           DisplayMoveError("Could not parse move");\r
7209         }\r
7210       }\r
7211       EndDialog(hDlg, TRUE);\r
7212       return TRUE;\r
7213     case IDCANCEL:\r
7214       EndDialog(hDlg, FALSE);\r
7215       return TRUE;\r
7216     default:\r
7217       break;\r
7218     }\r
7219     break;\r
7220   }\r
7221   return FALSE;\r
7222 }\r
7223 \r
7224 VOID\r
7225 PopUpMoveDialog(char firstchar)\r
7226 {\r
7227     FARPROC lpProc;\r
7228     \r
7229     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7230         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7231         gameMode == AnalyzeMode || gameMode == EditGame || \r
7232         gameMode == EditPosition || gameMode == IcsExamining ||\r
7233         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7234         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7235                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7236                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7237         gameMode == Training) {\r
7238       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7239       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7240         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7241       FreeProcInstance(lpProc);\r
7242     }\r
7243 }\r
7244 \r
7245 /*---------------------------------------------------------------------------*\\r
7246  *\r
7247  * Type-in name dialog functions\r
7248  * \r
7249 \*---------------------------------------------------------------------------*/\r
7250 \r
7251 LRESULT CALLBACK\r
7252 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7253 {\r
7254   char move[MSG_SIZ];\r
7255   HWND hInput;\r
7256 \r
7257   switch (message) {\r
7258   case WM_INITDIALOG:\r
7259     move[0] = (char) lParam;\r
7260     move[1] = NULLCHAR;\r
7261     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7262     hInput = GetDlgItem(hDlg, OPT_Name);\r
7263     SetWindowText(hInput, move);\r
7264     SetFocus(hInput);\r
7265     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7266     return FALSE;\r
7267 \r
7268   case WM_COMMAND:\r
7269     switch (LOWORD(wParam)) {\r
7270     case IDOK:\r
7271       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7272       appData.userName = strdup(move);\r
7273       SetUserLogo();\r
7274 \r
7275       EndDialog(hDlg, TRUE);\r
7276       return TRUE;\r
7277     case IDCANCEL:\r
7278       EndDialog(hDlg, FALSE);\r
7279       return TRUE;\r
7280     default:\r
7281       break;\r
7282     }\r
7283     break;\r
7284   }\r
7285   return FALSE;\r
7286 }\r
7287 \r
7288 VOID\r
7289 PopUpNameDialog(char firstchar)\r
7290 {\r
7291     FARPROC lpProc;\r
7292     \r
7293       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7294       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7295         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7296       FreeProcInstance(lpProc);\r
7297 }\r
7298 \r
7299 /*---------------------------------------------------------------------------*\\r
7300  *\r
7301  *  Error dialogs\r
7302  * \r
7303 \*---------------------------------------------------------------------------*/\r
7304 \r
7305 /* Nonmodal error box */\r
7306 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7307                              WPARAM wParam, LPARAM lParam);\r
7308 \r
7309 VOID\r
7310 ErrorPopUp(char *title, char *content)\r
7311 {\r
7312   FARPROC lpProc;\r
7313   char *p, *q;\r
7314   BOOLEAN modal = hwndMain == NULL;\r
7315 \r
7316   p = content;\r
7317   q = errorMessage;\r
7318   while (*p) {\r
7319     if (*p == '\n') {\r
7320       if (modal) {\r
7321         *q++ = ' ';\r
7322         p++;\r
7323       } else {\r
7324         *q++ = '\r';\r
7325         *q++ = *p++;\r
7326       }\r
7327     } else {\r
7328       *q++ = *p++;\r
7329     }\r
7330   }\r
7331   *q = NULLCHAR;\r
7332   strncpy(errorTitle, title, sizeof(errorTitle));\r
7333   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7334   \r
7335   if (modal) {\r
7336     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7337   } else {\r
7338     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7339     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7340                  hwndMain, (DLGPROC)lpProc);\r
7341     FreeProcInstance(lpProc);\r
7342   }\r
7343 }\r
7344 \r
7345 VOID\r
7346 ErrorPopDown()\r
7347 {\r
7348   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7349   if (errorDialog == NULL) return;\r
7350   DestroyWindow(errorDialog);\r
7351   errorDialog = NULL;\r
7352 }\r
7353 \r
7354 LRESULT CALLBACK\r
7355 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7356 {\r
7357   HANDLE hwndText;\r
7358   RECT rChild;\r
7359 \r
7360   switch (message) {\r
7361   case WM_INITDIALOG:\r
7362     GetWindowRect(hDlg, &rChild);\r
7363 \r
7364     /*\r
7365     SetWindowPos(hDlg, NULL, rChild.left,\r
7366       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7367       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7368     */\r
7369 \r
7370     /* \r
7371         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7372         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7373         and it doesn't work when you resize the dialog.\r
7374         For now, just give it a default position.\r
7375     */\r
7376     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7377 \r
7378     errorDialog = hDlg;\r
7379     SetWindowText(hDlg, errorTitle);\r
7380     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7381     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7382     return FALSE;\r
7383 \r
7384   case WM_COMMAND:\r
7385     switch (LOWORD(wParam)) {\r
7386     case IDOK:\r
7387     case IDCANCEL:\r
7388       if (errorDialog == hDlg) errorDialog = NULL;\r
7389       DestroyWindow(hDlg);\r
7390       return TRUE;\r
7391 \r
7392     default:\r
7393       break;\r
7394     }\r
7395     break;\r
7396   }\r
7397   return FALSE;\r
7398 }\r
7399 \r
7400 #ifdef GOTHIC\r
7401 HWND gothicDialog = NULL;\r
7402 \r
7403 LRESULT CALLBACK\r
7404 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7405 {\r
7406   HANDLE hwndText;\r
7407   RECT rChild;\r
7408   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7409 \r
7410   switch (message) {\r
7411   case WM_INITDIALOG:\r
7412     GetWindowRect(hDlg, &rChild);\r
7413 \r
7414     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7415                                                              SWP_NOZORDER);\r
7416 \r
7417     /* \r
7418         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7419         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7420         and it doesn't work when you resize the dialog.\r
7421         For now, just give it a default position.\r
7422     */\r
7423     gothicDialog = hDlg;\r
7424     SetWindowText(hDlg, errorTitle);\r
7425     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7426     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7427     return FALSE;\r
7428 \r
7429   case WM_COMMAND:\r
7430     switch (LOWORD(wParam)) {\r
7431     case IDOK:\r
7432     case IDCANCEL:\r
7433       if (errorDialog == hDlg) errorDialog = NULL;\r
7434       DestroyWindow(hDlg);\r
7435       return TRUE;\r
7436 \r
7437     default:\r
7438       break;\r
7439     }\r
7440     break;\r
7441   }\r
7442   return FALSE;\r
7443 }\r
7444 \r
7445 VOID\r
7446 GothicPopUp(char *title, VariantClass variant)\r
7447 {\r
7448   FARPROC lpProc;\r
7449   static char *lastTitle;\r
7450 \r
7451   strncpy(errorTitle, title, sizeof(errorTitle));\r
7452   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7453 \r
7454   if(lastTitle != title && gothicDialog != NULL) {\r
7455     DestroyWindow(gothicDialog);\r
7456     gothicDialog = NULL;\r
7457   }\r
7458   if(variant != VariantNormal && gothicDialog == NULL) {\r
7459     title = lastTitle;\r
7460     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7461     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7462                  hwndMain, (DLGPROC)lpProc);\r
7463     FreeProcInstance(lpProc);\r
7464   }\r
7465 }\r
7466 #endif\r
7467 \r
7468 /*---------------------------------------------------------------------------*\\r
7469  *\r
7470  *  Ics Interaction console functions\r
7471  *\r
7472 \*---------------------------------------------------------------------------*/\r
7473 \r
7474 #define HISTORY_SIZE 64\r
7475 static char *history[HISTORY_SIZE];\r
7476 int histIn = 0, histP = 0;\r
7477 \r
7478 VOID\r
7479 SaveInHistory(char *cmd)\r
7480 {\r
7481   if (history[histIn] != NULL) {\r
7482     free(history[histIn]);\r
7483     history[histIn] = NULL;\r
7484   }\r
7485   if (*cmd == NULLCHAR) return;\r
7486   history[histIn] = StrSave(cmd);\r
7487   histIn = (histIn + 1) % HISTORY_SIZE;\r
7488   if (history[histIn] != NULL) {\r
7489     free(history[histIn]);\r
7490     history[histIn] = NULL;\r
7491   }\r
7492   histP = histIn;\r
7493 }\r
7494 \r
7495 char *\r
7496 PrevInHistory(char *cmd)\r
7497 {\r
7498   int newhp;\r
7499   if (histP == histIn) {\r
7500     if (history[histIn] != NULL) free(history[histIn]);\r
7501     history[histIn] = StrSave(cmd);\r
7502   }\r
7503   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7504   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7505   histP = newhp;\r
7506   return history[histP];\r
7507 }\r
7508 \r
7509 char *\r
7510 NextInHistory()\r
7511 {\r
7512   if (histP == histIn) return NULL;\r
7513   histP = (histP + 1) % HISTORY_SIZE;\r
7514   return history[histP];\r
7515 }\r
7516 \r
7517 typedef struct {\r
7518   char *item;\r
7519   char *command;\r
7520   BOOLEAN getname;\r
7521   BOOLEAN immediate;\r
7522 } IcsTextMenuEntry;\r
7523 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7524 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7525 \r
7526 void\r
7527 ParseIcsTextMenu(char *icsTextMenuString)\r
7528 {\r
7529 //  int flags = 0;\r
7530   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7531   char *p = icsTextMenuString;\r
7532   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7533     free(e->item);\r
7534     e->item = NULL;\r
7535     if (e->command != NULL) {\r
7536       free(e->command);\r
7537       e->command = NULL;\r
7538     }\r
7539     e++;\r
7540   }\r
7541   e = icsTextMenuEntry;\r
7542   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7543     if (*p == ';' || *p == '\n') {\r
7544       e->item = strdup("-");\r
7545       e->command = NULL;\r
7546       p++;\r
7547     } else if (*p == '-') {\r
7548       e->item = strdup("-");\r
7549       e->command = NULL;\r
7550       p++;\r
7551       if (*p) p++;\r
7552     } else {\r
7553       char *q, *r, *s, *t;\r
7554       char c;\r
7555       q = strchr(p, ',');\r
7556       if (q == NULL) break;\r
7557       *q = NULLCHAR;\r
7558       r = strchr(q + 1, ',');\r
7559       if (r == NULL) break;\r
7560       *r = NULLCHAR;\r
7561       s = strchr(r + 1, ',');\r
7562       if (s == NULL) break;\r
7563       *s = NULLCHAR;\r
7564       c = ';';\r
7565       t = strchr(s + 1, c);\r
7566       if (t == NULL) {\r
7567         c = '\n';\r
7568         t = strchr(s + 1, c);\r
7569       }\r
7570       if (t != NULL) *t = NULLCHAR;\r
7571       e->item = strdup(p);\r
7572       e->command = strdup(q + 1);\r
7573       e->getname = *(r + 1) != '0';\r
7574       e->immediate = *(s + 1) != '0';\r
7575       *q = ',';\r
7576       *r = ',';\r
7577       *s = ',';\r
7578       if (t == NULL) break;\r
7579       *t = c;\r
7580       p = t + 1;\r
7581     }\r
7582     e++;\r
7583   } \r
7584 }\r
7585 \r
7586 HMENU\r
7587 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7588 {\r
7589   HMENU hmenu, h;\r
7590   int i = 0;\r
7591   hmenu = LoadMenu(hInst, "TextMenu");\r
7592   h = GetSubMenu(hmenu, 0);\r
7593   while (e->item) {\r
7594     if (strcmp(e->item, "-") == 0) {\r
7595       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7596     } else {\r
7597       if (e->item[0] == '|') {\r
7598         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7599                    IDM_CommandX + i, &e->item[1]);\r
7600       } else {\r
7601         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7602       }\r
7603     }\r
7604     e++;\r
7605     i++;\r
7606   } \r
7607   return hmenu;\r
7608 }\r
7609 \r
7610 WNDPROC consoleTextWindowProc;\r
7611 \r
7612 void\r
7613 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7614 {\r
7615   char buf[MSG_SIZ], name[MSG_SIZ];\r
7616   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7617   CHARRANGE sel;\r
7618 \r
7619   if (!getname) {\r
7620     SetWindowText(hInput, command);\r
7621     if (immediate) {\r
7622       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7623     } else {\r
7624       sel.cpMin = 999999;\r
7625       sel.cpMax = 999999;\r
7626       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7627       SetFocus(hInput);\r
7628     }\r
7629     return;\r
7630   }    \r
7631   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7632   if (sel.cpMin == sel.cpMax) {\r
7633     /* Expand to surrounding word */\r
7634     TEXTRANGE tr;\r
7635     do {\r
7636       tr.chrg.cpMax = sel.cpMin;\r
7637       tr.chrg.cpMin = --sel.cpMin;\r
7638       if (sel.cpMin < 0) break;\r
7639       tr.lpstrText = name;\r
7640       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7641     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7642     sel.cpMin++;\r
7643 \r
7644     do {\r
7645       tr.chrg.cpMin = sel.cpMax;\r
7646       tr.chrg.cpMax = ++sel.cpMax;\r
7647       tr.lpstrText = name;\r
7648       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7649     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7650     sel.cpMax--;\r
7651 \r
7652     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7653       MessageBeep(MB_ICONEXCLAMATION);\r
7654       return;\r
7655     }\r
7656     tr.chrg = sel;\r
7657     tr.lpstrText = name;\r
7658     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7659   } else {\r
7660     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7661       MessageBeep(MB_ICONEXCLAMATION);\r
7662       return;\r
7663     }\r
7664     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7665   }\r
7666   if (immediate) {\r
7667     sprintf(buf, "%s %s", command, name);\r
7668     SetWindowText(hInput, buf);\r
7669     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7670   } else {\r
7671     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7672     SetWindowText(hInput, buf);\r
7673     sel.cpMin = 999999;\r
7674     sel.cpMax = 999999;\r
7675     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7676     SetFocus(hInput);\r
7677   }\r
7678 }\r
7679 \r
7680 LRESULT CALLBACK \r
7681 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7682 {\r
7683   HWND hInput;\r
7684   CHARRANGE sel;\r
7685 \r
7686   switch (message) {\r
7687   case WM_KEYDOWN:\r
7688     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7689     switch (wParam) {\r
7690     case VK_PRIOR:\r
7691       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7692       return 0;\r
7693     case VK_NEXT:\r
7694       sel.cpMin = 999999;\r
7695       sel.cpMax = 999999;\r
7696       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7697       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7698       return 0;\r
7699     }\r
7700     break;\r
7701   case WM_CHAR:\r
7702    if(wParam != '\022') {\r
7703     if (wParam == '\t') {\r
7704       if (GetKeyState(VK_SHIFT) < 0) {\r
7705         /* shifted */\r
7706         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7707         if (buttonDesc[0].hwnd) {\r
7708           SetFocus(buttonDesc[0].hwnd);\r
7709         } else {\r
7710           SetFocus(hwndMain);\r
7711         }\r
7712       } else {\r
7713         /* unshifted */\r
7714         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7715       }\r
7716     } else {\r
7717       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7718       JAWS_DELETE( SetFocus(hInput); )\r
7719       SendMessage(hInput, message, wParam, lParam);\r
7720     }\r
7721     return 0;\r
7722    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7723   case WM_RBUTTONUP:\r
7724     if (GetKeyState(VK_SHIFT) & ~1) {\r
7725       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7726         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7727     } else {\r
7728       POINT pt;\r
7729       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7730       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7731       if (sel.cpMin == sel.cpMax) {\r
7732         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7733         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7734       }\r
7735       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7736         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7737       }\r
7738       pt.x = LOWORD(lParam);\r
7739       pt.y = HIWORD(lParam);\r
7740       MenuPopup(hwnd, pt, hmenu, -1);\r
7741     }\r
7742     return 0;\r
7743   case WM_PASTE:\r
7744     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7745     SetFocus(hInput);\r
7746     return SendMessage(hInput, message, wParam, lParam);\r
7747   case WM_MBUTTONDOWN:\r
7748     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7749   case WM_RBUTTONDOWN:\r
7750     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7751       /* Move selection here if it was empty */\r
7752       POINT pt;\r
7753       pt.x = LOWORD(lParam);\r
7754       pt.y = HIWORD(lParam);\r
7755       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7756       if (sel.cpMin == sel.cpMax) {\r
7757         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7758         sel.cpMax = sel.cpMin;\r
7759         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7760       }\r
7761       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7762     }\r
7763     return 0;\r
7764   case WM_COMMAND:\r
7765     switch (LOWORD(wParam)) {\r
7766     case IDM_QuickPaste:\r
7767       {\r
7768         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7769         if (sel.cpMin == sel.cpMax) {\r
7770           MessageBeep(MB_ICONEXCLAMATION);\r
7771           return 0;\r
7772         }\r
7773         SendMessage(hwnd, WM_COPY, 0, 0);\r
7774         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7775         SendMessage(hInput, WM_PASTE, 0, 0);\r
7776         SetFocus(hInput);\r
7777         return 0;\r
7778       }\r
7779     case IDM_Cut:\r
7780       SendMessage(hwnd, WM_CUT, 0, 0);\r
7781       return 0;\r
7782     case IDM_Paste:\r
7783       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7784       return 0;\r
7785     case IDM_Copy:\r
7786       SendMessage(hwnd, WM_COPY, 0, 0);\r
7787       return 0;\r
7788     default:\r
7789       {\r
7790         int i = LOWORD(wParam) - IDM_CommandX;\r
7791         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7792             icsTextMenuEntry[i].command != NULL) {\r
7793           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7794                    icsTextMenuEntry[i].getname,\r
7795                    icsTextMenuEntry[i].immediate);\r
7796           return 0;\r
7797         }\r
7798       }\r
7799       break;\r
7800     }\r
7801     break;\r
7802   }\r
7803   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7804 }\r
7805 \r
7806 WNDPROC consoleInputWindowProc;\r
7807 \r
7808 LRESULT CALLBACK\r
7809 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7810 {\r
7811   char buf[MSG_SIZ];\r
7812   char *p;\r
7813   static BOOL sendNextChar = FALSE;\r
7814   static BOOL quoteNextChar = FALSE;\r
7815   InputSource *is = consoleInputSource;\r
7816   CHARFORMAT cf;\r
7817   CHARRANGE sel;\r
7818 \r
7819   switch (message) {\r
7820   case WM_CHAR:\r
7821     if (!appData.localLineEditing || sendNextChar) {\r
7822       is->buf[0] = (CHAR) wParam;\r
7823       is->count = 1;\r
7824       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7825       sendNextChar = FALSE;\r
7826       return 0;\r
7827     }\r
7828     if (quoteNextChar) {\r
7829       buf[0] = (char) wParam;\r
7830       buf[1] = NULLCHAR;\r
7831       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7832       quoteNextChar = FALSE;\r
7833       return 0;\r
7834     }\r
7835     switch (wParam) {\r
7836     case '\r':   /* Enter key */\r
7837       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7838       if (consoleEcho) SaveInHistory(is->buf);\r
7839       is->buf[is->count++] = '\n';\r
7840       is->buf[is->count] = NULLCHAR;\r
7841       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7842       if (consoleEcho) {\r
7843         ConsoleOutput(is->buf, is->count, TRUE);\r
7844       } else if (appData.localLineEditing) {\r
7845         ConsoleOutput("\n", 1, TRUE);\r
7846       }\r
7847       /* fall thru */\r
7848     case '\033': /* Escape key */\r
7849       SetWindowText(hwnd, "");\r
7850       cf.cbSize = sizeof(CHARFORMAT);\r
7851       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7852       if (consoleEcho) {\r
7853         cf.crTextColor = textAttribs[ColorNormal].color;\r
7854       } else {\r
7855         cf.crTextColor = COLOR_ECHOOFF;\r
7856       }\r
7857       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7858       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7859       return 0;\r
7860     case '\t':   /* Tab key */\r
7861       if (GetKeyState(VK_SHIFT) < 0) {\r
7862         /* shifted */\r
7863         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7864       } else {\r
7865         /* unshifted */\r
7866         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7867         if (buttonDesc[0].hwnd) {\r
7868           SetFocus(buttonDesc[0].hwnd);\r
7869         } else {\r
7870           SetFocus(hwndMain);\r
7871         }\r
7872       }\r
7873       return 0;\r
7874     case '\023': /* Ctrl+S */\r
7875       sendNextChar = TRUE;\r
7876       return 0;\r
7877     case '\021': /* Ctrl+Q */\r
7878       quoteNextChar = TRUE;\r
7879       return 0;\r
7880     JAWS_REPLAY\r
7881     default:\r
7882       break;\r
7883     }\r
7884     break;\r
7885   case WM_KEYDOWN:\r
7886     switch (wParam) {\r
7887     case VK_UP:\r
7888       GetWindowText(hwnd, buf, MSG_SIZ);\r
7889       p = PrevInHistory(buf);\r
7890       if (p != NULL) {\r
7891         SetWindowText(hwnd, p);\r
7892         sel.cpMin = 999999;\r
7893         sel.cpMax = 999999;\r
7894         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7895         return 0;\r
7896       }\r
7897       break;\r
7898     case VK_DOWN:\r
7899       p = NextInHistory();\r
7900       if (p != NULL) {\r
7901         SetWindowText(hwnd, p);\r
7902         sel.cpMin = 999999;\r
7903         sel.cpMax = 999999;\r
7904         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7905         return 0;\r
7906       }\r
7907       break;\r
7908     case VK_HOME:\r
7909     case VK_END:\r
7910       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7911       /* fall thru */\r
7912     case VK_PRIOR:\r
7913     case VK_NEXT:\r
7914       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7915       return 0;\r
7916     }\r
7917     break;\r
7918   case WM_MBUTTONDOWN:\r
7919     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7920       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7921     break;\r
7922   case WM_RBUTTONUP:\r
7923     if (GetKeyState(VK_SHIFT) & ~1) {\r
7924       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7925         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7926     } else {\r
7927       POINT pt;\r
7928       HMENU hmenu;\r
7929       hmenu = LoadMenu(hInst, "InputMenu");\r
7930       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7931       if (sel.cpMin == sel.cpMax) {\r
7932         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7933         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7934       }\r
7935       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7936         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7937       }\r
7938       pt.x = LOWORD(lParam);\r
7939       pt.y = HIWORD(lParam);\r
7940       MenuPopup(hwnd, pt, hmenu, -1);\r
7941     }\r
7942     return 0;\r
7943   case WM_COMMAND:\r
7944     switch (LOWORD(wParam)) { \r
7945     case IDM_Undo:\r
7946       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7947       return 0;\r
7948     case IDM_SelectAll:\r
7949       sel.cpMin = 0;\r
7950       sel.cpMax = -1; /*999999?*/\r
7951       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7952       return 0;\r
7953     case IDM_Cut:\r
7954       SendMessage(hwnd, WM_CUT, 0, 0);\r
7955       return 0;\r
7956     case IDM_Paste:\r
7957       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7958       return 0;\r
7959     case IDM_Copy:\r
7960       SendMessage(hwnd, WM_COPY, 0, 0);\r
7961       return 0;\r
7962     }\r
7963     break;\r
7964   }\r
7965   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7966 }\r
7967 \r
7968 #define CO_MAX  100000\r
7969 #define CO_TRIM   1000\r
7970 \r
7971 LRESULT CALLBACK\r
7972 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7973 {\r
7974   static SnapData sd;\r
7975   HWND hText, hInput;\r
7976   RECT rect;\r
7977   static int sizeX, sizeY;\r
7978   int newSizeX, newSizeY;\r
7979   MINMAXINFO *mmi;\r
7980   WORD wMask;\r
7981 \r
7982   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7983   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7984 \r
7985   switch (message) {\r
7986   case WM_NOTIFY:\r
7987     if (((NMHDR*)lParam)->code == EN_LINK)\r
7988     {\r
7989       ENLINK *pLink = (ENLINK*)lParam;\r
7990       if (pLink->msg == WM_LBUTTONUP)\r
7991       {\r
7992         TEXTRANGE tr;\r
7993 \r
7994         tr.chrg = pLink->chrg;\r
7995         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7996         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7997         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7998         free(tr.lpstrText);\r
7999       }\r
8000     }\r
8001     break;\r
8002   case WM_INITDIALOG: /* message: initialize dialog box */\r
8003     hwndConsole = hDlg;\r
8004     SetFocus(hInput);\r
8005     consoleTextWindowProc = (WNDPROC)\r
8006       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8007     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8008     consoleInputWindowProc = (WNDPROC)\r
8009       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8010     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8011     Colorize(ColorNormal, TRUE);\r
8012     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8013     ChangedConsoleFont();\r
8014     GetClientRect(hDlg, &rect);\r
8015     sizeX = rect.right;\r
8016     sizeY = rect.bottom;\r
8017     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8018         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8019       WINDOWPLACEMENT wp;\r
8020       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8021       wp.length = sizeof(WINDOWPLACEMENT);\r
8022       wp.flags = 0;\r
8023       wp.showCmd = SW_SHOW;\r
8024       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8025       wp.rcNormalPosition.left = wpConsole.x;\r
8026       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8027       wp.rcNormalPosition.top = wpConsole.y;\r
8028       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8029       SetWindowPlacement(hDlg, &wp);\r
8030     }\r
8031 \r
8032    // [HGM] Chessknight's change 2004-07-13\r
8033    else { /* Determine Defaults */\r
8034        WINDOWPLACEMENT wp;\r
8035        wpConsole.x = winWidth + 1;\r
8036        wpConsole.y = boardY;\r
8037        wpConsole.width = screenWidth -  winWidth;\r
8038        wpConsole.height = winHeight;\r
8039        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8040        wp.length = sizeof(WINDOWPLACEMENT);\r
8041        wp.flags = 0;\r
8042        wp.showCmd = SW_SHOW;\r
8043        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8044        wp.rcNormalPosition.left = wpConsole.x;\r
8045        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8046        wp.rcNormalPosition.top = wpConsole.y;\r
8047        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8048        SetWindowPlacement(hDlg, &wp);\r
8049     }\r
8050 \r
8051    // Allow hText to highlight URLs and send notifications on them\r
8052    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
8053    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
8054    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
8055    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
8056 \r
8057     return FALSE;\r
8058 \r
8059   case WM_SETFOCUS:\r
8060     SetFocus(hInput);\r
8061     return 0;\r
8062 \r
8063   case WM_CLOSE:\r
8064     ExitEvent(0);\r
8065     /* not reached */\r
8066     break;\r
8067 \r
8068   case WM_SIZE:\r
8069     if (IsIconic(hDlg)) break;\r
8070     newSizeX = LOWORD(lParam);\r
8071     newSizeY = HIWORD(lParam);\r
8072     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8073       RECT rectText, rectInput;\r
8074       POINT pt;\r
8075       int newTextHeight, newTextWidth;\r
8076       GetWindowRect(hText, &rectText);\r
8077       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8078       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8079       if (newTextHeight < 0) {\r
8080         newSizeY += -newTextHeight;\r
8081         newTextHeight = 0;\r
8082       }\r
8083       SetWindowPos(hText, NULL, 0, 0,\r
8084         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8085       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8086       pt.x = rectInput.left;\r
8087       pt.y = rectInput.top + newSizeY - sizeY;\r
8088       ScreenToClient(hDlg, &pt);\r
8089       SetWindowPos(hInput, NULL, \r
8090         pt.x, pt.y, /* needs client coords */   \r
8091         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8092         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8093     }\r
8094     sizeX = newSizeX;\r
8095     sizeY = newSizeY;\r
8096     break;\r
8097 \r
8098   case WM_GETMINMAXINFO:\r
8099     /* Prevent resizing window too small */\r
8100     mmi = (MINMAXINFO *) lParam;\r
8101     mmi->ptMinTrackSize.x = 100;\r
8102     mmi->ptMinTrackSize.y = 100;\r
8103     break;\r
8104 \r
8105   /* [AS] Snapping */\r
8106   case WM_ENTERSIZEMOVE:\r
8107     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8108 \r
8109   case WM_SIZING:\r
8110     return OnSizing( &sd, hDlg, wParam, lParam );\r
8111 \r
8112   case WM_MOVING:\r
8113     return OnMoving( &sd, hDlg, wParam, lParam );\r
8114 \r
8115   case WM_EXITSIZEMOVE:\r
8116         UpdateICSWidth(hText);\r
8117     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8118   }\r
8119 \r
8120   return DefWindowProc(hDlg, message, wParam, lParam);\r
8121 }\r
8122 \r
8123 \r
8124 VOID\r
8125 ConsoleCreate()\r
8126 {\r
8127   HWND hCons;\r
8128   if (hwndConsole) return;\r
8129   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8130   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8131 }\r
8132 \r
8133 \r
8134 VOID\r
8135 ConsoleOutput(char* data, int length, int forceVisible)\r
8136 {\r
8137   HWND hText;\r
8138   int trim, exlen;\r
8139   char *p, *q;\r
8140   char buf[CO_MAX+1];\r
8141   POINT pEnd;\r
8142   RECT rect;\r
8143   static int delayLF = 0;\r
8144   CHARRANGE savesel, sel;\r
8145 \r
8146   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8147   p = data;\r
8148   q = buf;\r
8149   if (delayLF) {\r
8150     *q++ = '\r';\r
8151     *q++ = '\n';\r
8152     delayLF = 0;\r
8153   }\r
8154   while (length--) {\r
8155     if (*p == '\n') {\r
8156       if (*++p) {\r
8157         *q++ = '\r';\r
8158         *q++ = '\n';\r
8159       } else {\r
8160         delayLF = 1;\r
8161       }\r
8162     } else if (*p == '\007') {\r
8163        MyPlaySound(&sounds[(int)SoundBell]);\r
8164        p++;\r
8165     } else {\r
8166       *q++ = *p++;\r
8167     }\r
8168   }\r
8169   *q = NULLCHAR;\r
8170   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8171   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8172   /* Save current selection */\r
8173   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8174   exlen = GetWindowTextLength(hText);\r
8175   /* Find out whether current end of text is visible */\r
8176   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8177   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8178   /* Trim existing text if it's too long */\r
8179   if (exlen + (q - buf) > CO_MAX) {\r
8180     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8181     sel.cpMin = 0;\r
8182     sel.cpMax = trim;\r
8183     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8184     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8185     exlen -= trim;\r
8186     savesel.cpMin -= trim;\r
8187     savesel.cpMax -= trim;\r
8188     if (exlen < 0) exlen = 0;\r
8189     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8190     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8191   }\r
8192   /* Append the new text */\r
8193   sel.cpMin = exlen;\r
8194   sel.cpMax = exlen;\r
8195   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8196   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8197   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8198   if (forceVisible || exlen == 0 ||\r
8199       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8200        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8201     /* Scroll to make new end of text visible if old end of text\r
8202        was visible or new text is an echo of user typein */\r
8203     sel.cpMin = 9999999;\r
8204     sel.cpMax = 9999999;\r
8205     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8206     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8207     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8208     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8209   }\r
8210   if (savesel.cpMax == exlen || forceVisible) {\r
8211     /* Move insert point to new end of text if it was at the old\r
8212        end of text or if the new text is an echo of user typein */\r
8213     sel.cpMin = 9999999;\r
8214     sel.cpMax = 9999999;\r
8215     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8216   } else {\r
8217     /* Restore previous selection */\r
8218     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8219   }\r
8220   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8221 }\r
8222 \r
8223 /*---------*/\r
8224 \r
8225 \r
8226 void\r
8227 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8228 {\r
8229   char buf[100];\r
8230   char *str;\r
8231   COLORREF oldFg, oldBg;\r
8232   HFONT oldFont;\r
8233   RECT rect;\r
8234 \r
8235   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8236 \r
8237   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8238   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8239   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8240 \r
8241   rect.left = x;\r
8242   rect.right = x + squareSize;\r
8243   rect.top  = y;\r
8244   rect.bottom = y + squareSize;\r
8245   str = buf;\r
8246 \r
8247   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8248                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8249              y, ETO_CLIPPED|ETO_OPAQUE,\r
8250              &rect, str, strlen(str), NULL);\r
8251 \r
8252   (void) SetTextColor(hdc, oldFg);\r
8253   (void) SetBkColor(hdc, oldBg);\r
8254   (void) SelectObject(hdc, oldFont);\r
8255 }\r
8256 \r
8257 void\r
8258 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8259               RECT *rect, char *color, char *flagFell)\r
8260 {\r
8261   char buf[100];\r
8262   char *str;\r
8263   COLORREF oldFg, oldBg;\r
8264   HFONT oldFont;\r
8265 \r
8266   if (appData.clockMode) {\r
8267     if (tinyLayout)\r
8268       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8269     else\r
8270       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8271     str = buf;\r
8272   } else {\r
8273     str = color;\r
8274   }\r
8275 \r
8276   if (highlight) {\r
8277     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8278     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8279   } else {\r
8280     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8281     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8282   }\r
8283   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8284 \r
8285   JAWS_SILENCE\r
8286 \r
8287   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8288              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8289              rect, str, strlen(str), NULL);\r
8290   if(logoHeight > 0 && appData.clockMode) {\r
8291       RECT r;\r
8292       sprintf(buf, "%s %s", buf+7, flagFell);\r
8293       r.top = rect->top + logoHeight/2;\r
8294       r.left = rect->left;\r
8295       r.right = rect->right;\r
8296       r.bottom = rect->bottom;\r
8297       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8298                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8299                  &r, str, strlen(str), NULL);\r
8300   }\r
8301   (void) SetTextColor(hdc, oldFg);\r
8302   (void) SetBkColor(hdc, oldBg);\r
8303   (void) SelectObject(hdc, oldFont);\r
8304 }\r
8305 \r
8306 \r
8307 int\r
8308 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8309            OVERLAPPED *ovl)\r
8310 {\r
8311   int ok, err;\r
8312 \r
8313   /* [AS]  */\r
8314   if( count <= 0 ) {\r
8315     if (appData.debugMode) {\r
8316       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8317     }\r
8318 \r
8319     return ERROR_INVALID_USER_BUFFER;\r
8320   }\r
8321 \r
8322   ResetEvent(ovl->hEvent);\r
8323   ovl->Offset = ovl->OffsetHigh = 0;\r
8324   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8325   if (ok) {\r
8326     err = NO_ERROR;\r
8327   } else {\r
8328     err = GetLastError();\r
8329     if (err == ERROR_IO_PENDING) {\r
8330       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8331       if (ok)\r
8332         err = NO_ERROR;\r
8333       else\r
8334         err = GetLastError();\r
8335     }\r
8336   }\r
8337   return err;\r
8338 }\r
8339 \r
8340 int\r
8341 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8342             OVERLAPPED *ovl)\r
8343 {\r
8344   int ok, err;\r
8345 \r
8346   ResetEvent(ovl->hEvent);\r
8347   ovl->Offset = ovl->OffsetHigh = 0;\r
8348   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8349   if (ok) {\r
8350     err = NO_ERROR;\r
8351   } else {\r
8352     err = GetLastError();\r
8353     if (err == ERROR_IO_PENDING) {\r
8354       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8355       if (ok)\r
8356         err = NO_ERROR;\r
8357       else\r
8358         err = GetLastError();\r
8359     }\r
8360   }\r
8361   return err;\r
8362 }\r
8363 \r
8364 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8365 void CheckForInputBufferFull( InputSource * is )\r
8366 {\r
8367     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8368         /* Look for end of line */\r
8369         char * p = is->buf;\r
8370         \r
8371         while( p < is->next && *p != '\n' ) {\r
8372             p++;\r
8373         }\r
8374 \r
8375         if( p >= is->next ) {\r
8376             if (appData.debugMode) {\r
8377                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8378             }\r
8379 \r
8380             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8381             is->count = (DWORD) -1;\r
8382             is->next = is->buf;\r
8383         }\r
8384     }\r
8385 }\r
8386 \r
8387 DWORD\r
8388 InputThread(LPVOID arg)\r
8389 {\r
8390   InputSource *is;\r
8391   OVERLAPPED ovl;\r
8392 \r
8393   is = (InputSource *) arg;\r
8394   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8395   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8396   while (is->hThread != NULL) {\r
8397     is->error = DoReadFile(is->hFile, is->next,\r
8398                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8399                            &is->count, &ovl);\r
8400     if (is->error == NO_ERROR) {\r
8401       is->next += is->count;\r
8402     } else {\r
8403       if (is->error == ERROR_BROKEN_PIPE) {\r
8404         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8405         is->count = 0;\r
8406       } else {\r
8407         is->count = (DWORD) -1;\r
8408         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8409         break; \r
8410       }\r
8411     }\r
8412 \r
8413     CheckForInputBufferFull( is );\r
8414 \r
8415     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8416 \r
8417     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8418 \r
8419     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8420   }\r
8421 \r
8422   CloseHandle(ovl.hEvent);\r
8423   CloseHandle(is->hFile);\r
8424 \r
8425   if (appData.debugMode) {\r
8426     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8427   }\r
8428 \r
8429   return 0;\r
8430 }\r
8431 \r
8432 \r
8433 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8434 DWORD\r
8435 NonOvlInputThread(LPVOID arg)\r
8436 {\r
8437   InputSource *is;\r
8438   char *p, *q;\r
8439   int i;\r
8440   char prev;\r
8441 \r
8442   is = (InputSource *) arg;\r
8443   while (is->hThread != NULL) {\r
8444     is->error = ReadFile(is->hFile, is->next,\r
8445                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8446                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8447     if (is->error == NO_ERROR) {\r
8448       /* Change CRLF to LF */\r
8449       if (is->next > is->buf) {\r
8450         p = is->next - 1;\r
8451         i = is->count + 1;\r
8452       } else {\r
8453         p = is->next;\r
8454         i = is->count;\r
8455       }\r
8456       q = p;\r
8457       prev = NULLCHAR;\r
8458       while (i > 0) {\r
8459         if (prev == '\r' && *p == '\n') {\r
8460           *(q-1) = '\n';\r
8461           is->count--;\r
8462         } else { \r
8463           *q++ = *p;\r
8464         }\r
8465         prev = *p++;\r
8466         i--;\r
8467       }\r
8468       *q = NULLCHAR;\r
8469       is->next = q;\r
8470     } else {\r
8471       if (is->error == ERROR_BROKEN_PIPE) {\r
8472         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8473         is->count = 0; \r
8474       } else {\r
8475         is->count = (DWORD) -1;\r
8476       }\r
8477     }\r
8478 \r
8479     CheckForInputBufferFull( is );\r
8480 \r
8481     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8482 \r
8483     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8484 \r
8485     if (is->count < 0) break;  /* Quit on error */\r
8486   }\r
8487   CloseHandle(is->hFile);\r
8488   return 0;\r
8489 }\r
8490 \r
8491 DWORD\r
8492 SocketInputThread(LPVOID arg)\r
8493 {\r
8494   InputSource *is;\r
8495 \r
8496   is = (InputSource *) arg;\r
8497   while (is->hThread != NULL) {\r
8498     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8499     if ((int)is->count == SOCKET_ERROR) {\r
8500       is->count = (DWORD) -1;\r
8501       is->error = WSAGetLastError();\r
8502     } else {\r
8503       is->error = NO_ERROR;\r
8504       is->next += is->count;\r
8505       if (is->count == 0 && is->second == is) {\r
8506         /* End of file on stderr; quit with no message */\r
8507         break;\r
8508       }\r
8509     }\r
8510     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8511 \r
8512     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8513 \r
8514     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8515   }\r
8516   return 0;\r
8517 }\r
8518 \r
8519 VOID\r
8520 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8521 {\r
8522   InputSource *is;\r
8523 \r
8524   is = (InputSource *) lParam;\r
8525   if (is->lineByLine) {\r
8526     /* Feed in lines one by one */\r
8527     char *p = is->buf;\r
8528     char *q = p;\r
8529     while (q < is->next) {\r
8530       if (*q++ == '\n') {\r
8531         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8532         p = q;\r
8533       }\r
8534     }\r
8535     \r
8536     /* Move any partial line to the start of the buffer */\r
8537     q = is->buf;\r
8538     while (p < is->next) {\r
8539       *q++ = *p++;\r
8540     }\r
8541     is->next = q;\r
8542 \r
8543     if (is->error != NO_ERROR || is->count == 0) {\r
8544       /* Notify backend of the error.  Note: If there was a partial\r
8545          line at the end, it is not flushed through. */\r
8546       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8547     }\r
8548   } else {\r
8549     /* Feed in the whole chunk of input at once */\r
8550     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8551     is->next = is->buf;\r
8552   }\r
8553 }\r
8554 \r
8555 /*---------------------------------------------------------------------------*\\r
8556  *\r
8557  *  Menu enables. Used when setting various modes.\r
8558  *\r
8559 \*---------------------------------------------------------------------------*/\r
8560 \r
8561 typedef struct {\r
8562   int item;\r
8563   int flags;\r
8564 } Enables;\r
8565 \r
8566 VOID\r
8567 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8568 {\r
8569   while (enab->item > 0) {\r
8570     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8571     enab++;\r
8572   }\r
8573 }\r
8574 \r
8575 Enables gnuEnables[] = {\r
8576   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8577   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8578   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8579   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8580   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8581   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8582   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8583   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8584   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8585   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8586   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8587   { -1, -1 }\r
8588 };\r
8589 \r
8590 Enables icsEnables[] = {\r
8591   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8592   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8593   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8594   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8595   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8596   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8597   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8598   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8599   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8600   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8601   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8602   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8603   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8604   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8605   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8606   { -1, -1 }\r
8607 };\r
8608 \r
8609 #ifdef ZIPPY\r
8610 Enables zippyEnables[] = {\r
8611   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8612   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8613   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8614   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8615   { -1, -1 }\r
8616 };\r
8617 #endif\r
8618 \r
8619 Enables ncpEnables[] = {\r
8620   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8621   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8622   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8623   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8624   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8625   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8626   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8627   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8628   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8629   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8630   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8631   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8632   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8633   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8634   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8635   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8636   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8637   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8638   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8639   { -1, -1 }\r
8640 };\r
8641 \r
8642 Enables trainingOnEnables[] = {\r
8643   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8644   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8645   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8646   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8647   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8648   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8649   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8650   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8651   { -1, -1 }\r
8652 };\r
8653 \r
8654 Enables trainingOffEnables[] = {\r
8655   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8656   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8657   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8658   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8659   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8660   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8661   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8662   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8663   { -1, -1 }\r
8664 };\r
8665 \r
8666 /* These modify either ncpEnables or gnuEnables */\r
8667 Enables cmailEnables[] = {\r
8668   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8669   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8670   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8671   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8672   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8673   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8674   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8675   { -1, -1 }\r
8676 };\r
8677 \r
8678 Enables machineThinkingEnables[] = {\r
8679   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8680   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8681   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8682   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8683   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8684   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8685   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8686   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8687   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8688   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8689   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8690   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8691   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8692   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8693   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8694   { -1, -1 }\r
8695 };\r
8696 \r
8697 Enables userThinkingEnables[] = {\r
8698   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8699   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8700   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8701   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8702   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8703   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8704   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8705   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8706   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8707   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8708   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8709   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8710   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8711   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8712   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8713   { -1, -1 }\r
8714 };\r
8715 \r
8716 /*---------------------------------------------------------------------------*\\r
8717  *\r
8718  *  Front-end interface functions exported by XBoard.\r
8719  *  Functions appear in same order as prototypes in frontend.h.\r
8720  * \r
8721 \*---------------------------------------------------------------------------*/\r
8722 VOID\r
8723 ModeHighlight()\r
8724 {\r
8725   static UINT prevChecked = 0;\r
8726   static int prevPausing = 0;\r
8727   UINT nowChecked;\r
8728 \r
8729   if (pausing != prevPausing) {\r
8730     prevPausing = pausing;\r
8731     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8732                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8733     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8734   }\r
8735 \r
8736   switch (gameMode) {\r
8737   case BeginningOfGame:\r
8738     if (appData.icsActive)\r
8739       nowChecked = IDM_IcsClient;\r
8740     else if (appData.noChessProgram)\r
8741       nowChecked = IDM_EditGame;\r
8742     else\r
8743       nowChecked = IDM_MachineBlack;\r
8744     break;\r
8745   case MachinePlaysBlack:\r
8746     nowChecked = IDM_MachineBlack;\r
8747     break;\r
8748   case MachinePlaysWhite:\r
8749     nowChecked = IDM_MachineWhite;\r
8750     break;\r
8751   case TwoMachinesPlay:\r
8752     nowChecked = IDM_TwoMachines;\r
8753     break;\r
8754   case AnalyzeMode:\r
8755     nowChecked = IDM_AnalysisMode;\r
8756     break;\r
8757   case AnalyzeFile:\r
8758     nowChecked = IDM_AnalyzeFile;\r
8759     break;\r
8760   case EditGame:\r
8761     nowChecked = IDM_EditGame;\r
8762     break;\r
8763   case PlayFromGameFile:\r
8764     nowChecked = IDM_LoadGame;\r
8765     break;\r
8766   case EditPosition:\r
8767     nowChecked = IDM_EditPosition;\r
8768     break;\r
8769   case Training:\r
8770     nowChecked = IDM_Training;\r
8771     break;\r
8772   case IcsPlayingWhite:\r
8773   case IcsPlayingBlack:\r
8774   case IcsObserving:\r
8775   case IcsIdle:\r
8776     nowChecked = IDM_IcsClient;\r
8777     break;\r
8778   default:\r
8779   case EndOfGame:\r
8780     nowChecked = 0;\r
8781     break;\r
8782   }\r
8783   if (prevChecked != 0)\r
8784     (void) CheckMenuItem(GetMenu(hwndMain),\r
8785                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8786   if (nowChecked != 0)\r
8787     (void) CheckMenuItem(GetMenu(hwndMain),\r
8788                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8789 \r
8790   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8791     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8792                           MF_BYCOMMAND|MF_ENABLED);\r
8793   } else {\r
8794     (void) EnableMenuItem(GetMenu(hwndMain), \r
8795                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8796   }\r
8797 \r
8798   prevChecked = nowChecked;\r
8799 \r
8800   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8801   if (appData.icsActive) {\r
8802        if (appData.icsEngineAnalyze) {\r
8803                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8804                        MF_BYCOMMAND|MF_CHECKED);\r
8805        } else {\r
8806                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8807                        MF_BYCOMMAND|MF_UNCHECKED);\r
8808        }\r
8809   }\r
8810 }\r
8811 \r
8812 VOID\r
8813 SetICSMode()\r
8814 {\r
8815   HMENU hmenu = GetMenu(hwndMain);\r
8816   SetMenuEnables(hmenu, icsEnables);\r
8817   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8818     MF_BYPOSITION|MF_ENABLED);\r
8819 #ifdef ZIPPY\r
8820   if (appData.zippyPlay) {\r
8821     SetMenuEnables(hmenu, zippyEnables);\r
8822     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8823          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8824           MF_BYCOMMAND|MF_ENABLED);\r
8825   }\r
8826 #endif\r
8827 }\r
8828 \r
8829 VOID\r
8830 SetGNUMode()\r
8831 {\r
8832   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8833 }\r
8834 \r
8835 VOID\r
8836 SetNCPMode()\r
8837 {\r
8838   HMENU hmenu = GetMenu(hwndMain);\r
8839   SetMenuEnables(hmenu, ncpEnables);\r
8840   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8841     MF_BYPOSITION|MF_GRAYED);\r
8842     DrawMenuBar(hwndMain);\r
8843 }\r
8844 \r
8845 VOID\r
8846 SetCmailMode()\r
8847 {\r
8848   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8849 }\r
8850 \r
8851 VOID \r
8852 SetTrainingModeOn()\r
8853 {\r
8854   int i;\r
8855   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8856   for (i = 0; i < N_BUTTONS; i++) {\r
8857     if (buttonDesc[i].hwnd != NULL)\r
8858       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8859   }\r
8860   CommentPopDown();\r
8861 }\r
8862 \r
8863 VOID SetTrainingModeOff()\r
8864 {\r
8865   int i;\r
8866   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8867   for (i = 0; i < N_BUTTONS; i++) {\r
8868     if (buttonDesc[i].hwnd != NULL)\r
8869       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8870   }\r
8871 }\r
8872 \r
8873 \r
8874 VOID\r
8875 SetUserThinkingEnables()\r
8876 {\r
8877   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8878 }\r
8879 \r
8880 VOID\r
8881 SetMachineThinkingEnables()\r
8882 {\r
8883   HMENU hMenu = GetMenu(hwndMain);\r
8884   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8885 \r
8886   SetMenuEnables(hMenu, machineThinkingEnables);\r
8887 \r
8888   if (gameMode == MachinePlaysBlack) {\r
8889     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8890   } else if (gameMode == MachinePlaysWhite) {\r
8891     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8892   } else if (gameMode == TwoMachinesPlay) {\r
8893     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8894   }\r
8895 }\r
8896 \r
8897 \r
8898 VOID\r
8899 DisplayTitle(char *str)\r
8900 {\r
8901   char title[MSG_SIZ], *host;\r
8902   if (str[0] != NULLCHAR) {\r
8903     strcpy(title, str);\r
8904   } else if (appData.icsActive) {\r
8905     if (appData.icsCommPort[0] != NULLCHAR)\r
8906       host = "ICS";\r
8907     else \r
8908       host = appData.icsHost;\r
8909     sprintf(title, "%s: %s", szTitle, host);\r
8910   } else if (appData.noChessProgram) {\r
8911     strcpy(title, szTitle);\r
8912   } else {\r
8913     strcpy(title, szTitle);\r
8914     strcat(title, ": ");\r
8915     strcat(title, first.tidy);\r
8916   }\r
8917   SetWindowText(hwndMain, title);\r
8918 }\r
8919 \r
8920 \r
8921 VOID\r
8922 DisplayMessage(char *str1, char *str2)\r
8923 {\r
8924   HDC hdc;\r
8925   HFONT oldFont;\r
8926   int remain = MESSAGE_TEXT_MAX - 1;\r
8927   int len;\r
8928 \r
8929   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8930   messageText[0] = NULLCHAR;\r
8931   if (*str1) {\r
8932     len = strlen(str1);\r
8933     if (len > remain) len = remain;\r
8934     strncpy(messageText, str1, len);\r
8935     messageText[len] = NULLCHAR;\r
8936     remain -= len;\r
8937   }\r
8938   if (*str2 && remain >= 2) {\r
8939     if (*str1) {\r
8940       strcat(messageText, "  ");\r
8941       remain -= 2;\r
8942     }\r
8943     len = strlen(str2);\r
8944     if (len > remain) len = remain;\r
8945     strncat(messageText, str2, len);\r
8946   }\r
8947   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8948 \r
8949   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8950 \r
8951   SAYMACHINEMOVE();\r
8952 \r
8953   hdc = GetDC(hwndMain);\r
8954   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8955   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8956              &messageRect, messageText, strlen(messageText), NULL);\r
8957   (void) SelectObject(hdc, oldFont);\r
8958   (void) ReleaseDC(hwndMain, hdc);\r
8959 }\r
8960 \r
8961 VOID\r
8962 DisplayError(char *str, int error)\r
8963 {\r
8964   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8965   int len;\r
8966 \r
8967   if (error == 0) {\r
8968     strcpy(buf, str);\r
8969   } else {\r
8970     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8971                         NULL, error, LANG_NEUTRAL,\r
8972                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8973     if (len > 0) {\r
8974       sprintf(buf, "%s:\n%s", str, buf2);\r
8975     } else {\r
8976       ErrorMap *em = errmap;\r
8977       while (em->err != 0 && em->err != error) em++;\r
8978       if (em->err != 0) {\r
8979         sprintf(buf, "%s:\n%s", str, em->msg);\r
8980       } else {\r
8981         sprintf(buf, "%s:\nError code %d", str, error);\r
8982       }\r
8983     }\r
8984   }\r
8985   \r
8986   ErrorPopUp("Error", buf);\r
8987 }\r
8988 \r
8989 \r
8990 VOID\r
8991 DisplayMoveError(char *str)\r
8992 {\r
8993   fromX = fromY = -1;\r
8994   ClearHighlights();\r
8995   DrawPosition(FALSE, NULL);\r
8996   if (appData.popupMoveErrors) {\r
8997     ErrorPopUp("Error", str);\r
8998   } else {\r
8999     DisplayMessage(str, "");\r
9000     moveErrorMessageUp = TRUE;\r
9001   }\r
9002 }\r
9003 \r
9004 VOID\r
9005 DisplayFatalError(char *str, int error, int exitStatus)\r
9006 {\r
9007   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9008   int len;\r
9009   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9010 \r
9011   if (error != 0) {\r
9012     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9013                         NULL, error, LANG_NEUTRAL,\r
9014                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9015     if (len > 0) {\r
9016       sprintf(buf, "%s:\n%s", str, buf2);\r
9017     } else {\r
9018       ErrorMap *em = errmap;\r
9019       while (em->err != 0 && em->err != error) em++;\r
9020       if (em->err != 0) {\r
9021         sprintf(buf, "%s:\n%s", str, em->msg);\r
9022       } else {\r
9023         sprintf(buf, "%s:\nError code %d", str, error);\r
9024       }\r
9025     }\r
9026     str = buf;\r
9027   }\r
9028   if (appData.debugMode) {\r
9029     fprintf(debugFP, "%s: %s\n", label, str);\r
9030   }\r
9031   if (appData.popupExitMessage) {\r
9032     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9033                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9034   }\r
9035   ExitEvent(exitStatus);\r
9036 }\r
9037 \r
9038 \r
9039 VOID\r
9040 DisplayInformation(char *str)\r
9041 {\r
9042   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9043 }\r
9044 \r
9045 \r
9046 VOID\r
9047 DisplayNote(char *str)\r
9048 {\r
9049   ErrorPopUp("Note", str);\r
9050 }\r
9051 \r
9052 \r
9053 typedef struct {\r
9054   char *title, *question, *replyPrefix;\r
9055   ProcRef pr;\r
9056 } QuestionParams;\r
9057 \r
9058 LRESULT CALLBACK\r
9059 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9060 {\r
9061   static QuestionParams *qp;\r
9062   char reply[MSG_SIZ];\r
9063   int len, err;\r
9064 \r
9065   switch (message) {\r
9066   case WM_INITDIALOG:\r
9067     qp = (QuestionParams *) lParam;\r
9068     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9069     SetWindowText(hDlg, qp->title);\r
9070     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9071     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9072     return FALSE;\r
9073 \r
9074   case WM_COMMAND:\r
9075     switch (LOWORD(wParam)) {\r
9076     case IDOK:\r
9077       strcpy(reply, qp->replyPrefix);\r
9078       if (*reply) strcat(reply, " ");\r
9079       len = strlen(reply);\r
9080       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9081       strcat(reply, "\n");\r
9082       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9083       EndDialog(hDlg, TRUE);\r
9084       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9085       return TRUE;\r
9086     case IDCANCEL:\r
9087       EndDialog(hDlg, FALSE);\r
9088       return TRUE;\r
9089     default:\r
9090       break;\r
9091     }\r
9092     break;\r
9093   }\r
9094   return FALSE;\r
9095 }\r
9096 \r
9097 VOID\r
9098 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9099 {\r
9100     QuestionParams qp;\r
9101     FARPROC lpProc;\r
9102     \r
9103     qp.title = title;\r
9104     qp.question = question;\r
9105     qp.replyPrefix = replyPrefix;\r
9106     qp.pr = pr;\r
9107     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9108     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9109       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9110     FreeProcInstance(lpProc);\r
9111 }\r
9112 \r
9113 /* [AS] Pick FRC position */\r
9114 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9115 {\r
9116     static int * lpIndexFRC;\r
9117     BOOL index_is_ok;\r
9118     char buf[16];\r
9119 \r
9120     switch( message )\r
9121     {\r
9122     case WM_INITDIALOG:\r
9123         lpIndexFRC = (int *) lParam;\r
9124 \r
9125         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9126 \r
9127         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9128         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9129         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9130         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9131 \r
9132         break;\r
9133 \r
9134     case WM_COMMAND:\r
9135         switch( LOWORD(wParam) ) {\r
9136         case IDOK:\r
9137             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9138             EndDialog( hDlg, 0 );\r
9139             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9140             return TRUE;\r
9141         case IDCANCEL:\r
9142             EndDialog( hDlg, 1 );   \r
9143             return TRUE;\r
9144         case IDC_NFG_Edit:\r
9145             if( HIWORD(wParam) == EN_CHANGE ) {\r
9146                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9147 \r
9148                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9149             }\r
9150             return TRUE;\r
9151         case IDC_NFG_Random:\r
9152             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9153             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9154             return TRUE;\r
9155         }\r
9156 \r
9157         break;\r
9158     }\r
9159 \r
9160     return FALSE;\r
9161 }\r
9162 \r
9163 int NewGameFRC()\r
9164 {\r
9165     int result;\r
9166     int index = appData.defaultFrcPosition;\r
9167     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9168 \r
9169     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9170 \r
9171     if( result == 0 ) {\r
9172         appData.defaultFrcPosition = index;\r
9173     }\r
9174 \r
9175     return result;\r
9176 }\r
9177 \r
9178 /* [AS] Game list options */\r
9179 typedef struct {\r
9180     char id;\r
9181     char * name;\r
9182 } GLT_Item;\r
9183 \r
9184 static GLT_Item GLT_ItemInfo[] = {\r
9185     { GLT_EVENT,      "Event" },\r
9186     { GLT_SITE,       "Site" },\r
9187     { GLT_DATE,       "Date" },\r
9188     { GLT_ROUND,      "Round" },\r
9189     { GLT_PLAYERS,    "Players" },\r
9190     { GLT_RESULT,     "Result" },\r
9191     { GLT_WHITE_ELO,  "White Rating" },\r
9192     { GLT_BLACK_ELO,  "Black Rating" },\r
9193     { GLT_TIME_CONTROL,"Time Control" },\r
9194     { GLT_VARIANT,    "Variant" },\r
9195     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9196     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9197     { 0, 0 }\r
9198 };\r
9199 \r
9200 const char * GLT_FindItem( char id )\r
9201 {\r
9202     const char * result = 0;\r
9203 \r
9204     GLT_Item * list = GLT_ItemInfo;\r
9205 \r
9206     while( list->id != 0 ) {\r
9207         if( list->id == id ) {\r
9208             result = list->name;\r
9209             break;\r
9210         }\r
9211 \r
9212         list++;\r
9213     }\r
9214 \r
9215     return result;\r
9216 }\r
9217 \r
9218 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9219 {\r
9220     const char * name = GLT_FindItem( id );\r
9221 \r
9222     if( name != 0 ) {\r
9223         if( index >= 0 ) {\r
9224             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9225         }\r
9226         else {\r
9227             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9228         }\r
9229     }\r
9230 }\r
9231 \r
9232 void GLT_TagsToList( HWND hDlg, char * tags )\r
9233 {\r
9234     char * pc = tags;\r
9235 \r
9236     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9237 \r
9238     while( *pc ) {\r
9239         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9240         pc++;\r
9241     }\r
9242 \r
9243     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9244 \r
9245     pc = GLT_ALL_TAGS;\r
9246 \r
9247     while( *pc ) {\r
9248         if( strchr( tags, *pc ) == 0 ) {\r
9249             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9250         }\r
9251         pc++;\r
9252     }\r
9253 \r
9254     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9255 }\r
9256 \r
9257 char GLT_ListItemToTag( HWND hDlg, int index )\r
9258 {\r
9259     char result = '\0';\r
9260     char name[128];\r
9261 \r
9262     GLT_Item * list = GLT_ItemInfo;\r
9263 \r
9264     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9265         while( list->id != 0 ) {\r
9266             if( strcmp( list->name, name ) == 0 ) {\r
9267                 result = list->id;\r
9268                 break;\r
9269             }\r
9270 \r
9271             list++;\r
9272         }\r
9273     }\r
9274 \r
9275     return result;\r
9276 }\r
9277 \r
9278 void GLT_MoveSelection( HWND hDlg, int delta )\r
9279 {\r
9280     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9281     int idx2 = idx1 + delta;\r
9282     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9283 \r
9284     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9285         char buf[128];\r
9286 \r
9287         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9288         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9289         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9290         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9291     }\r
9292 }\r
9293 \r
9294 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9295 {\r
9296     static char glt[64];\r
9297     static char * lpUserGLT;\r
9298 \r
9299     switch( message )\r
9300     {\r
9301     case WM_INITDIALOG:\r
9302         lpUserGLT = (char *) lParam;\r
9303         \r
9304         strcpy( glt, lpUserGLT );\r
9305 \r
9306         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9307 \r
9308         /* Initialize list */\r
9309         GLT_TagsToList( hDlg, glt );\r
9310 \r
9311         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9312 \r
9313         break;\r
9314 \r
9315     case WM_COMMAND:\r
9316         switch( LOWORD(wParam) ) {\r
9317         case IDOK:\r
9318             {\r
9319                 char * pc = lpUserGLT;\r
9320                 int idx = 0;\r
9321 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9322                 char id;\r
9323 \r
9324                 do {\r
9325                     id = GLT_ListItemToTag( hDlg, idx );\r
9326 \r
9327                     *pc++ = id;\r
9328                     idx++;\r
9329                 } while( id != '\0' );\r
9330             }\r
9331             EndDialog( hDlg, 0 );\r
9332             return TRUE;\r
9333         case IDCANCEL:\r
9334             EndDialog( hDlg, 1 );\r
9335             return TRUE;\r
9336 \r
9337         case IDC_GLT_Default:\r
9338             strcpy( glt, GLT_DEFAULT_TAGS );\r
9339             GLT_TagsToList( hDlg, glt );\r
9340             return TRUE;\r
9341 \r
9342         case IDC_GLT_Restore:\r
9343             strcpy( glt, lpUserGLT );\r
9344             GLT_TagsToList( hDlg, glt );\r
9345             return TRUE;\r
9346 \r
9347         case IDC_GLT_Up:\r
9348             GLT_MoveSelection( hDlg, -1 );\r
9349             return TRUE;\r
9350 \r
9351         case IDC_GLT_Down:\r
9352             GLT_MoveSelection( hDlg, +1 );\r
9353             return TRUE;\r
9354         }\r
9355 \r
9356         break;\r
9357     }\r
9358 \r
9359     return FALSE;\r
9360 }\r
9361 \r
9362 int GameListOptions()\r
9363 {\r
9364     char glt[64];\r
9365     int result;\r
9366     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9367 \r
9368     strcpy( glt, appData.gameListTags );\r
9369 \r
9370     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9371 \r
9372     if( result == 0 ) {\r
9373         /* [AS] Memory leak here! */\r
9374         appData.gameListTags = strdup( glt ); \r
9375     }\r
9376 \r
9377     return result;\r
9378 }\r
9379 \r
9380 \r
9381 VOID\r
9382 DisplayIcsInteractionTitle(char *str)\r
9383 {\r
9384   char consoleTitle[MSG_SIZ];\r
9385 \r
9386   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9387   SetWindowText(hwndConsole, consoleTitle);\r
9388 }\r
9389 \r
9390 void\r
9391 DrawPosition(int fullRedraw, Board board)\r
9392 {\r
9393   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9394 }\r
9395 \r
9396 void NotifyFrontendLogin()\r
9397 {\r
9398         if (hwndConsole)\r
9399                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
9400 }\r
9401 \r
9402 VOID\r
9403 ResetFrontEnd()\r
9404 {\r
9405   fromX = fromY = -1;\r
9406   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9407     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9408     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9409     dragInfo.lastpos = dragInfo.pos;\r
9410     dragInfo.start.x = dragInfo.start.y = -1;\r
9411     dragInfo.from = dragInfo.start;\r
9412     ReleaseCapture();\r
9413     DrawPosition(TRUE, NULL);\r
9414   }\r
9415 }\r
9416 \r
9417 \r
9418 VOID\r
9419 CommentPopUp(char *title, char *str)\r
9420 {\r
9421   HWND hwnd = GetActiveWindow();\r
9422   EitherCommentPopUp(0, title, str, FALSE);\r
9423   SAY(str);\r
9424   SetActiveWindow(hwnd);\r
9425 }\r
9426 \r
9427 VOID\r
9428 CommentPopDown(void)\r
9429 {\r
9430   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9431   if (commentDialog) {\r
9432     ShowWindow(commentDialog, SW_HIDE);\r
9433   }\r
9434   commentUp = FALSE;\r
9435 }\r
9436 \r
9437 VOID\r
9438 EditCommentPopUp(int index, char *title, char *str)\r
9439 {\r
9440   EitherCommentPopUp(index, title, str, TRUE);\r
9441 }\r
9442 \r
9443 \r
9444 VOID\r
9445 RingBell()\r
9446 {\r
9447   MyPlaySound(&sounds[(int)SoundMove]);\r
9448 }\r
9449 \r
9450 VOID PlayIcsWinSound()\r
9451 {\r
9452   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9453 }\r
9454 \r
9455 VOID PlayIcsLossSound()\r
9456 {\r
9457   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9458 }\r
9459 \r
9460 VOID PlayIcsDrawSound()\r
9461 {\r
9462   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9463 }\r
9464 \r
9465 VOID PlayIcsUnfinishedSound()\r
9466 {\r
9467   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9468 }\r
9469 \r
9470 VOID\r
9471 PlayAlarmSound()\r
9472 {\r
9473   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9474 }\r
9475 \r
9476 \r
9477 VOID\r
9478 EchoOn()\r
9479 {\r
9480   HWND hInput;\r
9481   consoleEcho = TRUE;\r
9482   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9483   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9484   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9485 }\r
9486 \r
9487 \r
9488 VOID\r
9489 EchoOff()\r
9490 {\r
9491   CHARFORMAT cf;\r
9492   HWND hInput;\r
9493   consoleEcho = FALSE;\r
9494   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9495   /* This works OK: set text and background both to the same color */\r
9496   cf = consoleCF;\r
9497   cf.crTextColor = COLOR_ECHOOFF;\r
9498   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9499   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9500 }\r
9501 \r
9502 /* No Raw()...? */\r
9503 \r
9504 void Colorize(ColorClass cc, int continuation)\r
9505 {\r
9506   currentColorClass = cc;\r
9507   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9508   consoleCF.crTextColor = textAttribs[cc].color;\r
9509   consoleCF.dwEffects = textAttribs[cc].effects;\r
9510   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9511 }\r
9512 \r
9513 char *\r
9514 UserName()\r
9515 {\r
9516   static char buf[MSG_SIZ];\r
9517   DWORD bufsiz = MSG_SIZ;\r
9518 \r
9519   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9520         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9521   }\r
9522   if (!GetUserName(buf, &bufsiz)) {\r
9523     /*DisplayError("Error getting user name", GetLastError());*/\r
9524     strcpy(buf, "User");\r
9525   }\r
9526   return buf;\r
9527 }\r
9528 \r
9529 char *\r
9530 HostName()\r
9531 {\r
9532   static char buf[MSG_SIZ];\r
9533   DWORD bufsiz = MSG_SIZ;\r
9534 \r
9535   if (!GetComputerName(buf, &bufsiz)) {\r
9536     /*DisplayError("Error getting host name", GetLastError());*/\r
9537     strcpy(buf, "Unknown");\r
9538   }\r
9539   return buf;\r
9540 }\r
9541 \r
9542 \r
9543 int\r
9544 ClockTimerRunning()\r
9545 {\r
9546   return clockTimerEvent != 0;\r
9547 }\r
9548 \r
9549 int\r
9550 StopClockTimer()\r
9551 {\r
9552   if (clockTimerEvent == 0) return FALSE;\r
9553   KillTimer(hwndMain, clockTimerEvent);\r
9554   clockTimerEvent = 0;\r
9555   return TRUE;\r
9556 }\r
9557 \r
9558 void\r
9559 StartClockTimer(long millisec)\r
9560 {\r
9561   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9562                              (UINT) millisec, NULL);\r
9563 }\r
9564 \r
9565 void\r
9566 DisplayWhiteClock(long timeRemaining, int highlight)\r
9567 {\r
9568   HDC hdc;\r
9569   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9570 \r
9571   if(appData.noGUI) return;\r
9572   hdc = GetDC(hwndMain);\r
9573   if (!IsIconic(hwndMain)) {\r
9574     DisplayAClock(hdc, timeRemaining, highlight, \r
9575                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9576   }\r
9577   if (highlight && iconCurrent == iconBlack) {\r
9578     iconCurrent = iconWhite;\r
9579     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9580     if (IsIconic(hwndMain)) {\r
9581       DrawIcon(hdc, 2, 2, iconCurrent);\r
9582     }\r
9583   }\r
9584   (void) ReleaseDC(hwndMain, hdc);\r
9585   if (hwndConsole)\r
9586     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9587 }\r
9588 \r
9589 void\r
9590 DisplayBlackClock(long timeRemaining, int highlight)\r
9591 {\r
9592   HDC hdc;\r
9593   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9594 \r
9595   if(appData.noGUI) return;\r
9596   hdc = GetDC(hwndMain);\r
9597   if (!IsIconic(hwndMain)) {\r
9598     DisplayAClock(hdc, timeRemaining, highlight, \r
9599                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9600   }\r
9601   if (highlight && iconCurrent == iconWhite) {\r
9602     iconCurrent = iconBlack;\r
9603     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9604     if (IsIconic(hwndMain)) {\r
9605       DrawIcon(hdc, 2, 2, iconCurrent);\r
9606     }\r
9607   }\r
9608   (void) ReleaseDC(hwndMain, hdc);\r
9609   if (hwndConsole)\r
9610     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9611 }\r
9612 \r
9613 \r
9614 int\r
9615 LoadGameTimerRunning()\r
9616 {\r
9617   return loadGameTimerEvent != 0;\r
9618 }\r
9619 \r
9620 int\r
9621 StopLoadGameTimer()\r
9622 {\r
9623   if (loadGameTimerEvent == 0) return FALSE;\r
9624   KillTimer(hwndMain, loadGameTimerEvent);\r
9625   loadGameTimerEvent = 0;\r
9626   return TRUE;\r
9627 }\r
9628 \r
9629 void\r
9630 StartLoadGameTimer(long millisec)\r
9631 {\r
9632   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9633                                 (UINT) millisec, NULL);\r
9634 }\r
9635 \r
9636 void\r
9637 AutoSaveGame()\r
9638 {\r
9639   char *defName;\r
9640   FILE *f;\r
9641   char fileTitle[MSG_SIZ];\r
9642 \r
9643   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9644   f = OpenFileDialog(hwndMain, "a", defName,\r
9645                      appData.oldSaveStyle ? "gam" : "pgn",\r
9646                      GAME_FILT, \r
9647                      "Save Game to File", NULL, fileTitle, NULL);\r
9648   if (f != NULL) {\r
9649     SaveGame(f, 0, "");\r
9650     fclose(f);\r
9651   }\r
9652 }\r
9653 \r
9654 \r
9655 void\r
9656 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9657 {\r
9658   if (delayedTimerEvent != 0) {\r
9659     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9660       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9661     }\r
9662     KillTimer(hwndMain, delayedTimerEvent);\r
9663     delayedTimerEvent = 0;\r
9664     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9665     delayedTimerCallback();\r
9666   }\r
9667   delayedTimerCallback = cb;\r
9668   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9669                                 (UINT) millisec, NULL);\r
9670 }\r
9671 \r
9672 DelayedEventCallback\r
9673 GetDelayedEvent()\r
9674 {\r
9675   if (delayedTimerEvent) {\r
9676     return delayedTimerCallback;\r
9677   } else {\r
9678     return NULL;\r
9679   }\r
9680 }\r
9681 \r
9682 void\r
9683 CancelDelayedEvent()\r
9684 {\r
9685   if (delayedTimerEvent) {\r
9686     KillTimer(hwndMain, delayedTimerEvent);\r
9687     delayedTimerEvent = 0;\r
9688   }\r
9689 }\r
9690 \r
9691 DWORD GetWin32Priority(int nice)\r
9692 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9693 /*\r
9694 REALTIME_PRIORITY_CLASS     0x00000100\r
9695 HIGH_PRIORITY_CLASS         0x00000080\r
9696 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9697 NORMAL_PRIORITY_CLASS       0x00000020\r
9698 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9699 IDLE_PRIORITY_CLASS         0x00000040\r
9700 */\r
9701         if (nice < -15) return 0x00000080;\r
9702         if (nice < 0)   return 0x00008000;\r
9703         if (nice == 0)  return 0x00000020;\r
9704         if (nice < 15)  return 0x00004000;\r
9705         return 0x00000040;\r
9706 }\r
9707 \r
9708 /* Start a child process running the given program.\r
9709    The process's standard output can be read from "from", and its\r
9710    standard input can be written to "to".\r
9711    Exit with fatal error if anything goes wrong.\r
9712    Returns an opaque pointer that can be used to destroy the process\r
9713    later.\r
9714 */\r
9715 int\r
9716 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9717 {\r
9718 #define BUFSIZE 4096\r
9719 \r
9720   HANDLE hChildStdinRd, hChildStdinWr,\r
9721     hChildStdoutRd, hChildStdoutWr;\r
9722   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9723   SECURITY_ATTRIBUTES saAttr;\r
9724   BOOL fSuccess;\r
9725   PROCESS_INFORMATION piProcInfo;\r
9726   STARTUPINFO siStartInfo;\r
9727   ChildProc *cp;\r
9728   char buf[MSG_SIZ];\r
9729   DWORD err;\r
9730 \r
9731   if (appData.debugMode) {\r
9732     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9733   }\r
9734 \r
9735   *pr = NoProc;\r
9736 \r
9737   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9738   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9739   saAttr.bInheritHandle = TRUE;\r
9740   saAttr.lpSecurityDescriptor = NULL;\r
9741 \r
9742   /*\r
9743    * The steps for redirecting child's STDOUT:\r
9744    *     1. Create anonymous pipe to be STDOUT for child.\r
9745    *     2. Create a noninheritable duplicate of read handle,\r
9746    *         and close the inheritable read handle.\r
9747    */\r
9748 \r
9749   /* Create a pipe for the child's STDOUT. */\r
9750   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9751     return GetLastError();\r
9752   }\r
9753 \r
9754   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9755   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9756                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9757                              FALSE,     /* not inherited */\r
9758                              DUPLICATE_SAME_ACCESS);\r
9759   if (! fSuccess) {\r
9760     return GetLastError();\r
9761   }\r
9762   CloseHandle(hChildStdoutRd);\r
9763 \r
9764   /*\r
9765    * The steps for redirecting child's STDIN:\r
9766    *     1. Create anonymous pipe to be STDIN for child.\r
9767    *     2. Create a noninheritable duplicate of write handle,\r
9768    *         and close the inheritable write handle.\r
9769    */\r
9770 \r
9771   /* Create a pipe for the child's STDIN. */\r
9772   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9773     return GetLastError();\r
9774   }\r
9775 \r
9776   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9777   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9778                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9779                              FALSE,     /* not inherited */\r
9780                              DUPLICATE_SAME_ACCESS);\r
9781   if (! fSuccess) {\r
9782     return GetLastError();\r
9783   }\r
9784   CloseHandle(hChildStdinWr);\r
9785 \r
9786   /* Arrange to (1) look in dir for the child .exe file, and\r
9787    * (2) have dir be the child's working directory.  Interpret\r
9788    * dir relative to the directory WinBoard loaded from. */\r
9789   GetCurrentDirectory(MSG_SIZ, buf);\r
9790   SetCurrentDirectory(installDir);\r
9791   SetCurrentDirectory(dir);\r
9792 \r
9793   /* Now create the child process. */\r
9794 \r
9795   siStartInfo.cb = sizeof(STARTUPINFO);\r
9796   siStartInfo.lpReserved = NULL;\r
9797   siStartInfo.lpDesktop = NULL;\r
9798   siStartInfo.lpTitle = NULL;\r
9799   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9800   siStartInfo.cbReserved2 = 0;\r
9801   siStartInfo.lpReserved2 = NULL;\r
9802   siStartInfo.hStdInput = hChildStdinRd;\r
9803   siStartInfo.hStdOutput = hChildStdoutWr;\r
9804   siStartInfo.hStdError = hChildStdoutWr;\r
9805 \r
9806   fSuccess = CreateProcess(NULL,\r
9807                            cmdLine,        /* command line */\r
9808                            NULL,           /* process security attributes */\r
9809                            NULL,           /* primary thread security attrs */\r
9810                            TRUE,           /* handles are inherited */\r
9811                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9812                            NULL,           /* use parent's environment */\r
9813                            NULL,\r
9814                            &siStartInfo, /* STARTUPINFO pointer */\r
9815                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9816 \r
9817   err = GetLastError();\r
9818   SetCurrentDirectory(buf); /* return to prev directory */\r
9819   if (! fSuccess) {\r
9820     return err;\r
9821   }\r
9822 \r
9823   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9824     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9825     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9826   }\r
9827 \r
9828   /* Close the handles we don't need in the parent */\r
9829   CloseHandle(piProcInfo.hThread);\r
9830   CloseHandle(hChildStdinRd);\r
9831   CloseHandle(hChildStdoutWr);\r
9832 \r
9833   /* Prepare return value */\r
9834   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9835   cp->kind = CPReal;\r
9836   cp->hProcess = piProcInfo.hProcess;\r
9837   cp->pid = piProcInfo.dwProcessId;\r
9838   cp->hFrom = hChildStdoutRdDup;\r
9839   cp->hTo = hChildStdinWrDup;\r
9840 \r
9841   *pr = (void *) cp;\r
9842 \r
9843   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9844      2000 where engines sometimes don't see the initial command(s)\r
9845      from WinBoard and hang.  I don't understand how that can happen,\r
9846      but the Sleep is harmless, so I've put it in.  Others have also\r
9847      reported what may be the same problem, so hopefully this will fix\r
9848      it for them too.  */\r
9849   Sleep(500);\r
9850 \r
9851   return NO_ERROR;\r
9852 }\r
9853 \r
9854 \r
9855 void\r
9856 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9857 {\r
9858   ChildProc *cp; int result;\r
9859 \r
9860   cp = (ChildProc *) pr;\r
9861   if (cp == NULL) return;\r
9862 \r
9863   switch (cp->kind) {\r
9864   case CPReal:\r
9865     /* TerminateProcess is considered harmful, so... */\r
9866     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9867     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9868     /* The following doesn't work because the chess program\r
9869        doesn't "have the same console" as WinBoard.  Maybe\r
9870        we could arrange for this even though neither WinBoard\r
9871        nor the chess program uses a console for stdio? */\r
9872     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9873 \r
9874     /* [AS] Special termination modes for misbehaving programs... */\r
9875     if( signal == 9 ) { \r
9876         result = TerminateProcess( cp->hProcess, 0 );\r
9877 \r
9878         if ( appData.debugMode) {\r
9879             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9880         }\r
9881     }\r
9882     else if( signal == 10 ) {\r
9883         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9884 \r
9885         if( dw != WAIT_OBJECT_0 ) {\r
9886             result = TerminateProcess( cp->hProcess, 0 );\r
9887 \r
9888             if ( appData.debugMode) {\r
9889                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9890             }\r
9891 \r
9892         }\r
9893     }\r
9894 \r
9895     CloseHandle(cp->hProcess);\r
9896     break;\r
9897 \r
9898   case CPComm:\r
9899     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9900     break;\r
9901 \r
9902   case CPSock:\r
9903     closesocket(cp->sock);\r
9904     WSACleanup();\r
9905     break;\r
9906 \r
9907   case CPRcmd:\r
9908     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9909     closesocket(cp->sock);\r
9910     closesocket(cp->sock2);\r
9911     WSACleanup();\r
9912     break;\r
9913   }\r
9914   free(cp);\r
9915 }\r
9916 \r
9917 void\r
9918 InterruptChildProcess(ProcRef pr)\r
9919 {\r
9920   ChildProc *cp;\r
9921 \r
9922   cp = (ChildProc *) pr;\r
9923   if (cp == NULL) return;\r
9924   switch (cp->kind) {\r
9925   case CPReal:\r
9926     /* The following doesn't work because the chess program\r
9927        doesn't "have the same console" as WinBoard.  Maybe\r
9928        we could arrange for this even though neither WinBoard\r
9929        nor the chess program uses a console for stdio */\r
9930     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9931     break;\r
9932 \r
9933   case CPComm:\r
9934   case CPSock:\r
9935     /* Can't interrupt */\r
9936     break;\r
9937 \r
9938   case CPRcmd:\r
9939     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9940     break;\r
9941   }\r
9942 }\r
9943 \r
9944 \r
9945 int\r
9946 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9947 {\r
9948   char cmdLine[MSG_SIZ];\r
9949 \r
9950   if (port[0] == NULLCHAR) {\r
9951     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9952   } else {\r
9953     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9954   }\r
9955   return StartChildProcess(cmdLine, "", pr);\r
9956 }\r
9957 \r
9958 \r
9959 /* Code to open TCP sockets */\r
9960 \r
9961 int\r
9962 OpenTCP(char *host, char *port, ProcRef *pr)\r
9963 {\r
9964   ChildProc *cp;\r
9965   int err;\r
9966   SOCKET s;\r
9967   struct sockaddr_in sa, mysa;\r
9968   struct hostent FAR *hp;\r
9969   unsigned short uport;\r
9970   WORD wVersionRequested;\r
9971   WSADATA wsaData;\r
9972 \r
9973   /* Initialize socket DLL */\r
9974   wVersionRequested = MAKEWORD(1, 1);\r
9975   err = WSAStartup(wVersionRequested, &wsaData);\r
9976   if (err != 0) return err;\r
9977 \r
9978   /* Make socket */\r
9979   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9980     err = WSAGetLastError();\r
9981     WSACleanup();\r
9982     return err;\r
9983   }\r
9984 \r
9985   /* Bind local address using (mostly) don't-care values.\r
9986    */\r
9987   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9988   mysa.sin_family = AF_INET;\r
9989   mysa.sin_addr.s_addr = INADDR_ANY;\r
9990   uport = (unsigned short) 0;\r
9991   mysa.sin_port = htons(uport);\r
9992   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9993       == SOCKET_ERROR) {\r
9994     err = WSAGetLastError();\r
9995     WSACleanup();\r
9996     return err;\r
9997   }\r
9998 \r
9999   /* Resolve remote host name */\r
10000   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10001   if (!(hp = gethostbyname(host))) {\r
10002     unsigned int b0, b1, b2, b3;\r
10003 \r
10004     err = WSAGetLastError();\r
10005 \r
10006     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10007       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10008       hp->h_addrtype = AF_INET;\r
10009       hp->h_length = 4;\r
10010       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10011       hp->h_addr_list[0] = (char *) malloc(4);\r
10012       hp->h_addr_list[0][0] = (char) b0;\r
10013       hp->h_addr_list[0][1] = (char) b1;\r
10014       hp->h_addr_list[0][2] = (char) b2;\r
10015       hp->h_addr_list[0][3] = (char) b3;\r
10016     } else {\r
10017       WSACleanup();\r
10018       return err;\r
10019     }\r
10020   }\r
10021   sa.sin_family = hp->h_addrtype;\r
10022   uport = (unsigned short) atoi(port);\r
10023   sa.sin_port = htons(uport);\r
10024   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10025 \r
10026   /* Make connection */\r
10027   if (connect(s, (struct sockaddr *) &sa,\r
10028               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10029     err = WSAGetLastError();\r
10030     WSACleanup();\r
10031     return err;\r
10032   }\r
10033 \r
10034   /* Prepare return value */\r
10035   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10036   cp->kind = CPSock;\r
10037   cp->sock = s;\r
10038   *pr = (ProcRef *) cp;\r
10039 \r
10040   return NO_ERROR;\r
10041 }\r
10042 \r
10043 int\r
10044 OpenCommPort(char *name, ProcRef *pr)\r
10045 {\r
10046   HANDLE h;\r
10047   COMMTIMEOUTS ct;\r
10048   ChildProc *cp;\r
10049   char fullname[MSG_SIZ];\r
10050 \r
10051   if (*name != '\\')\r
10052     sprintf(fullname, "\\\\.\\%s", name);\r
10053   else\r
10054     strcpy(fullname, name);\r
10055 \r
10056   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10057                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10058   if (h == (HANDLE) -1) {\r
10059     return GetLastError();\r
10060   }\r
10061   hCommPort = h;\r
10062 \r
10063   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10064 \r
10065   /* Accumulate characters until a 100ms pause, then parse */\r
10066   ct.ReadIntervalTimeout = 100;\r
10067   ct.ReadTotalTimeoutMultiplier = 0;\r
10068   ct.ReadTotalTimeoutConstant = 0;\r
10069   ct.WriteTotalTimeoutMultiplier = 0;\r
10070   ct.WriteTotalTimeoutConstant = 0;\r
10071   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10072 \r
10073   /* Prepare return value */\r
10074   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10075   cp->kind = CPComm;\r
10076   cp->hFrom = h;\r
10077   cp->hTo = h;\r
10078   *pr = (ProcRef *) cp;\r
10079 \r
10080   return NO_ERROR;\r
10081 }\r
10082 \r
10083 int\r
10084 OpenLoopback(ProcRef *pr)\r
10085 {\r
10086   DisplayFatalError("Not implemented", 0, 1);\r
10087   return NO_ERROR;\r
10088 }\r
10089 \r
10090 \r
10091 int\r
10092 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10093 {\r
10094   ChildProc *cp;\r
10095   int err;\r
10096   SOCKET s, s2, s3;\r
10097   struct sockaddr_in sa, mysa;\r
10098   struct hostent FAR *hp;\r
10099   unsigned short uport;\r
10100   WORD wVersionRequested;\r
10101   WSADATA wsaData;\r
10102   int fromPort;\r
10103   char stderrPortStr[MSG_SIZ];\r
10104 \r
10105   /* Initialize socket DLL */\r
10106   wVersionRequested = MAKEWORD(1, 1);\r
10107   err = WSAStartup(wVersionRequested, &wsaData);\r
10108   if (err != 0) return err;\r
10109 \r
10110   /* Resolve remote host name */\r
10111   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10112   if (!(hp = gethostbyname(host))) {\r
10113     unsigned int b0, b1, b2, b3;\r
10114 \r
10115     err = WSAGetLastError();\r
10116 \r
10117     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10118       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10119       hp->h_addrtype = AF_INET;\r
10120       hp->h_length = 4;\r
10121       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10122       hp->h_addr_list[0] = (char *) malloc(4);\r
10123       hp->h_addr_list[0][0] = (char) b0;\r
10124       hp->h_addr_list[0][1] = (char) b1;\r
10125       hp->h_addr_list[0][2] = (char) b2;\r
10126       hp->h_addr_list[0][3] = (char) b3;\r
10127     } else {\r
10128       WSACleanup();\r
10129       return err;\r
10130     }\r
10131   }\r
10132   sa.sin_family = hp->h_addrtype;\r
10133   uport = (unsigned short) 514;\r
10134   sa.sin_port = htons(uport);\r
10135   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10136 \r
10137   /* Bind local socket to unused "privileged" port address\r
10138    */\r
10139   s = INVALID_SOCKET;\r
10140   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10141   mysa.sin_family = AF_INET;\r
10142   mysa.sin_addr.s_addr = INADDR_ANY;\r
10143   for (fromPort = 1023;; fromPort--) {\r
10144     if (fromPort < 0) {\r
10145       WSACleanup();\r
10146       return WSAEADDRINUSE;\r
10147     }\r
10148     if (s == INVALID_SOCKET) {\r
10149       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10150         err = WSAGetLastError();\r
10151         WSACleanup();\r
10152         return err;\r
10153       }\r
10154     }\r
10155     uport = (unsigned short) fromPort;\r
10156     mysa.sin_port = htons(uport);\r
10157     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10158         == SOCKET_ERROR) {\r
10159       err = WSAGetLastError();\r
10160       if (err == WSAEADDRINUSE) continue;\r
10161       WSACleanup();\r
10162       return err;\r
10163     }\r
10164     if (connect(s, (struct sockaddr *) &sa,\r
10165       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10166       err = WSAGetLastError();\r
10167       if (err == WSAEADDRINUSE) {\r
10168         closesocket(s);\r
10169         s = -1;\r
10170         continue;\r
10171       }\r
10172       WSACleanup();\r
10173       return err;\r
10174     }\r
10175     break;\r
10176   }\r
10177 \r
10178   /* Bind stderr local socket to unused "privileged" port address\r
10179    */\r
10180   s2 = INVALID_SOCKET;\r
10181   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10182   mysa.sin_family = AF_INET;\r
10183   mysa.sin_addr.s_addr = INADDR_ANY;\r
10184   for (fromPort = 1023;; fromPort--) {\r
10185     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10186     if (fromPort < 0) {\r
10187       (void) closesocket(s);\r
10188       WSACleanup();\r
10189       return WSAEADDRINUSE;\r
10190     }\r
10191     if (s2 == INVALID_SOCKET) {\r
10192       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10193         err = WSAGetLastError();\r
10194         closesocket(s);\r
10195         WSACleanup();\r
10196         return err;\r
10197       }\r
10198     }\r
10199     uport = (unsigned short) fromPort;\r
10200     mysa.sin_port = htons(uport);\r
10201     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10202         == SOCKET_ERROR) {\r
10203       err = WSAGetLastError();\r
10204       if (err == WSAEADDRINUSE) continue;\r
10205       (void) closesocket(s);\r
10206       WSACleanup();\r
10207       return err;\r
10208     }\r
10209     if (listen(s2, 1) == SOCKET_ERROR) {\r
10210       err = WSAGetLastError();\r
10211       if (err == WSAEADDRINUSE) {\r
10212         closesocket(s2);\r
10213         s2 = INVALID_SOCKET;\r
10214         continue;\r
10215       }\r
10216       (void) closesocket(s);\r
10217       (void) closesocket(s2);\r
10218       WSACleanup();\r
10219       return err;\r
10220     }\r
10221     break;\r
10222   }\r
10223   prevStderrPort = fromPort; // remember port used\r
10224   sprintf(stderrPortStr, "%d", fromPort);\r
10225 \r
10226   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10227     err = WSAGetLastError();\r
10228     (void) closesocket(s);\r
10229     (void) closesocket(s2);\r
10230     WSACleanup();\r
10231     return err;\r
10232   }\r
10233 \r
10234   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10235     err = WSAGetLastError();\r
10236     (void) closesocket(s);\r
10237     (void) closesocket(s2);\r
10238     WSACleanup();\r
10239     return err;\r
10240   }\r
10241   if (*user == NULLCHAR) user = UserName();\r
10242   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10243     err = WSAGetLastError();\r
10244     (void) closesocket(s);\r
10245     (void) closesocket(s2);\r
10246     WSACleanup();\r
10247     return err;\r
10248   }\r
10249   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10250     err = WSAGetLastError();\r
10251     (void) closesocket(s);\r
10252     (void) closesocket(s2);\r
10253     WSACleanup();\r
10254     return err;\r
10255   }\r
10256 \r
10257   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10258     err = WSAGetLastError();\r
10259     (void) closesocket(s);\r
10260     (void) closesocket(s2);\r
10261     WSACleanup();\r
10262     return err;\r
10263   }\r
10264   (void) closesocket(s2);  /* Stop listening */\r
10265 \r
10266   /* Prepare return value */\r
10267   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10268   cp->kind = CPRcmd;\r
10269   cp->sock = s;\r
10270   cp->sock2 = s3;\r
10271   *pr = (ProcRef *) cp;\r
10272 \r
10273   return NO_ERROR;\r
10274 }\r
10275 \r
10276 \r
10277 InputSourceRef\r
10278 AddInputSource(ProcRef pr, int lineByLine,\r
10279                InputCallback func, VOIDSTAR closure)\r
10280 {\r
10281   InputSource *is, *is2 = NULL;\r
10282   ChildProc *cp = (ChildProc *) pr;\r
10283 \r
10284   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10285   is->lineByLine = lineByLine;\r
10286   is->func = func;\r
10287   is->closure = closure;\r
10288   is->second = NULL;\r
10289   is->next = is->buf;\r
10290   if (pr == NoProc) {\r
10291     is->kind = CPReal;\r
10292     consoleInputSource = is;\r
10293   } else {\r
10294     is->kind = cp->kind;\r
10295     /* \r
10296         [AS] Try to avoid a race condition if the thread is given control too early:\r
10297         we create all threads suspended so that the is->hThread variable can be\r
10298         safely assigned, then let the threads start with ResumeThread.\r
10299     */\r
10300     switch (cp->kind) {\r
10301     case CPReal:\r
10302       is->hFile = cp->hFrom;\r
10303       cp->hFrom = NULL; /* now owned by InputThread */\r
10304       is->hThread =\r
10305         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10306                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10307       break;\r
10308 \r
10309     case CPComm:\r
10310       is->hFile = cp->hFrom;\r
10311       cp->hFrom = NULL; /* now owned by InputThread */\r
10312       is->hThread =\r
10313         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10314                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10315       break;\r
10316 \r
10317     case CPSock:\r
10318       is->sock = cp->sock;\r
10319       is->hThread =\r
10320         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10321                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10322       break;\r
10323 \r
10324     case CPRcmd:\r
10325       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10326       *is2 = *is;\r
10327       is->sock = cp->sock;\r
10328       is->second = is2;\r
10329       is2->sock = cp->sock2;\r
10330       is2->second = is2;\r
10331       is->hThread =\r
10332         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10333                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10334       is2->hThread =\r
10335         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10336                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10337       break;\r
10338     }\r
10339 \r
10340     if( is->hThread != NULL ) {\r
10341         ResumeThread( is->hThread );\r
10342     }\r
10343 \r
10344     if( is2 != NULL && is2->hThread != NULL ) {\r
10345         ResumeThread( is2->hThread );\r
10346     }\r
10347   }\r
10348 \r
10349   return (InputSourceRef) is;\r
10350 }\r
10351 \r
10352 void\r
10353 RemoveInputSource(InputSourceRef isr)\r
10354 {\r
10355   InputSource *is;\r
10356 \r
10357   is = (InputSource *) isr;\r
10358   is->hThread = NULL;  /* tell thread to stop */\r
10359   CloseHandle(is->hThread);\r
10360   if (is->second != NULL) {\r
10361     is->second->hThread = NULL;\r
10362     CloseHandle(is->second->hThread);\r
10363   }\r
10364 }\r
10365 \r
10366 int no_wrap(char *message, int count)\r
10367 {\r
10368     ConsoleOutput(message, count, FALSE);\r
10369     return count;\r
10370 }\r
10371 \r
10372 int\r
10373 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10374 {\r
10375   DWORD dOutCount;\r
10376   int outCount = SOCKET_ERROR;\r
10377   ChildProc *cp = (ChildProc *) pr;\r
10378   static OVERLAPPED ovl;\r
10379   static int line = 0;\r
10380 \r
10381   if (pr == NoProc)\r
10382   {\r
10383     if (appData.noJoin || !appData.useInternalWrap)\r
10384       return no_wrap(message, count);\r
10385     else\r
10386     {\r
10387       int width = get_term_width();\r
10388       int len = wrap(NULL, message, count, width, &line);\r
10389       char *msg = malloc(len);\r
10390       int dbgchk;\r
10391 \r
10392       if (!msg)\r
10393         return no_wrap(message, count);\r
10394       else\r
10395       {\r
10396         dbgchk = wrap(msg, message, count, width, &line);\r
10397         if (dbgchk != len && appData.debugMode)\r
10398             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
10399         ConsoleOutput(msg, len, FALSE);\r
10400         free(msg);\r
10401         return len;\r
10402       }\r
10403     }\r
10404   }\r
10405 \r
10406   if (ovl.hEvent == NULL) {\r
10407     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10408   }\r
10409   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10410 \r
10411   switch (cp->kind) {\r
10412   case CPSock:\r
10413   case CPRcmd:\r
10414     outCount = send(cp->sock, message, count, 0);\r
10415     if (outCount == SOCKET_ERROR) {\r
10416       *outError = WSAGetLastError();\r
10417     } else {\r
10418       *outError = NO_ERROR;\r
10419     }\r
10420     break;\r
10421 \r
10422   case CPReal:\r
10423     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10424                   &dOutCount, NULL)) {\r
10425       *outError = NO_ERROR;\r
10426       outCount = (int) dOutCount;\r
10427     } else {\r
10428       *outError = GetLastError();\r
10429     }\r
10430     break;\r
10431 \r
10432   case CPComm:\r
10433     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10434                             &dOutCount, &ovl);\r
10435     if (*outError == NO_ERROR) {\r
10436       outCount = (int) dOutCount;\r
10437     }\r
10438     break;\r
10439   }\r
10440   return outCount;\r
10441 }\r
10442 \r
10443 int\r
10444 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10445                        long msdelay)\r
10446 {\r
10447   /* Ignore delay, not implemented for WinBoard */\r
10448   return OutputToProcess(pr, message, count, outError);\r
10449 }\r
10450 \r
10451 \r
10452 void\r
10453 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10454                         char *buf, int count, int error)\r
10455 {\r
10456   DisplayFatalError("Not implemented", 0, 1);\r
10457 }\r
10458 \r
10459 /* see wgamelist.c for Game List functions */\r
10460 /* see wedittags.c for Edit Tags functions */\r
10461 \r
10462 \r
10463 VOID\r
10464 ICSInitScript()\r
10465 {\r
10466   FILE *f;\r
10467   char buf[MSG_SIZ];\r
10468   char *dummy;\r
10469 \r
10470   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10471     f = fopen(buf, "r");\r
10472     if (f != NULL) {\r
10473       ProcessICSInitScript(f);\r
10474       fclose(f);\r
10475     }\r
10476   }\r
10477 }\r
10478 \r
10479 \r
10480 VOID\r
10481 StartAnalysisClock()\r
10482 {\r
10483   if (analysisTimerEvent) return;\r
10484   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10485                                         (UINT) 2000, NULL);\r
10486 }\r
10487 \r
10488 LRESULT CALLBACK\r
10489 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10490 {\r
10491   static HANDLE hwndText;\r
10492   RECT rect;\r
10493   static int sizeX, sizeY;\r
10494   int newSizeX, newSizeY, flags;\r
10495   MINMAXINFO *mmi;\r
10496 \r
10497   switch (message) {\r
10498   case WM_INITDIALOG: /* message: initialize dialog box */\r
10499     /* Initialize the dialog items */\r
10500     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10501     SetWindowText(hDlg, analysisTitle);\r
10502     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10503     /* Size and position the dialog */\r
10504     if (!analysisDialog) {\r
10505       analysisDialog = hDlg;\r
10506       flags = SWP_NOZORDER;\r
10507       GetClientRect(hDlg, &rect);\r
10508       sizeX = rect.right;\r
10509       sizeY = rect.bottom;\r
10510       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10511           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10512         WINDOWPLACEMENT wp;\r
10513         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10514         wp.length = sizeof(WINDOWPLACEMENT);\r
10515         wp.flags = 0;\r
10516         wp.showCmd = SW_SHOW;\r
10517         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10518         wp.rcNormalPosition.left = analysisX;\r
10519         wp.rcNormalPosition.right = analysisX + analysisW;\r
10520         wp.rcNormalPosition.top = analysisY;\r
10521         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10522         SetWindowPlacement(hDlg, &wp);\r
10523 \r
10524         GetClientRect(hDlg, &rect);\r
10525         newSizeX = rect.right;\r
10526         newSizeY = rect.bottom;\r
10527         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10528                               newSizeX, newSizeY);\r
10529         sizeX = newSizeX;\r
10530         sizeY = newSizeY;\r
10531       }\r
10532     }\r
10533     return FALSE;\r
10534 \r
10535   case WM_COMMAND: /* message: received a command */\r
10536     switch (LOWORD(wParam)) {\r
10537     case IDCANCEL:\r
10538       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10539           ExitAnalyzeMode();\r
10540           ModeHighlight();\r
10541           return TRUE;\r
10542       }\r
10543       EditGameEvent();\r
10544       return TRUE;\r
10545     default:\r
10546       break;\r
10547     }\r
10548     break;\r
10549 \r
10550   case WM_SIZE:\r
10551     newSizeX = LOWORD(lParam);\r
10552     newSizeY = HIWORD(lParam);\r
10553     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10554     sizeX = newSizeX;\r
10555     sizeY = newSizeY;\r
10556     break;\r
10557 \r
10558   case WM_GETMINMAXINFO:\r
10559     /* Prevent resizing window too small */\r
10560     mmi = (MINMAXINFO *) lParam;\r
10561     mmi->ptMinTrackSize.x = 100;\r
10562     mmi->ptMinTrackSize.y = 100;\r
10563     break;\r
10564   }\r
10565   return FALSE;\r
10566 }\r
10567 \r
10568 VOID\r
10569 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10570 {\r
10571   highlightInfo.sq[0].x = fromX;\r
10572   highlightInfo.sq[0].y = fromY;\r
10573   highlightInfo.sq[1].x = toX;\r
10574   highlightInfo.sq[1].y = toY;\r
10575 }\r
10576 \r
10577 VOID\r
10578 ClearHighlights()\r
10579 {\r
10580   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10581     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10582 }\r
10583 \r
10584 VOID\r
10585 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10586 {\r
10587   premoveHighlightInfo.sq[0].x = fromX;\r
10588   premoveHighlightInfo.sq[0].y = fromY;\r
10589   premoveHighlightInfo.sq[1].x = toX;\r
10590   premoveHighlightInfo.sq[1].y = toY;\r
10591 }\r
10592 \r
10593 VOID\r
10594 ClearPremoveHighlights()\r
10595 {\r
10596   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10597     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10598 }\r
10599 \r
10600 VOID\r
10601 ShutDownFrontEnd()\r
10602 {\r
10603   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10604   DeleteClipboardTempFiles();\r
10605 }\r
10606 \r
10607 void\r
10608 BoardToTop()\r
10609 {\r
10610     if (IsIconic(hwndMain))\r
10611       ShowWindow(hwndMain, SW_RESTORE);\r
10612 \r
10613     SetActiveWindow(hwndMain);\r
10614 }\r
10615 \r
10616 /*\r
10617  * Prototypes for animation support routines\r
10618  */\r
10619 static void ScreenSquare(int column, int row, POINT * pt);\r
10620 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10621      POINT frames[], int * nFrames);\r
10622 \r
10623 \r
10624 void\r
10625 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10626 {       // [HGM] atomic: animate blast wave\r
10627         int i;\r
10628 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10629         explodeInfo.fromX = fromX;\r
10630         explodeInfo.fromY = fromY;\r
10631         explodeInfo.toX = toX;\r
10632         explodeInfo.toY = toY;\r
10633         for(i=1; i<nFrames; i++) {\r
10634             explodeInfo.radius = (i*180)/(nFrames-1);\r
10635             DrawPosition(FALSE, NULL);\r
10636             Sleep(appData.animSpeed);\r
10637         }\r
10638         explodeInfo.radius = 0;\r
10639         DrawPosition(TRUE, NULL);\r
10640 }\r
10641 \r
10642 #define kFactor 4\r
10643 \r
10644 void\r
10645 AnimateMove(board, fromX, fromY, toX, toY)\r
10646      Board board;\r
10647      int fromX;\r
10648      int fromY;\r
10649      int toX;\r
10650      int toY;\r
10651 {\r
10652   ChessSquare piece;\r
10653   POINT start, finish, mid;\r
10654   POINT frames[kFactor * 2 + 1];\r
10655   int nFrames, n;\r
10656 \r
10657   if (!appData.animate) return;\r
10658   if (doingSizing) return;\r
10659   if (fromY < 0 || fromX < 0) return;\r
10660   piece = board[fromY][fromX];\r
10661   if (piece >= EmptySquare) return;\r
10662 \r
10663   ScreenSquare(fromX, fromY, &start);\r
10664   ScreenSquare(toX, toY, &finish);\r
10665 \r
10666   /* All pieces except knights move in straight line */\r
10667   if (piece != WhiteKnight && piece != BlackKnight) {\r
10668     mid.x = start.x + (finish.x - start.x) / 2;\r
10669     mid.y = start.y + (finish.y - start.y) / 2;\r
10670   } else {\r
10671     /* Knight: make diagonal movement then straight */\r
10672     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10673        mid.x = start.x + (finish.x - start.x) / 2;\r
10674        mid.y = finish.y;\r
10675      } else {\r
10676        mid.x = finish.x;\r
10677        mid.y = start.y + (finish.y - start.y) / 2;\r
10678      }\r
10679   }\r
10680   \r
10681   /* Don't use as many frames for very short moves */\r
10682   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10683     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10684   else\r
10685     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10686 \r
10687   animInfo.from.x = fromX;\r
10688   animInfo.from.y = fromY;\r
10689   animInfo.to.x = toX;\r
10690   animInfo.to.y = toY;\r
10691   animInfo.lastpos = start;\r
10692   animInfo.piece = piece;\r
10693   for (n = 0; n < nFrames; n++) {\r
10694     animInfo.pos = frames[n];\r
10695     DrawPosition(FALSE, NULL);\r
10696     animInfo.lastpos = animInfo.pos;\r
10697     Sleep(appData.animSpeed);\r
10698   }\r
10699   animInfo.pos = finish;\r
10700   DrawPosition(FALSE, NULL);\r
10701   animInfo.piece = EmptySquare;\r
10702   if(gameInfo.variant == VariantAtomic && \r
10703      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10704         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10705 }\r
10706 \r
10707 /*      Convert board position to corner of screen rect and color       */\r
10708 \r
10709 static void\r
10710 ScreenSquare(column, row, pt)\r
10711      int column; int row; POINT * pt;\r
10712 {\r
10713   if (flipView) {\r
10714     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10715     pt->y = lineGap + row * (squareSize + lineGap);\r
10716   } else {\r
10717     pt->x = lineGap + column * (squareSize + lineGap);\r
10718     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10719   }\r
10720 }\r
10721 \r
10722 /*      Generate a series of frame coords from start->mid->finish.\r
10723         The movement rate doubles until the half way point is\r
10724         reached, then halves back down to the final destination,\r
10725         which gives a nice slow in/out effect. The algorithmn\r
10726         may seem to generate too many intermediates for short\r
10727         moves, but remember that the purpose is to attract the\r
10728         viewers attention to the piece about to be moved and\r
10729         then to where it ends up. Too few frames would be less\r
10730         noticeable.                                             */\r
10731 \r
10732 static void\r
10733 Tween(start, mid, finish, factor, frames, nFrames)\r
10734      POINT * start; POINT * mid;\r
10735      POINT * finish; int factor;\r
10736      POINT frames[]; int * nFrames;\r
10737 {\r
10738   int n, fraction = 1, count = 0;\r
10739 \r
10740   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10741   for (n = 0; n < factor; n++)\r
10742     fraction *= 2;\r
10743   for (n = 0; n < factor; n++) {\r
10744     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10745     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10746     count ++;\r
10747     fraction = fraction / 2;\r
10748   }\r
10749   \r
10750   /* Midpoint */\r
10751   frames[count] = *mid;\r
10752   count ++;\r
10753   \r
10754   /* Slow out, stepping 1/2, then 1/4, ... */\r
10755   fraction = 2;\r
10756   for (n = 0; n < factor; n++) {\r
10757     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10758     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10759     count ++;\r
10760     fraction = fraction * 2;\r
10761   }\r
10762   *nFrames = count;\r
10763 }\r
10764 \r
10765 void\r
10766 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10767 {\r
10768     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10769 \r
10770     EvalGraphSet( first, last, current, pvInfoList );\r
10771 }\r
10772 \r
10773 void SetProgramStats( FrontEndProgramStats * stats )\r
10774 {\r
10775     EngineOutputUpdate( stats );\r
10776 }\r