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