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