Added internal wrapping ability.
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "winboard.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "wgamelist.h"\r
89 #include "wedittags.h"\r
90 #include "woptions.h"\r
91 #include "wsockerr.h"\r
92 #include "defaults.h"\r
93 #include "help.h"\r
94 #include "wsnap.h"\r
95 \r
96 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
97 \r
98   int myrandom(void);\r
99   void mysrandom(unsigned int seed);\r
100 \r
101 extern int whiteFlag, blackFlag;\r
102 Boolean flipClock = FALSE;\r
103 extern HANDLE chatHandle[];\r
104 extern int ics_type;\r
105 \r
106 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
107 VOID NewVariantPopup(HWND hwnd);\r
108 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
109                    /*char*/int promoChar));\r
110 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
111 void DisplayMove P((int moveNumber));\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P(());\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129 } DragInfo;\r
130 \r
131 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
132 \r
133 typedef struct {\r
134   POINT sq[2];    /* board coordinates of from, to squares */\r
135 } HighlightInfo;\r
136 \r
137 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 \r
140 typedef struct { // [HGM] atomic\r
141   int fromX, fromY, toX, toY, radius;\r
142 } ExplodeInfo;\r
143 \r
144 static ExplodeInfo explodeInfo;\r
145 \r
146 /* Window class names */\r
147 char szAppName[] = "WinBoard";\r
148 char szConsoleName[] = "WBConsole";\r
149 \r
150 /* Title bar text */\r
151 char szTitle[] = "WinBoard";\r
152 char szConsoleTitle[] = "I C S Interaction";\r
153 \r
154 char *programName;\r
155 char *settingsFileName;\r
156 BOOLEAN saveSettingsOnExit;\r
157 char installDir[MSG_SIZ];\r
158 \r
159 BoardSize boardSize;\r
160 BOOLEAN chessProgram;\r
161 static int boardX, boardY;\r
162 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
163 static int squareSize, lineGap, minorSize;\r
164 static int winWidth, winHeight, winW, winH;\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
166 static int logoHeight = 0;\r
167 static char messageText[MESSAGE_TEXT_MAX];\r
168 static int clockTimerEvent = 0;\r
169 static int loadGameTimerEvent = 0;\r
170 static int analysisTimerEvent = 0;\r
171 static DelayedEventCallback delayedTimerCallback;\r
172 static int delayedTimerEvent = 0;\r
173 static int buttonCount = 2;\r
174 char *icsTextMenuString;\r
175 char *icsNames;\r
176 char *firstChessProgramNames;\r
177 char *secondChessProgramNames;\r
178 \r
179 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
180 \r
181 #define PALETTESIZE 256\r
182 \r
183 HINSTANCE hInst;          /* current instance */\r
184 HWND hwndMain = NULL;        /* root window*/\r
185 HWND hwndConsole = NULL;\r
186 BOOLEAN alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 HWND hCommPort = NULL;    /* currently open comm port */\r
194 static HWND hwndPause;    /* pause button */\r
195 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
196 static HBRUSH lightSquareBrush, darkSquareBrush,\r
197   blackSquareBrush, /* [HGM] for band between board and holdings */\r
198   explodeBrush,     /* [HGM] atomic */\r
199   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
200 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
201 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
202 static HPEN gridPen = NULL;\r
203 static HPEN highlightPen = NULL;\r
204 static HPEN premovePen = NULL;\r
205 static NPLOGPALETTE pLogPal;\r
206 static BOOL paletteChanged = FALSE;\r
207 static HICON iconWhite, iconBlack, iconCurrent;\r
208 static int doingSizing = FALSE;\r
209 static int lastSizing = 0;\r
210 static int prevStderrPort;\r
211 static HBITMAP userLogo;\r
212 \r
213 /* [AS] Support for background textures */\r
214 #define BACK_TEXTURE_MODE_DISABLED      0\r
215 #define BACK_TEXTURE_MODE_PLAIN         1\r
216 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 #if defined(_winmajor)\r
229 #define oldDialog (_winmajor < 4)\r
230 #else\r
231 #define oldDialog 0\r
232 #endif\r
233 #endif\r
234 \r
235 char *defaultTextAttribs[] = \r
236 {\r
237   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
238   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
239   COLOR_NONE\r
240 };\r
241 \r
242 typedef struct {\r
243   char *name;\r
244   int squareSize;\r
245   int lineGap;\r
246   int smallLayout;\r
247   int tinyLayout;\r
248   int cliWidth, cliHeight;\r
249 } SizeInfo;\r
250 \r
251 SizeInfo sizeInfo[] = \r
252 {\r
253   { "tiny",     21, 0, 1, 1, 0, 0 },\r
254   { "teeny",    25, 1, 1, 1, 0, 0 },\r
255   { "dinky",    29, 1, 1, 1, 0, 0 },\r
256   { "petite",   33, 1, 1, 1, 0, 0 },\r
257   { "slim",     37, 2, 1, 0, 0, 0 },\r
258   { "small",    40, 2, 1, 0, 0, 0 },\r
259   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
260   { "middling", 49, 2, 0, 0, 0, 0 },\r
261   { "average",  54, 2, 0, 0, 0, 0 },\r
262   { "moderate", 58, 3, 0, 0, 0, 0 },\r
263   { "medium",   64, 3, 0, 0, 0, 0 },\r
264   { "bulky",    72, 3, 0, 0, 0, 0 },\r
265   { "large",    80, 3, 0, 0, 0, 0 },\r
266   { "big",      87, 3, 0, 0, 0, 0 },\r
267   { "huge",     95, 3, 0, 0, 0, 0 },\r
268   { "giant",    108, 3, 0, 0, 0, 0 },\r
269   { "colossal", 116, 4, 0, 0, 0, 0 },\r
270   { "titanic",  129, 4, 0, 0, 0, 0 },\r
271   { NULL, 0, 0, 0, 0, 0, 0 }\r
272 };\r
273 \r
274 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
275 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
276 {\r
277   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
278   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
279   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
280   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
281   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
282   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
283   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
284   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
285   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
286   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
287   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
288   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
289   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
290   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
291   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
292   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
293   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
294   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
295 };\r
296 \r
297 MyFont *font[NUM_SIZES][NUM_FONTS];\r
298 \r
299 typedef struct {\r
300   char *label;\r
301   int id;\r
302   HWND hwnd;\r
303   WNDPROC wndproc;\r
304 } MyButtonDesc;\r
305 \r
306 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
307 #define N_BUTTONS 5\r
308 \r
309 MyButtonDesc buttonDesc[N_BUTTONS] =\r
310 {\r
311   {"<<", IDM_ToStart, NULL, NULL},\r
312   {"<", IDM_Backward, NULL, NULL},\r
313   {"P", IDM_Pause, NULL, NULL},\r
314   {">", IDM_Forward, NULL, NULL},\r
315   {">>", IDM_ToEnd, NULL, NULL},\r
316 };\r
317 \r
318 int tinyLayout = 0, smallLayout = 0;\r
319 #define MENU_BAR_ITEMS 7\r
320 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
321   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
322   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
323 };\r
324 \r
325 \r
326 MySound sounds[(int)NSoundClasses];\r
327 MyTextAttribs textAttribs[(int)NColorClasses];\r
328 \r
329 MyColorizeAttribs colorizeAttribs[] = {\r
330   { (COLORREF)0, 0, "Shout Text" },\r
331   { (COLORREF)0, 0, "SShout/CShout" },\r
332   { (COLORREF)0, 0, "Channel 1 Text" },\r
333   { (COLORREF)0, 0, "Channel Text" },\r
334   { (COLORREF)0, 0, "Kibitz Text" },\r
335   { (COLORREF)0, 0, "Tell Text" },\r
336   { (COLORREF)0, 0, "Challenge Text" },\r
337   { (COLORREF)0, 0, "Request Text" },\r
338   { (COLORREF)0, 0, "Seek Text" },\r
339   { (COLORREF)0, 0, "Normal Text" },\r
340   { (COLORREF)0, 0, "None" }\r
341 };\r
342 \r
343 \r
344 \r
345 static char *commentTitle;\r
346 static char *commentText;\r
347 static int commentIndex;\r
348 static Boolean editComment = FALSE;\r
349 HWND commentDialog = NULL;\r
350 BOOLEAN commentDialogUp = FALSE;\r
351 static int commentX, commentY, commentH, commentW;\r
352 \r
353 static char *analysisTitle;\r
354 static char *analysisText;\r
355 HWND analysisDialog = NULL;\r
356 BOOLEAN analysisDialogUp = FALSE;\r
357 static int analysisX, analysisY, analysisH, analysisW;\r
358 \r
359 char errorTitle[MSG_SIZ];\r
360 char errorMessage[2*MSG_SIZ];\r
361 HWND errorDialog = NULL;\r
362 BOOLEAN moveErrorMessageUp = FALSE;\r
363 BOOLEAN consoleEcho = TRUE;\r
364 CHARFORMAT consoleCF;\r
365 COLORREF consoleBackgroundColor;\r
366 \r
367 char *programVersion;\r
368 \r
369 #define CPReal 1\r
370 #define CPComm 2\r
371 #define CPSock 3\r
372 #define CPRcmd 4\r
373 typedef int CPKind;\r
374 \r
375 typedef struct {\r
376   CPKind kind;\r
377   HANDLE hProcess;\r
378   DWORD pid;\r
379   HANDLE hTo;\r
380   HANDLE hFrom;\r
381   SOCKET sock;\r
382   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
383 } ChildProc;\r
384 \r
385 #define INPUT_SOURCE_BUF_SIZE 4096\r
386 \r
387 typedef struct _InputSource {\r
388   CPKind kind;\r
389   HANDLE hFile;\r
390   SOCKET sock;\r
391   int lineByLine;\r
392   HANDLE hThread;\r
393   DWORD id;\r
394   char buf[INPUT_SOURCE_BUF_SIZE];\r
395   char *next;\r
396   DWORD count;\r
397   int error;\r
398   InputCallback func;\r
399   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
400   VOIDSTAR closure;\r
401 } InputSource;\r
402 \r
403 InputSource *consoleInputSource;\r
404 \r
405 DCB dcb;\r
406 \r
407 /* forward */\r
408 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
409 VOID ConsoleCreate();\r
410 LRESULT CALLBACK\r
411   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
412 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
413 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
414 VOID ParseCommSettings(char *arg, DCB *dcb);\r
415 LRESULT CALLBACK\r
416   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
417 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
418 void ParseIcsTextMenu(char *icsTextMenuString);\r
419 VOID PopUpMoveDialog(char firstchar);\r
420 VOID PopUpNameDialog(char firstchar);\r
421 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
422 \r
423 /* [AS] */\r
424 int NewGameFRC();\r
425 int GameListOptions();\r
426 \r
427 HWND moveHistoryDialog = NULL;\r
428 BOOLEAN moveHistoryDialogUp = FALSE;\r
429 \r
430 WindowPlacement wpMoveHistory;\r
431 \r
432 HWND evalGraphDialog = NULL;\r
433 BOOLEAN evalGraphDialogUp = FALSE;\r
434 \r
435 WindowPlacement wpEvalGraph;\r
436 \r
437 HWND engineOutputDialog = NULL;\r
438 BOOLEAN engineOutputDialogUp = FALSE;\r
439 \r
440 WindowPlacement wpEngineOutput;\r
441 WindowPlacement wpGameList;\r
442 WindowPlacement wpConsole;\r
443 \r
444 VOID MoveHistoryPopUp();\r
445 VOID MoveHistoryPopDown();\r
446 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
447 BOOL MoveHistoryIsUp();\r
448 \r
449 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
450 VOID EvalGraphPopUp();\r
451 VOID EvalGraphPopDown();\r
452 BOOL EvalGraphIsUp();\r
453 \r
454 VOID EngineOutputPopUp();\r
455 VOID EngineOutputPopDown();\r
456 BOOL EngineOutputIsUp();\r
457 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
458 \r
459 VOID EngineOptionsPopup(); // [HGM] settings\r
460 \r
461 VOID GothicPopUp(char *title, VariantClass variant);\r
462 /*\r
463  * Setting "frozen" should disable all user input other than deleting\r
464  * the window.  We do this while engines are initializing themselves.\r
465  */\r
466 static int frozen = 0;\r
467 static int oldMenuItemState[MENU_BAR_ITEMS];\r
468 void FreezeUI()\r
469 {\r
470   HMENU hmenu;\r
471   int i;\r
472 \r
473   if (frozen) return;\r
474   frozen = 1;\r
475   hmenu = GetMenu(hwndMain);\r
476   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
477     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
478   }\r
479   DrawMenuBar(hwndMain);\r
480 }\r
481 \r
482 /* Undo a FreezeUI */\r
483 void ThawUI()\r
484 {\r
485   HMENU hmenu;\r
486   int i;\r
487 \r
488   if (!frozen) return;\r
489   frozen = 0;\r
490   hmenu = GetMenu(hwndMain);\r
491   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
492     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
493   }\r
494   DrawMenuBar(hwndMain);\r
495 }\r
496 \r
497 /*static*/ int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
498 \r
499 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
500 #ifdef JAWS\r
501 #include "jaws.c"\r
502 #else\r
503 #define JAWS_INIT\r
504 #define JAWS_ARGS\r
505 #define JAWS_ALT_INTERCEPT\r
506 #define JAWS_KB_NAVIGATION\r
507 #define JAWS_MENU_ITEMS\r
508 #define JAWS_SILENCE\r
509 #define JAWS_REPLAY\r
510 #define JAWS_ACCEL\r
511 #define JAWS_COPYRIGHT\r
512 #define JAWS_DELETE(X) X\r
513 #define SAYMACHINEMOVE()\r
514 #define SAY(X)\r
515 #endif\r
516 \r
517 /*---------------------------------------------------------------------------*\\r
518  *\r
519  * WinMain\r
520  *\r
521 \*---------------------------------------------------------------------------*/\r
522 \r
523 int APIENTRY\r
524 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
525         LPSTR lpCmdLine, int nCmdShow)\r
526 {\r
527   MSG msg;\r
528   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
529 //  INITCOMMONCONTROLSEX ex;\r
530 \r
531   debugFP = stderr;\r
532 \r
533   LoadLibrary("RICHED32.DLL");\r
534   consoleCF.cbSize = sizeof(CHARFORMAT);\r
535 \r
536   if (!InitApplication(hInstance)) {\r
537     return (FALSE);\r
538   }\r
539   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
540     return (FALSE);\r
541   }\r
542 \r
543   JAWS_INIT\r
544 \r
545 //  InitCommonControlsEx(&ex);\r
546   InitCommonControls();\r
547 \r
548   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
549   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
550   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
551 \r
552   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
553 \r
554   while (GetMessage(&msg, /* message structure */\r
555                     NULL, /* handle of window receiving the message */\r
556                     0,    /* lowest message to examine */\r
557                     0))   /* highest message to examine */\r
558     {\r
559 \r
560       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
561         // [HGM] navigate: switch between all windows with tab\r
562         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
563         int i, currentElement = 0;\r
564 \r
565         // first determine what element of the chain we come from (if any)\r
566         if(appData.icsActive) {\r
567             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
568             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
569         }\r
570         if(engineOutputDialog && EngineOutputIsUp()) {\r
571             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
572             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
573         }\r
574         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
575             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
576         }\r
577         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
578         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
579         if(msg.hwnd == e1)                 currentElement = 2; else\r
580         if(msg.hwnd == e2)                 currentElement = 3; else\r
581         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
582         if(msg.hwnd == mh)                currentElement = 4; else\r
583         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
584         if(msg.hwnd == hText)  currentElement = 5; else\r
585         if(msg.hwnd == hInput) currentElement = 6; else\r
586         for (i = 0; i < N_BUTTONS; i++) {\r
587             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
588         }\r
589 \r
590         // determine where to go to\r
591         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
592           do {\r
593             currentElement = (currentElement + direction) % 7;\r
594             switch(currentElement) {\r
595                 case 0:\r
596                   h = hwndMain; break; // passing this case always makes the loop exit\r
597                 case 1:\r
598                   h = buttonDesc[0].hwnd; break; // could be NULL\r
599                 case 2:\r
600                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
601                   h = e1; break;\r
602                 case 3:\r
603                   if(!EngineOutputIsUp()) continue;\r
604                   h = e2; break;\r
605                 case 4:\r
606                   if(!MoveHistoryIsUp()) continue;\r
607                   h = mh; break;\r
608 //              case 6: // input to eval graph does not seem to get here!\r
609 //                if(!EvalGraphIsUp()) continue;\r
610 //                h = evalGraphDialog; break;\r
611                 case 5:\r
612                   if(!appData.icsActive) continue;\r
613                   SAY("display");\r
614                   h = hText; break;\r
615                 case 6:\r
616                   if(!appData.icsActive) continue;\r
617                   SAY("input");\r
618                   h = hInput; break;\r
619             }\r
620           } while(h == 0);\r
621 \r
622           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
623           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
624           SetFocus(h);\r
625 \r
626           continue; // this message now has been processed\r
627         }\r
628       }\r
629 \r
630       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
631           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
632           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
633           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
634           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
635           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
636           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
637           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
638           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
639           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
640         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
641         for(i=0; i<MAX_CHAT; i++) \r
642             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
643                 done = 1; break;\r
644         }\r
645         if(done) continue; // [HGM] chat: end patch\r
646         TranslateMessage(&msg); /* Translates virtual key codes */\r
647         DispatchMessage(&msg);  /* Dispatches message to window */\r
648       }\r
649     }\r
650 \r
651 \r
652   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
653 }\r
654 \r
655 /*---------------------------------------------------------------------------*\\r
656  *\r
657  * Initialization functions\r
658  *\r
659 \*---------------------------------------------------------------------------*/\r
660 \r
661 void\r
662 SetUserLogo()\r
663 {   // update user logo if necessary\r
664     static char oldUserName[MSG_SIZ], *curName;\r
665 \r
666     if(appData.autoLogo) {\r
667           curName = UserName();\r
668           if(strcmp(curName, oldUserName)) {\r
669                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
670                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
671                 strcpy(oldUserName, curName);\r
672           }\r
673     }\r
674 }\r
675 \r
676 BOOL\r
677 InitApplication(HINSTANCE hInstance)\r
678 {\r
679   WNDCLASS wc;\r
680 \r
681   /* Fill in window class structure with parameters that describe the */\r
682   /* main window. */\r
683 \r
684   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
685   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
686   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
687   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
688   wc.hInstance     = hInstance;         /* Owner of this class */\r
689   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
690   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
691   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
692   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
693   wc.lpszClassName = szAppName;                 /* Name to register as */\r
694 \r
695   /* Register the window class and return success/failure code. */\r
696   if (!RegisterClass(&wc)) return FALSE;\r
697 \r
698   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
699   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
700   wc.cbClsExtra    = 0;\r
701   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
702   wc.hInstance     = hInstance;\r
703   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
704   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
705   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
706   wc.lpszMenuName  = NULL;\r
707   wc.lpszClassName = szConsoleName;\r
708 \r
709   if (!RegisterClass(&wc)) return FALSE;\r
710   return TRUE;\r
711 }\r
712 \r
713 \r
714 /* Set by InitInstance, used by EnsureOnScreen */\r
715 int screenHeight, screenWidth;\r
716 \r
717 void\r
718 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
719 {\r
720 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
721   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
722   if (*x > screenWidth - 32) *x = 0;\r
723   if (*y > screenHeight - 32) *y = 0;\r
724   if (*x < minX) *x = minX;\r
725   if (*y < minY) *y = minY;\r
726 }\r
727 \r
728 BOOL\r
729 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
730 {\r
731   HWND hwnd; /* Main window handle. */\r
732   int ibs;\r
733   WINDOWPLACEMENT wp;\r
734   char *filepart;\r
735 \r
736   hInst = hInstance;    /* Store instance handle in our global variable */\r
737 \r
738   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
739     *filepart = NULLCHAR;\r
740   } else {\r
741     GetCurrentDirectory(MSG_SIZ, installDir);\r
742   }\r
743   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
744   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
745   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
746   if (appData.debugMode) {\r
747     debugFP = fopen(appData.nameOfDebugFile, "w");\r
748     setbuf(debugFP, NULL);\r
749   }\r
750 \r
751   InitBackEnd1();\r
752 \r
753 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
754 //  InitEngineUCI( installDir, &second );\r
755 \r
756   /* Create a main window for this application instance. */\r
757   hwnd = CreateWindow(szAppName, szTitle,\r
758                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
759                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
760                       NULL, NULL, hInstance, NULL);\r
761   hwndMain = hwnd;\r
762 \r
763   /* If window could not be created, return "failure" */\r
764   if (!hwnd) {\r
765     return (FALSE);\r
766   }\r
767 \r
768   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
769   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
770       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
771 \r
772       if (first.programLogo == NULL && appData.debugMode) {\r
773           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
774       }\r
775   } else if(appData.autoLogo) {\r
776       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
777         char buf[MSG_SIZ];\r
778         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
779         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
780       }\r
781   }\r
782 \r
783   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
784       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
785 \r
786       if (second.programLogo == NULL && appData.debugMode) {\r
787           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
788       }\r
789   } else if(appData.autoLogo) {\r
790       char buf[MSG_SIZ];\r
791       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
792         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
793         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
794       } else\r
795       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
796         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
797         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
798       }\r
799   }\r
800 \r
801   SetUserLogo();\r
802 \r
803   iconWhite = LoadIcon(hInstance, "icon_white");\r
804   iconBlack = LoadIcon(hInstance, "icon_black");\r
805   iconCurrent = iconWhite;\r
806   InitDrawingColors();\r
807   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
808   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
809   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
810     /* Compute window size for each board size, and use the largest\r
811        size that fits on this screen as the default. */\r
812     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
813     if (boardSize == (BoardSize)-1 &&\r
814         winH <= screenHeight\r
815            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
816         && winW <= screenWidth) {\r
817       boardSize = (BoardSize)ibs;\r
818     }\r
819   }\r
820 \r
821   InitDrawingSizes(boardSize, 0);\r
822   InitMenuChecks();\r
823   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
824 \r
825   /* [AS] Load textures if specified */\r
826   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
827   \r
828   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
829       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
830       liteBackTextureMode = appData.liteBackTextureMode;\r
831 \r
832       if (liteBackTexture == NULL && appData.debugMode) {\r
833           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
834       }\r
835   }\r
836   \r
837   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
838       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
839       darkBackTextureMode = appData.darkBackTextureMode;\r
840 \r
841       if (darkBackTexture == NULL && appData.debugMode) {\r
842           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
843       }\r
844   }\r
845 \r
846   mysrandom( (unsigned) time(NULL) );\r
847 \r
848   /* [AS] Restore layout */\r
849   if( wpMoveHistory.visible ) {\r
850       MoveHistoryPopUp();\r
851   }\r
852 \r
853   if( wpEvalGraph.visible ) {\r
854       EvalGraphPopUp();\r
855   }\r
856 \r
857   if( wpEngineOutput.visible ) {\r
858       EngineOutputPopUp();\r
859   }\r
860 \r
861   InitBackEnd2();\r
862 \r
863   /* Make the window visible; update its client area; and return "success" */\r
864   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
865   wp.length = sizeof(WINDOWPLACEMENT);\r
866   wp.flags = 0;\r
867   wp.showCmd = nCmdShow;\r
868   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
869   wp.rcNormalPosition.left = boardX;\r
870   wp.rcNormalPosition.right = boardX + winWidth;\r
871   wp.rcNormalPosition.top = boardY;\r
872   wp.rcNormalPosition.bottom = boardY + winHeight;\r
873   SetWindowPlacement(hwndMain, &wp);\r
874 \r
875   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
876                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
877 \r
878   if (hwndConsole) {\r
879 #if AOT_CONSOLE\r
880     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
881                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
882 #endif\r
883     ShowWindow(hwndConsole, nCmdShow);\r
884   }\r
885   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
886   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
887 \r
888   return TRUE;\r
889 \r
890 }\r
891 \r
892 \r
893 typedef enum {\r
894   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
895   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
896   ArgSettingsFilename,\r
897   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
898 } ArgType;\r
899 \r
900 typedef struct {\r
901   char *argName;\r
902   ArgType argType;\r
903   /***\r
904   union {\r
905     String *pString;       // ArgString\r
906     int *pInt;             // ArgInt\r
907     float *pFloat;         // ArgFloat\r
908     Boolean *pBoolean;     // ArgBoolean\r
909     COLORREF *pColor;      // ArgColor\r
910     ColorClass cc;         // ArgAttribs\r
911     String *pFilename;     // ArgFilename\r
912     BoardSize *pBoardSize; // ArgBoardSize\r
913     int whichFont;         // ArgFont\r
914     DCB *pDCB;             // ArgCommSettings\r
915     String *pFilename;     // ArgSettingsFilename\r
916   } argLoc;\r
917   ***/\r
918   LPVOID argLoc;\r
919   BOOL save;\r
920 } ArgDescriptor;\r
921 \r
922 int junk;\r
923 ArgDescriptor argDescriptors[] = {\r
924   /* positional arguments */\r
925   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
926   { "", ArgNone, NULL },\r
927   /* keyword arguments */\r
928   JAWS_ARGS\r
929   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
930   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
931   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
932   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
933   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
934   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
935   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
936   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
937   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
938   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
939   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
940   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
941   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
942   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
943   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
944   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
945   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
946   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
947     FALSE },\r
948   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
949     FALSE },\r
950   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
951     FALSE },\r
952   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
953   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
954     FALSE },\r
955   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
956   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
957   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
958   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
959   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
960   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
961   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
962   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
963   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
964   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
965   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
966   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
967   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
968   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
969   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
970   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
971   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
972   /*!!bitmapDirectory?*/\r
973   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
974   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
975   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
976   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
977   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
978   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
979   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
980   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
981   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
982   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
983   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
984   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
985   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
986   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
987   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
988   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
989   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
990   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
991   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
992   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
993   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
994   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
995   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
996   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
997   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
998   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
999   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
1000   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
1001   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
1002   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
1003   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
1004   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
1005   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
1006   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1007   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1008   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
1009   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
1010   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
1011   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
1012   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1013   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1014   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1015   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1016   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1017   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1018   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1019   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1020   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
1021   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1022   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1023   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1024   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1025   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1026   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1027   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1028   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1029   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1030   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1031   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1032   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1033   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1034   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1035   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1036   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1037   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1038   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1039   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1040   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1041   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1042   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1043   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1044   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1045   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1046   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1047   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1048   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1049   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1050   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1051   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1052   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1053   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1054   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1055   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1056   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1057   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1058   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1059   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1060   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1061   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1062   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1063   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1064   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1065   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1066   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1067     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1068   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1069   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1070   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1071   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1072   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1073   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1074   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1075   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1076     TRUE }, /* must come after all fonts */\r
1077   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1078   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1079     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1080   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1081   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1082   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1083   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1084   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1085   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1086   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1087   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1088   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1089   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1090   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1091   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1092   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1093   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1094   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1095   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1096   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1097   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1098   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1099   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1100   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1101   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1102   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1103   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1104   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1105   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1106   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1107   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1108   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1109   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1110   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1111   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1112   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1113   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1114   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1115   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1116   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1117   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1118   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1119   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1120   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1121   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1122   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1123   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1124   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1125   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1126   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1127   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1128   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1129   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1130   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1131   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1132   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1133   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1134   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1135   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1136   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1137   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1138   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1139   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1140   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1141   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1142   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1143   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1144   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1145   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1146   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1147   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1148   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1149   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1150   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1151   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1152   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1153   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1154   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1155   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1156   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1157   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1158   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1159   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1160   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1161   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1162   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1163   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1164   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1165   { "highlightLastMove", ArgBoolean,\r
1166     (LPVOID) &appData.highlightLastMove, TRUE },\r
1167   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1168   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1169   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1170   { "highlightDragging", ArgBoolean,\r
1171     (LPVOID) &appData.highlightDragging, TRUE },\r
1172   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1173   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1174   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1175   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1176   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1177   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1178   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1179   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1180   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1181   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1182   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1183   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1184   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1185   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1186   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1187   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1188   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1189   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1190   { "soundShout", ArgFilename,\r
1191     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1192   { "soundSShout", ArgFilename,\r
1193     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1194   { "soundChannel1", ArgFilename,\r
1195     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1196   { "soundChannel", ArgFilename,\r
1197     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1198   { "soundKibitz", ArgFilename,\r
1199     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1200   { "soundTell", ArgFilename,\r
1201     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1202   { "soundChallenge", ArgFilename,\r
1203     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1204   { "soundRequest", ArgFilename,\r
1205     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1206   { "soundSeek", ArgFilename,\r
1207     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1208   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1209   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1210   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1211   { "soundIcsLoss", ArgFilename, \r
1212     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1213   { "soundIcsDraw", ArgFilename, \r
1214     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1215   { "soundIcsUnfinished", ArgFilename, \r
1216     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1217   { "soundIcsAlarm", ArgFilename, \r
1218     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1219   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1220   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1221   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1222   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1223   { "reuseChessPrograms", ArgBoolean,\r
1224     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1225   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1226   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1227   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1228   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1229   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1230   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1231   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1232   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1233   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1234   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1235   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1236   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1237   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1238   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1239   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1240     TRUE },\r
1241   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1242     TRUE },\r
1243   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1244   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1245   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1246   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1247   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1248   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1249   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1250   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1251   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1252   /* [AS] New features */\r
1253   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1254   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1255   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1256   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1257   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1258   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1259   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1260   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1261   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1262   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1263   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1264   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1265   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1266   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1267   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1268   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1269   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1270   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1271   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1272   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1273   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1274   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1275   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1276   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1277   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1278   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1279   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1280   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1281   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1282   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1283   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1284   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1285   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1286   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1287   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1288   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1289   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1290   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1291   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1292   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1293   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1294   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1295   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1296   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1297   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1298   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1299   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1300   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1301   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1302   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1303 \r
1304   /* [HGM] board-size, adjudication and misc. options */\r
1305   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1306   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1307   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1308   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1309   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1310   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1311   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1312   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1313   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1314   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1315   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1316   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1317   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1318   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1319   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1320   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1321   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1322   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1323   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1324   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1325   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1326   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1327   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1328   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1329   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1330   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1331   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1332   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1333   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1334   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1335   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1336   { "keepAlive", ArgInt, (LPVOID) &appData.keepAlive, FALSE },\r
1337   { "icstype", ArgInt, (LPVOID) &ics_type, FALSE },\r
1338   { "forceIllegalMoves", ArgTrue, (LPVOID) &appData.forceIllegal, FALSE },\r
1339 \r
1340 #ifdef ZIPPY\r
1341   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1342   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1343   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1344   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1345   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1346   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1347   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1348   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1349   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1350   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1351   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1352   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1353   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1354     FALSE },\r
1355   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1356   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1357   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1358   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1359   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1360   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1361   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1362     FALSE },\r
1363   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1364   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1365   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1366   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1367   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1368   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1369   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1370   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1371   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1372   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1373   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1374   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1375   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1376   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1377   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1378   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1379   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1380   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1381   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1382 #endif\r
1383   /* [HGM] options for broadcasting and time odds */\r
1384   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1385   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1386   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1387   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1388   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1389   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1390   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1391   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1392   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1393   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1394   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1395   { "keepLineBreaksICS", ArgBoolean, (LPVOID) &appData.noJoin, TRUE },\r
1396   { "wrapContinuationSequence", ArgString, (LPVOID) &appData.wrapContSeq, FALSE },\r
1397   { "useInternalWrap", ArgTrue, (LPVOID) &appData.useInternalWrap, FALSE }, /* noJoin usurps this if set */\r
1398   \r
1399   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1400   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1401   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1402   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1403   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1404   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1405   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1406   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1407   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1408   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1409   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1410   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1411   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1412   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1413   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1414   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1415   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1416   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1417   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1418   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1419   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1420   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1421   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1422   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1423   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1424   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1425   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1426   /* [AS] Layout stuff */\r
1427   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1428   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1429   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1430   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1431   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1432 \r
1433   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1434   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1435   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1436   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1437   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1438 \r
1439   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1440   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1441   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1442   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1443   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1444 \r
1445   { NULL, ArgNone, NULL, FALSE }\r
1446 };\r
1447 \r
1448 \r
1449 /* Kludge for indirection files on command line */\r
1450 char* lastIndirectionFilename;\r
1451 ArgDescriptor argDescriptorIndirection =\r
1452 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1453 \r
1454 \r
1455 VOID\r
1456 ExitArgError(char *msg, char *badArg)\r
1457 {\r
1458   char buf[MSG_SIZ];\r
1459 \r
1460   sprintf(buf, "%s %s", msg, badArg);\r
1461   DisplayFatalError(buf, 0, 2);\r
1462   exit(2);\r
1463 }\r
1464 \r
1465 /* Command line font name parser.  NULL name means do nothing.\r
1466    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1467    For backward compatibility, syntax without the colon is also\r
1468    accepted, but font names with digits in them won't work in that case.\r
1469 */\r
1470 VOID\r
1471 ParseFontName(char *name, MyFontParams *mfp)\r
1472 {\r
1473   char *p, *q;\r
1474   if (name == NULL) return;\r
1475   p = name;\r
1476   q = strchr(p, ':');\r
1477   if (q) {\r
1478     if (q - p >= sizeof(mfp->faceName))\r
1479       ExitArgError("Font name too long:", name);\r
1480     memcpy(mfp->faceName, p, q - p);\r
1481     mfp->faceName[q - p] = NULLCHAR;\r
1482     p = q + 1;\r
1483   } else {\r
1484     q = mfp->faceName;\r
1485     while (*p && !isdigit(*p)) {\r
1486       *q++ = *p++;\r
1487       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1488         ExitArgError("Font name too long:", name);\r
1489     }\r
1490     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1491     *q = NULLCHAR;\r
1492   }\r
1493   if (!*p) ExitArgError("Font point size missing:", name);\r
1494   mfp->pointSize = (float) atof(p);\r
1495   mfp->bold = (strchr(p, 'b') != NULL);\r
1496   mfp->italic = (strchr(p, 'i') != NULL);\r
1497   mfp->underline = (strchr(p, 'u') != NULL);\r
1498   mfp->strikeout = (strchr(p, 's') != NULL);\r
1499 }\r
1500 \r
1501 /* Color name parser.\r
1502    X version accepts X color names, but this one\r
1503    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1504 COLORREF\r
1505 ParseColorName(char *name)\r
1506 {\r
1507   int red, green, blue, count;\r
1508   char buf[MSG_SIZ];\r
1509 \r
1510   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1511   if (count != 3) {\r
1512     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1513       &red, &green, &blue);\r
1514   }\r
1515   if (count != 3) {\r
1516     sprintf(buf, "Can't parse color name %s", name);\r
1517     DisplayError(buf, 0);\r
1518     return RGB(0, 0, 0);\r
1519   }\r
1520   return PALETTERGB(red, green, blue);\r
1521 }\r
1522 \r
1523 \r
1524 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1525 {\r
1526   char *e = argValue;\r
1527   int eff = 0;\r
1528 \r
1529   while (*e) {\r
1530     if (*e == 'b')      eff |= CFE_BOLD;\r
1531     else if (*e == 'i') eff |= CFE_ITALIC;\r
1532     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1533     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1534     else if (*e == '#' || isdigit(*e)) break;\r
1535     e++;\r
1536   }\r
1537   *effects = eff;\r
1538   *color   = ParseColorName(e);\r
1539 }\r
1540 \r
1541 \r
1542 BoardSize\r
1543 ParseBoardSize(char *name)\r
1544 {\r
1545   BoardSize bs = SizeTiny;\r
1546   while (sizeInfo[bs].name != NULL) {\r
1547     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1548     bs++;\r
1549   }\r
1550   ExitArgError("Unrecognized board size value", name);\r
1551   return bs; /* not reached */\r
1552 }\r
1553 \r
1554 \r
1555 char\r
1556 StringGet(void *getClosure)\r
1557 {\r
1558   char **p = (char **) getClosure;\r
1559   return *((*p)++);\r
1560 }\r
1561 \r
1562 char\r
1563 FileGet(void *getClosure)\r
1564 {\r
1565   int c;\r
1566   FILE* f = (FILE*) getClosure;\r
1567 \r
1568   c = getc(f);\r
1569   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1570   if (c == EOF)\r
1571     return NULLCHAR;\r
1572   else\r
1573     return (char) c;\r
1574 }\r
1575 \r
1576 /* Parse settings file named "name". If file found, return the\r
1577    full name in fullname and return TRUE; else return FALSE */\r
1578 BOOLEAN\r
1579 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1580 {\r
1581   char *dummy;\r
1582   FILE *f;\r
1583   int ok; char buf[MSG_SIZ];\r
1584 \r
1585   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1586   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1587     sprintf(buf, "%s.ini", name);\r
1588     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1589   }\r
1590   if (ok) {\r
1591     f = fopen(fullname, "r");\r
1592     if (f != NULL) {\r
1593       ParseArgs(FileGet, f);\r
1594       fclose(f);\r
1595       return TRUE;\r
1596     }\r
1597   }\r
1598   return FALSE;\r
1599 }\r
1600 \r
1601 VOID\r
1602 ParseArgs(GetFunc get, void *cl)\r
1603 {\r
1604   char argName[ARG_MAX];\r
1605   char argValue[ARG_MAX];\r
1606   ArgDescriptor *ad;\r
1607   char start;\r
1608   char *q;\r
1609   int i, octval;\r
1610   char ch;\r
1611   int posarg = 0;\r
1612 \r
1613   ch = get(cl);\r
1614   for (;;) {\r
1615     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1616     if (ch == NULLCHAR) break;\r
1617     if (ch == ';') {\r
1618       /* Comment to end of line */\r
1619       ch = get(cl);\r
1620       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1621       continue;\r
1622     } else if (ch == '/' || ch == '-') {\r
1623       /* Switch */\r
1624       q = argName;\r
1625       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1626              ch != '\n' && ch != '\t') {\r
1627         *q++ = ch;\r
1628         ch = get(cl);\r
1629       }\r
1630       *q = NULLCHAR;\r
1631 \r
1632       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1633         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1634 \r
1635       if (ad->argName == NULL)\r
1636         ExitArgError("Unrecognized argument", argName);\r
1637 \r
1638     } else if (ch == '@') {\r
1639       /* Indirection file */\r
1640       ad = &argDescriptorIndirection;\r
1641       ch = get(cl);\r
1642     } else {\r
1643       /* Positional argument */\r
1644       ad = &argDescriptors[posarg++];\r
1645       strcpy(argName, ad->argName);\r
1646     }\r
1647 \r
1648     if (ad->argType == ArgTrue) {\r
1649       *(Boolean *) ad->argLoc = TRUE;\r
1650       continue;\r
1651     }\r
1652     if (ad->argType == ArgFalse) {\r
1653       *(Boolean *) ad->argLoc = FALSE;\r
1654       continue;\r
1655     }\r
1656 \r
1657     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1658     if (ch == NULLCHAR || ch == '\n') {\r
1659       ExitArgError("No value provided for argument", argName);\r
1660     }\r
1661     q = argValue;\r
1662     if (ch == '{') {\r
1663       // Quoting with { }.  No characters have to (or can) be escaped.\r
1664       // Thus the string cannot contain a '}' character.\r
1665       start = ch;\r
1666       ch = get(cl);\r
1667       while (start) {\r
1668         switch (ch) {\r
1669         case NULLCHAR:\r
1670           start = NULLCHAR;\r
1671           break;\r
1672           \r
1673         case '}':\r
1674           ch = get(cl);\r
1675           start = NULLCHAR;\r
1676           break;\r
1677 \r
1678         default:\r
1679           *q++ = ch;\r
1680           ch = get(cl);\r
1681           break;\r
1682         }\r
1683       }   \r
1684     } else if (ch == '\'' || ch == '"') {\r
1685       // Quoting with ' ' or " ", with \ as escape character.\r
1686       // Inconvenient for long strings that may contain Windows filenames.\r
1687       start = ch;\r
1688       ch = get(cl);\r
1689       while (start) {\r
1690         switch (ch) {\r
1691         case NULLCHAR:\r
1692           start = NULLCHAR;\r
1693           break;\r
1694 \r
1695         default:\r
1696         not_special:\r
1697           *q++ = ch;\r
1698           ch = get(cl);\r
1699           break;\r
1700 \r
1701         case '\'':\r
1702         case '\"':\r
1703           if (ch == start) {\r
1704             ch = get(cl);\r
1705             start = NULLCHAR;\r
1706             break;\r
1707           } else {\r
1708             goto not_special;\r
1709           }\r
1710 \r
1711         case '\\':\r
1712           if (ad->argType == ArgFilename\r
1713               || ad->argType == ArgSettingsFilename) {\r
1714               goto not_special;\r
1715           }\r
1716           ch = get(cl);\r
1717           switch (ch) {\r
1718           case NULLCHAR:\r
1719             ExitArgError("Incomplete \\ escape in value for", argName);\r
1720             break;\r
1721           case 'n':\r
1722             *q++ = '\n';\r
1723             ch = get(cl);\r
1724             break;\r
1725           case 'r':\r
1726             *q++ = '\r';\r
1727             ch = get(cl);\r
1728             break;\r
1729           case 't':\r
1730             *q++ = '\t';\r
1731             ch = get(cl);\r
1732             break;\r
1733           case 'b':\r
1734             *q++ = '\b';\r
1735             ch = get(cl);\r
1736             break;\r
1737           case 'f':\r
1738             *q++ = '\f';\r
1739             ch = get(cl);\r
1740             break;\r
1741           default:\r
1742             octval = 0;\r
1743             for (i = 0; i < 3; i++) {\r
1744               if (ch >= '0' && ch <= '7') {\r
1745                 octval = octval*8 + (ch - '0');\r
1746                 ch = get(cl);\r
1747               } else {\r
1748                 break;\r
1749               }\r
1750             }\r
1751             if (i > 0) {\r
1752               *q++ = (char) octval;\r
1753             } else {\r
1754               *q++ = ch;\r
1755               ch = get(cl);\r
1756             }\r
1757             break;\r
1758           }\r
1759           break;\r
1760         }\r
1761       }\r
1762     } else {\r
1763       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1764         *q++ = ch;\r
1765         ch = get(cl);\r
1766       }\r
1767     }\r
1768     *q = NULLCHAR;\r
1769 \r
1770     switch (ad->argType) {\r
1771     case ArgInt:\r
1772       *(int *) ad->argLoc = atoi(argValue);\r
1773       break;\r
1774 \r
1775     case ArgX:\r
1776       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1777       break;\r
1778 \r
1779     case ArgY:\r
1780       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1781       break;\r
1782 \r
1783     case ArgZ:\r
1784       *(int *) ad->argLoc = atoi(argValue);\r
1785       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1786       break;\r
1787 \r
1788     case ArgFloat:\r
1789       *(float *) ad->argLoc = (float) atof(argValue);\r
1790       break;\r
1791 \r
1792     case ArgString:\r
1793     case ArgFilename:\r
1794       *(char **) ad->argLoc = strdup(argValue);\r
1795       break;\r
1796 \r
1797     case ArgSettingsFilename:\r
1798       {\r
1799         char fullname[MSG_SIZ];\r
1800         if (ParseSettingsFile(argValue, fullname)) {\r
1801           if (ad->argLoc != NULL) {\r
1802             *(char **) ad->argLoc = strdup(fullname);\r
1803           }\r
1804         } else {\r
1805           if (ad->argLoc != NULL) {\r
1806           } else {\r
1807             ExitArgError("Failed to open indirection file", argValue);\r
1808           }\r
1809         }\r
1810       }\r
1811       break;\r
1812 \r
1813     case ArgBoolean:\r
1814       switch (argValue[0]) {\r
1815       case 't':\r
1816       case 'T':\r
1817         *(Boolean *) ad->argLoc = TRUE;\r
1818         break;\r
1819       case 'f':\r
1820       case 'F':\r
1821         *(Boolean *) ad->argLoc = FALSE;\r
1822         break;\r
1823       default:\r
1824         ExitArgError("Unrecognized boolean argument value", argValue);\r
1825         break;\r
1826       }\r
1827       break;\r
1828 \r
1829     case ArgColor:\r
1830       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1831       break;\r
1832 \r
1833     case ArgAttribs: {\r
1834       ColorClass cc = (ColorClass)ad->argLoc;\r
1835       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1836       }\r
1837       break;\r
1838       \r
1839     case ArgBoardSize:\r
1840       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1841       break;\r
1842 \r
1843     case ArgFont:\r
1844       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1845       break;\r
1846 \r
1847     case ArgCommSettings:\r
1848       ParseCommSettings(argValue, &dcb);\r
1849       break;\r
1850 \r
1851     case ArgNone:\r
1852       ExitArgError("Unrecognized argument", argValue);\r
1853       break;\r
1854     case ArgTrue:\r
1855     case ArgFalse: ;\r
1856     }\r
1857   }\r
1858 }\r
1859 \r
1860 VOID\r
1861 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1862 {\r
1863   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1864   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1865   DeleteDC(hdc);\r
1866   lf->lfWidth = 0;\r
1867   lf->lfEscapement = 0;\r
1868   lf->lfOrientation = 0;\r
1869   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1870   lf->lfItalic = mfp->italic;\r
1871   lf->lfUnderline = mfp->underline;\r
1872   lf->lfStrikeOut = mfp->strikeout;\r
1873   lf->lfCharSet = DEFAULT_CHARSET;\r
1874   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1875   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1876   lf->lfQuality = DEFAULT_QUALITY;\r
1877   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1878   strcpy(lf->lfFaceName, mfp->faceName);\r
1879 }\r
1880 \r
1881 VOID\r
1882 CreateFontInMF(MyFont *mf)\r
1883 {\r
1884   LFfromMFP(&mf->lf, &mf->mfp);\r
1885   if (mf->hf) DeleteObject(mf->hf);\r
1886   mf->hf = CreateFontIndirect(&mf->lf);\r
1887 }\r
1888 \r
1889 VOID\r
1890 SetDefaultTextAttribs()\r
1891 {\r
1892   ColorClass cc;\r
1893   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1894     ParseAttribs(&textAttribs[cc].color, \r
1895                  &textAttribs[cc].effects, \r
1896                  defaultTextAttribs[cc]);\r
1897   }\r
1898 }\r
1899 \r
1900 VOID\r
1901 SetDefaultSounds()\r
1902 {\r
1903   ColorClass cc;\r
1904   SoundClass sc;\r
1905   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1906     textAttribs[cc].sound.name = strdup("");\r
1907     textAttribs[cc].sound.data = NULL;\r
1908   }\r
1909   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1910     sounds[sc].name = strdup("");\r
1911     sounds[sc].data = NULL;\r
1912   }\r
1913   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1914 }\r
1915 \r
1916 VOID\r
1917 LoadAllSounds()\r
1918 {\r
1919   ColorClass cc;\r
1920   SoundClass sc;\r
1921   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1922     MyLoadSound(&textAttribs[cc].sound);\r
1923   }\r
1924   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1925     MyLoadSound(&sounds[sc]);\r
1926   }\r
1927 }\r
1928 \r
1929 VOID\r
1930 InitAppData(LPSTR lpCmdLine)\r
1931 {\r
1932   int i, j;\r
1933   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1934   char *dummy, *p;\r
1935 \r
1936   programName = szAppName;\r
1937 \r
1938   /* Initialize to defaults */\r
1939   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1940   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1941   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1942   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1943   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1944   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1945   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1946   SetDefaultTextAttribs();\r
1947   SetDefaultSounds();\r
1948   appData.movesPerSession = MOVES_PER_SESSION;\r
1949   appData.initString = INIT_STRING;\r
1950   appData.secondInitString = INIT_STRING;\r
1951   appData.firstComputerString = COMPUTER_STRING;\r
1952   appData.secondComputerString = COMPUTER_STRING;\r
1953   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1954   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1955   appData.firstPlaysBlack = FALSE;\r
1956   appData.noChessProgram = FALSE;\r
1957   chessProgram = FALSE;\r
1958   appData.firstHost = FIRST_HOST;\r
1959   appData.secondHost = SECOND_HOST;\r
1960   appData.firstDirectory = FIRST_DIRECTORY;\r
1961   appData.secondDirectory = SECOND_DIRECTORY;\r
1962   appData.bitmapDirectory = "";\r
1963   appData.remoteShell = REMOTE_SHELL;\r
1964   appData.remoteUser = "";\r
1965   appData.timeDelay = TIME_DELAY;\r
1966   appData.timeControl = TIME_CONTROL;\r
1967   appData.timeIncrement = TIME_INCREMENT;\r
1968   appData.icsActive = FALSE;\r
1969   appData.icsHost = "";\r
1970   appData.icsPort = ICS_PORT;\r
1971   appData.icsCommPort = ICS_COMM_PORT;\r
1972   appData.icsLogon = ICS_LOGON;\r
1973   appData.icsHelper = "";\r
1974   appData.useTelnet = FALSE;\r
1975   appData.telnetProgram = TELNET_PROGRAM;\r
1976   appData.gateway = "";\r
1977   appData.loadGameFile = "";\r
1978   appData.loadGameIndex = 0;\r
1979   appData.saveGameFile = "";\r
1980   appData.autoSaveGames = FALSE;\r
1981   appData.loadPositionFile = "";\r
1982   appData.loadPositionIndex = 1;\r
1983   appData.savePositionFile = "";\r
1984   appData.matchMode = FALSE;\r
1985   appData.matchGames = 0;\r
1986   appData.monoMode = FALSE;\r
1987   appData.debugMode = FALSE;\r
1988   appData.clockMode = TRUE;\r
1989   boardSize = (BoardSize) -1; /* determine by screen size */\r
1990   appData.Iconic = FALSE; /*unused*/\r
1991   appData.searchTime = "";\r
1992   appData.searchDepth = 0;\r
1993   appData.showCoords = FALSE;\r
1994   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1995   appData.autoCallFlag = FALSE;\r
1996   appData.flipView = FALSE;\r
1997   appData.autoFlipView = TRUE;\r
1998   appData.cmailGameName = "";\r
1999   appData.alwaysPromoteToQueen = FALSE;\r
2000   appData.oldSaveStyle = FALSE;\r
2001   appData.quietPlay = FALSE;\r
2002   appData.showThinking = FALSE;\r
2003   appData.ponderNextMove = TRUE;\r
2004   appData.periodicUpdates = TRUE;\r
2005   appData.popupExitMessage = TRUE;\r
2006   appData.popupMoveErrors = FALSE;\r
2007   appData.autoObserve = FALSE;\r
2008   appData.autoComment = FALSE;\r
2009   appData.animate = TRUE;\r
2010   appData.animSpeed = 10;\r
2011   appData.animateDragging = TRUE;\r
2012   appData.highlightLastMove = TRUE;\r
2013   appData.getMoveList = TRUE;\r
2014   appData.testLegality = TRUE;\r
2015   appData.premove = TRUE;\r
2016   appData.premoveWhite = FALSE;\r
2017   appData.premoveWhiteText = "";\r
2018   appData.premoveBlack = FALSE;\r
2019   appData.premoveBlackText = "";\r
2020   appData.icsAlarm = TRUE;\r
2021   appData.icsAlarmTime = 5000;\r
2022   appData.autoRaiseBoard = TRUE;\r
2023   appData.localLineEditing = TRUE;\r
2024   appData.colorize = TRUE;\r
2025   appData.reuseFirst = TRUE;\r
2026   appData.reuseSecond = TRUE;\r
2027   appData.blindfold = FALSE;\r
2028   appData.icsEngineAnalyze = FALSE;\r
2029   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2030   dcb.DCBlength = sizeof(DCB);\r
2031   dcb.BaudRate = 9600;\r
2032   dcb.fBinary = TRUE;\r
2033   dcb.fParity = FALSE;\r
2034   dcb.fOutxCtsFlow = FALSE;\r
2035   dcb.fOutxDsrFlow = FALSE;\r
2036   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2037   dcb.fDsrSensitivity = FALSE;\r
2038   dcb.fTXContinueOnXoff = TRUE;\r
2039   dcb.fOutX = FALSE;\r
2040   dcb.fInX = FALSE;\r
2041   dcb.fNull = FALSE;\r
2042   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2043   dcb.fAbortOnError = FALSE;\r
2044   dcb.ByteSize = 7;\r
2045   dcb.Parity = SPACEPARITY;\r
2046   dcb.StopBits = ONESTOPBIT;\r
2047   settingsFileName = SETTINGS_FILE;\r
2048   saveSettingsOnExit = TRUE;\r
2049   boardX = CW_USEDEFAULT;\r
2050   boardY = CW_USEDEFAULT;\r
2051   analysisX = CW_USEDEFAULT; \r
2052   analysisY = CW_USEDEFAULT; \r
2053   analysisW = CW_USEDEFAULT;\r
2054   analysisH = CW_USEDEFAULT;\r
2055   commentX = CW_USEDEFAULT; \r
2056   commentY = CW_USEDEFAULT; \r
2057   commentW = CW_USEDEFAULT;\r
2058   commentH = CW_USEDEFAULT;\r
2059   editTagsX = CW_USEDEFAULT; \r
2060   editTagsY = CW_USEDEFAULT; \r
2061   editTagsW = CW_USEDEFAULT;\r
2062   editTagsH = CW_USEDEFAULT;\r
2063   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2064   icsNames = ICS_NAMES;\r
2065   firstChessProgramNames = FCP_NAMES;\r
2066   secondChessProgramNames = SCP_NAMES;\r
2067   appData.initialMode = "";\r
2068   appData.variant = "normal";\r
2069   appData.firstProtocolVersion = PROTOVER;\r
2070   appData.secondProtocolVersion = PROTOVER;\r
2071   appData.showButtonBar = TRUE;\r
2072 \r
2073    /* [AS] New properties (see comments in header file) */\r
2074   appData.firstScoreIsAbsolute = FALSE;\r
2075   appData.secondScoreIsAbsolute = FALSE;\r
2076   appData.saveExtendedInfoInPGN = FALSE;\r
2077   appData.hideThinkingFromHuman = FALSE;\r
2078   appData.liteBackTextureFile = "";\r
2079   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2080   appData.darkBackTextureFile = "";\r
2081   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2082   appData.renderPiecesWithFont = "";\r
2083   appData.fontToPieceTable = "";\r
2084   appData.fontBackColorWhite = 0;\r
2085   appData.fontForeColorWhite = 0;\r
2086   appData.fontBackColorBlack = 0;\r
2087   appData.fontForeColorBlack = 0;\r
2088   appData.fontPieceSize = 80;\r
2089   appData.overrideLineGap = 1;\r
2090   appData.adjudicateLossThreshold = 0;\r
2091   appData.delayBeforeQuit = 0;\r
2092   appData.delayAfterQuit = 0;\r
2093   appData.nameOfDebugFile = "winboard.debug";\r
2094   appData.pgnEventHeader = "Computer Chess Game";\r
2095   appData.defaultFrcPosition = -1;\r
2096   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2097   appData.saveOutOfBookInfo = TRUE;\r
2098   appData.showEvalInMoveHistory = TRUE;\r
2099   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2100   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2101   appData.highlightMoveWithArrow = FALSE;\r
2102   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2103   appData.useStickyWindows = TRUE;\r
2104   appData.adjudicateDrawMoves = 0;\r
2105   appData.autoDisplayComment = TRUE;\r
2106   appData.autoDisplayTags = TRUE;\r
2107   appData.firstIsUCI = FALSE;\r
2108   appData.secondIsUCI = FALSE;\r
2109   appData.firstHasOwnBookUCI = TRUE;\r
2110   appData.secondHasOwnBookUCI = TRUE;\r
2111   appData.polyglotDir = "";\r
2112   appData.usePolyglotBook = FALSE;\r
2113   appData.polyglotBook = "";\r
2114   appData.defaultHashSize = 64;\r
2115   appData.defaultCacheSizeEGTB = 4;\r
2116   appData.defaultPathEGTB = "c:\\egtb";\r
2117   appData.firstOptions = "";\r
2118   appData.secondOptions = "";\r
2119 \r
2120   InitWindowPlacement( &wpGameList );\r
2121   InitWindowPlacement( &wpMoveHistory );\r
2122   InitWindowPlacement( &wpEvalGraph );\r
2123   InitWindowPlacement( &wpEngineOutput );\r
2124   InitWindowPlacement( &wpConsole );\r
2125 \r
2126   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2127   appData.NrFiles      = -1;\r
2128   appData.NrRanks      = -1;\r
2129   appData.holdingsSize = -1;\r
2130   appData.testClaims   = FALSE;\r
2131   appData.checkMates   = FALSE;\r
2132   appData.materialDraws= FALSE;\r
2133   appData.trivialDraws = FALSE;\r
2134   appData.ruleMoves    = 51;\r
2135   appData.drawRepeats  = 6;\r
2136   appData.matchPause   = 10000;\r
2137   appData.alphaRank    = FALSE;\r
2138   appData.allWhite     = FALSE;\r
2139   appData.upsideDown   = FALSE;\r
2140   appData.serverPause  = 15;\r
2141   appData.serverMovesName   = NULL;\r
2142   appData.suppressLoadMoves = FALSE;\r
2143   appData.firstTimeOdds  = 1;\r
2144   appData.secondTimeOdds = 1;\r
2145   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2146   appData.secondAccumulateTC = 1;\r
2147   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2148   appData.secondNPS = -1;\r
2149   appData.engineComments = 1;\r
2150   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2151   appData.egtFormats = "";\r
2152 \r
2153 #ifdef ZIPPY\r
2154   appData.zippyTalk = ZIPPY_TALK;\r
2155   appData.zippyPlay = ZIPPY_PLAY;\r
2156   appData.zippyLines = ZIPPY_LINES;\r
2157   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2158   appData.zippyPassword = ZIPPY_PASSWORD;\r
2159   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2160   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2161   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2162   appData.zippyUseI = ZIPPY_USE_I;\r
2163   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2164   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2165   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2166   appData.zippyGameStart = ZIPPY_GAME_START;\r
2167   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2168   appData.zippyAbort = ZIPPY_ABORT;\r
2169   appData.zippyVariants = ZIPPY_VARIANTS;\r
2170   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2171   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2172 #endif\r
2173 \r
2174   /* Point font array elements to structures and\r
2175      parse default font names */\r
2176   for (i=0; i<NUM_FONTS; i++) {\r
2177     for (j=0; j<NUM_SIZES; j++) {\r
2178       font[j][i] = &fontRec[j][i];\r
2179       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2180     }\r
2181   }\r
2182   \r
2183   /* Parse default settings file if any */\r
2184   if (ParseSettingsFile(settingsFileName, buf)) {\r
2185     settingsFileName = strdup(buf);\r
2186   }\r
2187 \r
2188   /* Parse command line */\r
2189   ParseArgs(StringGet, &lpCmdLine);\r
2190 \r
2191   /* [HGM] make sure board size is acceptable */\r
2192   if(appData.NrFiles > BOARD_SIZE ||\r
2193      appData.NrRanks > BOARD_SIZE   )\r
2194       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2195 \r
2196   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2197    * with options from the command line, we now make an even higher priority\r
2198    * overrule by WB options attached to the engine command line. This so that\r
2199    * tournament managers can use WB options (such as /timeOdds) that follow\r
2200    * the engines.\r
2201    */\r
2202   if(appData.firstChessProgram != NULL) {\r
2203       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2204       static char *f = "first";\r
2205       char buf[MSG_SIZ], *q = buf;\r
2206       if(p != NULL) { // engine command line contains WinBoard options\r
2207           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2208           ParseArgs(StringGet, &q);\r
2209           p[-1] = 0; // cut them offengine command line\r
2210       }\r
2211   }\r
2212   // now do same for second chess program\r
2213   if(appData.secondChessProgram != NULL) {\r
2214       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2215       static char *s = "second";\r
2216       char buf[MSG_SIZ], *q = buf;\r
2217       if(p != NULL) { // engine command line contains WinBoard options\r
2218           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2219           ParseArgs(StringGet, &q);\r
2220           p[-1] = 0; // cut them offengine command line\r
2221       }\r
2222   }\r
2223 \r
2224 \r
2225   /* Propagate options that affect others */\r
2226   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2227   if (appData.icsActive || appData.noChessProgram) {\r
2228      chessProgram = FALSE;  /* not local chess program mode */\r
2229   }\r
2230 \r
2231   /* Open startup dialog if needed */\r
2232   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2233       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2234       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2235                         *appData.secondChessProgram == NULLCHAR))) {\r
2236     FARPROC lpProc;\r
2237     \r
2238     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2239     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2240     FreeProcInstance(lpProc);\r
2241   }\r
2242 \r
2243   /* Make sure save files land in the right (?) directory */\r
2244   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2245     appData.saveGameFile = strdup(buf);\r
2246   }\r
2247   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2248     appData.savePositionFile = strdup(buf);\r
2249   }\r
2250 \r
2251   /* Finish initialization for fonts and sounds */\r
2252   for (i=0; i<NUM_FONTS; i++) {\r
2253     for (j=0; j<NUM_SIZES; j++) {\r
2254       CreateFontInMF(font[j][i]);\r
2255     }\r
2256   }\r
2257   /* xboard, and older WinBoards, controlled the move sound with the\r
2258      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2259      always turn the option on (so that the backend will call us),\r
2260      then let the user turn the sound off by setting it to silence if\r
2261      desired.  To accommodate old winboard.ini files saved by old\r
2262      versions of WinBoard, we also turn off the sound if the option\r
2263      was initially set to false. */\r
2264   if (!appData.ringBellAfterMoves) {\r
2265     sounds[(int)SoundMove].name = strdup("");\r
2266     appData.ringBellAfterMoves = TRUE;\r
2267   }\r
2268   GetCurrentDirectory(MSG_SIZ, currDir);\r
2269   SetCurrentDirectory(installDir);\r
2270   LoadAllSounds();\r
2271   SetCurrentDirectory(currDir);\r
2272 \r
2273   p = icsTextMenuString;\r
2274   if (p[0] == '@') {\r
2275     FILE* f = fopen(p + 1, "r");\r
2276     if (f == NULL) {\r
2277       DisplayFatalError(p + 1, errno, 2);\r
2278       return;\r
2279     }\r
2280     i = fread(buf, 1, sizeof(buf)-1, f);\r
2281     fclose(f);\r
2282     buf[i] = NULLCHAR;\r
2283     p = buf;\r
2284   }\r
2285   ParseIcsTextMenu(strdup(p));\r
2286 }\r
2287 \r
2288 \r
2289 VOID\r
2290 InitMenuChecks()\r
2291 {\r
2292   HMENU hmenu = GetMenu(hwndMain);\r
2293 \r
2294   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2295                         MF_BYCOMMAND|((appData.icsActive &&\r
2296                                        *appData.icsCommPort != NULLCHAR) ?\r
2297                                       MF_ENABLED : MF_GRAYED));\r
2298   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2299                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2300                                      MF_CHECKED : MF_UNCHECKED));\r
2301 }\r
2302 \r
2303 \r
2304 VOID\r
2305 SaveSettings(char* name)\r
2306 {\r
2307   FILE *f;\r
2308   ArgDescriptor *ad;\r
2309   WINDOWPLACEMENT wp;\r
2310   char dir[MSG_SIZ];\r
2311 \r
2312   if (!hwndMain) return;\r
2313 \r
2314   GetCurrentDirectory(MSG_SIZ, dir);\r
2315   SetCurrentDirectory(installDir);\r
2316   f = fopen(name, "w");\r
2317   SetCurrentDirectory(dir);\r
2318   if (f == NULL) {\r
2319     DisplayError(name, errno);\r
2320     return;\r
2321   }\r
2322   fprintf(f, ";\n");\r
2323   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2324   fprintf(f, ";\n");\r
2325   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2326   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2327   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2328   fprintf(f, ";\n");\r
2329 \r
2330   wp.length = sizeof(WINDOWPLACEMENT);\r
2331   GetWindowPlacement(hwndMain, &wp);\r
2332   boardX = wp.rcNormalPosition.left;\r
2333   boardY = wp.rcNormalPosition.top;\r
2334 \r
2335   if (hwndConsole) {\r
2336     GetWindowPlacement(hwndConsole, &wp);\r
2337     wpConsole.x = wp.rcNormalPosition.left;\r
2338     wpConsole.y = wp.rcNormalPosition.top;\r
2339     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2340     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2341   }\r
2342 \r
2343   if (analysisDialog) {\r
2344     GetWindowPlacement(analysisDialog, &wp);\r
2345     analysisX = wp.rcNormalPosition.left;\r
2346     analysisY = wp.rcNormalPosition.top;\r
2347     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2348     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2349   }\r
2350 \r
2351   if (commentDialog) {\r
2352     GetWindowPlacement(commentDialog, &wp);\r
2353     commentX = wp.rcNormalPosition.left;\r
2354     commentY = wp.rcNormalPosition.top;\r
2355     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2356     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2357   }\r
2358 \r
2359   if (editTagsDialog) {\r
2360     GetWindowPlacement(editTagsDialog, &wp);\r
2361     editTagsX = wp.rcNormalPosition.left;\r
2362     editTagsY = wp.rcNormalPosition.top;\r
2363     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2364     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2365   }\r
2366 \r
2367   if (gameListDialog) {\r
2368     GetWindowPlacement(gameListDialog, &wp);\r
2369     wpGameList.x = wp.rcNormalPosition.left;\r
2370     wpGameList.y = wp.rcNormalPosition.top;\r
2371     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2372     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2373   }\r
2374 \r
2375   /* [AS] Move history */\r
2376   wpMoveHistory.visible = MoveHistoryIsUp();\r
2377   \r
2378   if( moveHistoryDialog ) {\r
2379     GetWindowPlacement(moveHistoryDialog, &wp);\r
2380     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2381     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2382     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2383     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2384   }\r
2385 \r
2386   /* [AS] Eval graph */\r
2387   wpEvalGraph.visible = EvalGraphIsUp();\r
2388 \r
2389   if( evalGraphDialog ) {\r
2390     GetWindowPlacement(evalGraphDialog, &wp);\r
2391     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2392     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2393     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2394     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2395   }\r
2396 \r
2397   /* [AS] Engine output */\r
2398   wpEngineOutput.visible = EngineOutputIsUp();\r
2399 \r
2400   if( engineOutputDialog ) {\r
2401     GetWindowPlacement(engineOutputDialog, &wp);\r
2402     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2403     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2404     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2405     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2406   }\r
2407 \r
2408   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2409     if (!ad->save) continue;\r
2410     switch (ad->argType) {\r
2411     case ArgString:\r
2412       {\r
2413         char *p = *(char **)ad->argLoc;\r
2414         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2415           /* Quote multiline values or \-containing values\r
2416              with { } if possible */\r
2417           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2418         } else {\r
2419           /* Else quote with " " */\r
2420           fprintf(f, "/%s=\"", ad->argName);\r
2421           while (*p) {\r
2422             if (*p == '\n') fprintf(f, "\n");\r
2423             else if (*p == '\r') fprintf(f, "\\r");\r
2424             else if (*p == '\t') fprintf(f, "\\t");\r
2425             else if (*p == '\b') fprintf(f, "\\b");\r
2426             else if (*p == '\f') fprintf(f, "\\f");\r
2427             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2428             else if (*p == '\"') fprintf(f, "\\\"");\r
2429             else if (*p == '\\') fprintf(f, "\\\\");\r
2430             else putc(*p, f);\r
2431             p++;\r
2432           }\r
2433           fprintf(f, "\"\n");\r
2434         }\r
2435       }\r
2436       break;\r
2437     case ArgInt:\r
2438     case ArgZ:\r
2439       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2440       break;\r
2441     case ArgX:\r
2442       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2443       break;\r
2444     case ArgY:\r
2445       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2446       break;\r
2447     case ArgFloat:\r
2448       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2449       break;\r
2450     case ArgBoolean:\r
2451       fprintf(f, "/%s=%s\n", ad->argName, \r
2452         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2453       break;\r
2454     case ArgTrue:\r
2455       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2456       break;\r
2457     case ArgFalse:\r
2458       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2459       break;\r
2460     case ArgColor:\r
2461       {\r
2462         COLORREF color = *(COLORREF *)ad->argLoc;\r
2463         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2464           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2465       }\r
2466       break;\r
2467     case ArgAttribs:\r
2468       {\r
2469         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2470         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2471           (ta->effects & CFE_BOLD) ? "b" : "",\r
2472           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2473           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2474           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2475           (ta->effects) ? " " : "",\r
2476           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2477       }\r
2478       break;\r
2479     case ArgFilename:\r
2480       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2481         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2482       } else {\r
2483         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2484       }\r
2485       break;\r
2486     case ArgBoardSize:\r
2487       fprintf(f, "/%s=%s\n", ad->argName,\r
2488               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2489       break;\r
2490     case ArgFont:\r
2491       {\r
2492         int bs;\r
2493         for (bs=0; bs<NUM_SIZES; bs++) {\r
2494           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2495           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2496           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2497             ad->argName, mfp->faceName, mfp->pointSize,\r
2498             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2499             mfp->bold ? "b" : "",\r
2500             mfp->italic ? "i" : "",\r
2501             mfp->underline ? "u" : "",\r
2502             mfp->strikeout ? "s" : "");\r
2503         }\r
2504       }\r
2505       break;\r
2506     case ArgCommSettings:\r
2507       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2508     case ArgNone:\r
2509     case ArgSettingsFilename: ;\r
2510     }\r
2511   }\r
2512   fclose(f);\r
2513 }\r
2514 \r
2515 \r
2516 \r
2517 /*---------------------------------------------------------------------------*\\r
2518  *\r
2519  * GDI board drawing routines\r
2520  *\r
2521 \*---------------------------------------------------------------------------*/\r
2522 \r
2523 /* [AS] Draw square using background texture */\r
2524 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2525 {\r
2526     XFORM   x;\r
2527 \r
2528     if( mode == 0 ) {\r
2529         return; /* Should never happen! */\r
2530     }\r
2531 \r
2532     SetGraphicsMode( dst, GM_ADVANCED );\r
2533 \r
2534     switch( mode ) {\r
2535     case 1:\r
2536         /* Identity */\r
2537         break;\r
2538     case 2:\r
2539         /* X reflection */\r
2540         x.eM11 = -1.0;\r
2541         x.eM12 = 0;\r
2542         x.eM21 = 0;\r
2543         x.eM22 = 1.0;\r
2544         x.eDx = (FLOAT) dw + dx - 1;\r
2545         x.eDy = 0;\r
2546         dx = 0;\r
2547         SetWorldTransform( dst, &x );\r
2548         break;\r
2549     case 3:\r
2550         /* Y reflection */\r
2551         x.eM11 = 1.0;\r
2552         x.eM12 = 0;\r
2553         x.eM21 = 0;\r
2554         x.eM22 = -1.0;\r
2555         x.eDx = 0;\r
2556         x.eDy = (FLOAT) dh + dy - 1;\r
2557         dy = 0;\r
2558         SetWorldTransform( dst, &x );\r
2559         break;\r
2560     case 4:\r
2561         /* X/Y flip */\r
2562         x.eM11 = 0;\r
2563         x.eM12 = 1.0;\r
2564         x.eM21 = 1.0;\r
2565         x.eM22 = 0;\r
2566         x.eDx = (FLOAT) dx;\r
2567         x.eDy = (FLOAT) dy;\r
2568         dx = 0;\r
2569         dy = 0;\r
2570         SetWorldTransform( dst, &x );\r
2571         break;\r
2572     }\r
2573 \r
2574     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2575 \r
2576     x.eM11 = 1.0;\r
2577     x.eM12 = 0;\r
2578     x.eM21 = 0;\r
2579     x.eM22 = 1.0;\r
2580     x.eDx = 0;\r
2581     x.eDy = 0;\r
2582     SetWorldTransform( dst, &x );\r
2583 \r
2584     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2585 }\r
2586 \r
2587 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2588 enum {\r
2589     PM_WP = (int) WhitePawn, \r
2590     PM_WN = (int) WhiteKnight, \r
2591     PM_WB = (int) WhiteBishop, \r
2592     PM_WR = (int) WhiteRook, \r
2593     PM_WQ = (int) WhiteQueen, \r
2594     PM_WF = (int) WhiteFerz, \r
2595     PM_WW = (int) WhiteWazir, \r
2596     PM_WE = (int) WhiteAlfil, \r
2597     PM_WM = (int) WhiteMan, \r
2598     PM_WO = (int) WhiteCannon, \r
2599     PM_WU = (int) WhiteUnicorn, \r
2600     PM_WH = (int) WhiteNightrider, \r
2601     PM_WA = (int) WhiteAngel, \r
2602     PM_WC = (int) WhiteMarshall, \r
2603     PM_WAB = (int) WhiteCardinal, \r
2604     PM_WD = (int) WhiteDragon, \r
2605     PM_WL = (int) WhiteLance, \r
2606     PM_WS = (int) WhiteCobra, \r
2607     PM_WV = (int) WhiteFalcon, \r
2608     PM_WSG = (int) WhiteSilver, \r
2609     PM_WG = (int) WhiteGrasshopper, \r
2610     PM_WK = (int) WhiteKing,\r
2611     PM_BP = (int) BlackPawn, \r
2612     PM_BN = (int) BlackKnight, \r
2613     PM_BB = (int) BlackBishop, \r
2614     PM_BR = (int) BlackRook, \r
2615     PM_BQ = (int) BlackQueen, \r
2616     PM_BF = (int) BlackFerz, \r
2617     PM_BW = (int) BlackWazir, \r
2618     PM_BE = (int) BlackAlfil, \r
2619     PM_BM = (int) BlackMan,\r
2620     PM_BO = (int) BlackCannon, \r
2621     PM_BU = (int) BlackUnicorn, \r
2622     PM_BH = (int) BlackNightrider, \r
2623     PM_BA = (int) BlackAngel, \r
2624     PM_BC = (int) BlackMarshall, \r
2625     PM_BG = (int) BlackGrasshopper, \r
2626     PM_BAB = (int) BlackCardinal,\r
2627     PM_BD = (int) BlackDragon,\r
2628     PM_BL = (int) BlackLance,\r
2629     PM_BS = (int) BlackCobra,\r
2630     PM_BV = (int) BlackFalcon,\r
2631     PM_BSG = (int) BlackSilver,\r
2632     PM_BK = (int) BlackKing\r
2633 };\r
2634 \r
2635 static HFONT hPieceFont = NULL;\r
2636 static HBITMAP hPieceMask[(int) EmptySquare];\r
2637 static HBITMAP hPieceFace[(int) EmptySquare];\r
2638 static int fontBitmapSquareSize = 0;\r
2639 static char pieceToFontChar[(int) EmptySquare] =\r
2640                               { 'p', 'n', 'b', 'r', 'q', \r
2641                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2642                       'k', 'o', 'm', 'v', 't', 'w', \r
2643                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2644                                                               'l' };\r
2645 \r
2646 extern BOOL SetCharTable( char *table, const char * map );\r
2647 /* [HGM] moved to backend.c */\r
2648 \r
2649 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2650 {\r
2651     HBRUSH hbrush;\r
2652     BYTE r1 = GetRValue( color );\r
2653     BYTE g1 = GetGValue( color );\r
2654     BYTE b1 = GetBValue( color );\r
2655     BYTE r2 = r1 / 2;\r
2656     BYTE g2 = g1 / 2;\r
2657     BYTE b2 = b1 / 2;\r
2658     RECT rc;\r
2659 \r
2660     /* Create a uniform background first */\r
2661     hbrush = CreateSolidBrush( color );\r
2662     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2663     FillRect( hdc, &rc, hbrush );\r
2664     DeleteObject( hbrush );\r
2665     \r
2666     if( mode == 1 ) {\r
2667         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2668         int steps = squareSize / 2;\r
2669         int i;\r
2670 \r
2671         for( i=0; i<steps; i++ ) {\r
2672             BYTE r = r1 - (r1-r2) * i / steps;\r
2673             BYTE g = g1 - (g1-g2) * i / steps;\r
2674             BYTE b = b1 - (b1-b2) * i / steps;\r
2675 \r
2676             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2677             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2678             FillRect( hdc, &rc, hbrush );\r
2679             DeleteObject(hbrush);\r
2680         }\r
2681     }\r
2682     else if( mode == 2 ) {\r
2683         /* Diagonal gradient, good more or less for every piece */\r
2684         POINT triangle[3];\r
2685         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2686         HBRUSH hbrush_old;\r
2687         int steps = squareSize;\r
2688         int i;\r
2689 \r
2690         triangle[0].x = squareSize - steps;\r
2691         triangle[0].y = squareSize;\r
2692         triangle[1].x = squareSize;\r
2693         triangle[1].y = squareSize;\r
2694         triangle[2].x = squareSize;\r
2695         triangle[2].y = squareSize - steps;\r
2696 \r
2697         for( i=0; i<steps; i++ ) {\r
2698             BYTE r = r1 - (r1-r2) * i / steps;\r
2699             BYTE g = g1 - (g1-g2) * i / steps;\r
2700             BYTE b = b1 - (b1-b2) * i / steps;\r
2701 \r
2702             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2703             hbrush_old = SelectObject( hdc, hbrush );\r
2704             Polygon( hdc, triangle, 3 );\r
2705             SelectObject( hdc, hbrush_old );\r
2706             DeleteObject(hbrush);\r
2707             triangle[0].x++;\r
2708             triangle[2].y++;\r
2709         }\r
2710 \r
2711         SelectObject( hdc, hpen );\r
2712     }\r
2713 }\r
2714 \r
2715 /*\r
2716     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2717     seems to work ok. The main problem here is to find the "inside" of a chess\r
2718     piece: follow the steps as explained below.\r
2719 */\r
2720 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2721 {\r
2722     HBITMAP hbm;\r
2723     HBITMAP hbm_old;\r
2724     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2725     RECT rc;\r
2726     SIZE sz;\r
2727     POINT pt;\r
2728     int backColor = whitePieceColor; \r
2729     int foreColor = blackPieceColor;\r
2730     \r
2731     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2732         backColor = appData.fontBackColorWhite;\r
2733         foreColor = appData.fontForeColorWhite;\r
2734     }\r
2735     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2736         backColor = appData.fontBackColorBlack;\r
2737         foreColor = appData.fontForeColorBlack;\r
2738     }\r
2739 \r
2740     /* Mask */\r
2741     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2742 \r
2743     hbm_old = SelectObject( hdc, hbm );\r
2744 \r
2745     rc.left = 0;\r
2746     rc.top = 0;\r
2747     rc.right = squareSize;\r
2748     rc.bottom = squareSize;\r
2749 \r
2750     /* Step 1: background is now black */\r
2751     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2752 \r
2753     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2754 \r
2755     pt.x = (squareSize - sz.cx) / 2;\r
2756     pt.y = (squareSize - sz.cy) / 2;\r
2757 \r
2758     SetBkMode( hdc, TRANSPARENT );\r
2759     SetTextColor( hdc, chroma );\r
2760     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2761     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2762 \r
2763     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2764     /* Step 3: the area outside the piece is filled with white */\r
2765 //    FloodFill( hdc, 0, 0, chroma );\r
2766     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2767     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2768     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2769     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2770     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2771     /* \r
2772         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2773         but if the start point is not inside the piece we're lost!\r
2774         There should be a better way to do this... if we could create a region or path\r
2775         from the fill operation we would be fine for example.\r
2776     */\r
2777 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2778     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2779 \r
2780     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2781         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2782         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2783 \r
2784         SelectObject( dc2, bm2 );\r
2785         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2786         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2787         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2788         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2789         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2790 \r
2791         DeleteDC( dc2 );\r
2792         DeleteObject( bm2 );\r
2793     }\r
2794 \r
2795     SetTextColor( hdc, 0 );\r
2796     /* \r
2797         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2798         draw the piece again in black for safety.\r
2799     */\r
2800     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2801 \r
2802     SelectObject( hdc, hbm_old );\r
2803 \r
2804     if( hPieceMask[index] != NULL ) {\r
2805         DeleteObject( hPieceMask[index] );\r
2806     }\r
2807 \r
2808     hPieceMask[index] = hbm;\r
2809 \r
2810     /* Face */\r
2811     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2812 \r
2813     SelectObject( hdc, hbm );\r
2814 \r
2815     {\r
2816         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2817         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2818         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2819 \r
2820         SelectObject( dc1, hPieceMask[index] );\r
2821         SelectObject( dc2, bm2 );\r
2822         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2823         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2824         \r
2825         /* \r
2826             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2827             the piece background and deletes (makes transparent) the rest.\r
2828             Thanks to that mask, we are free to paint the background with the greates\r
2829             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2830             We use this, to make gradients and give the pieces a "roundish" look.\r
2831         */\r
2832         SetPieceBackground( hdc, backColor, 2 );\r
2833         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2834 \r
2835         DeleteDC( dc2 );\r
2836         DeleteDC( dc1 );\r
2837         DeleteObject( bm2 );\r
2838     }\r
2839 \r
2840     SetTextColor( hdc, foreColor );\r
2841     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2842 \r
2843     SelectObject( hdc, hbm_old );\r
2844 \r
2845     if( hPieceFace[index] != NULL ) {\r
2846         DeleteObject( hPieceFace[index] );\r
2847     }\r
2848 \r
2849     hPieceFace[index] = hbm;\r
2850 }\r
2851 \r
2852 static int TranslatePieceToFontPiece( int piece )\r
2853 {\r
2854     switch( piece ) {\r
2855     case BlackPawn:\r
2856         return PM_BP;\r
2857     case BlackKnight:\r
2858         return PM_BN;\r
2859     case BlackBishop:\r
2860         return PM_BB;\r
2861     case BlackRook:\r
2862         return PM_BR;\r
2863     case BlackQueen:\r
2864         return PM_BQ;\r
2865     case BlackKing:\r
2866         return PM_BK;\r
2867     case WhitePawn:\r
2868         return PM_WP;\r
2869     case WhiteKnight:\r
2870         return PM_WN;\r
2871     case WhiteBishop:\r
2872         return PM_WB;\r
2873     case WhiteRook:\r
2874         return PM_WR;\r
2875     case WhiteQueen:\r
2876         return PM_WQ;\r
2877     case WhiteKing:\r
2878         return PM_WK;\r
2879 \r
2880     case BlackAngel:\r
2881         return PM_BA;\r
2882     case BlackMarshall:\r
2883         return PM_BC;\r
2884     case BlackFerz:\r
2885         return PM_BF;\r
2886     case BlackNightrider:\r
2887         return PM_BH;\r
2888     case BlackAlfil:\r
2889         return PM_BE;\r
2890     case BlackWazir:\r
2891         return PM_BW;\r
2892     case BlackUnicorn:\r
2893         return PM_BU;\r
2894     case BlackCannon:\r
2895         return PM_BO;\r
2896     case BlackGrasshopper:\r
2897         return PM_BG;\r
2898     case BlackMan:\r
2899         return PM_BM;\r
2900     case BlackSilver:\r
2901         return PM_BSG;\r
2902     case BlackLance:\r
2903         return PM_BL;\r
2904     case BlackFalcon:\r
2905         return PM_BV;\r
2906     case BlackCobra:\r
2907         return PM_BS;\r
2908     case BlackCardinal:\r
2909         return PM_BAB;\r
2910     case BlackDragon:\r
2911         return PM_BD;\r
2912 \r
2913     case WhiteAngel:\r
2914         return PM_WA;\r
2915     case WhiteMarshall:\r
2916         return PM_WC;\r
2917     case WhiteFerz:\r
2918         return PM_WF;\r
2919     case WhiteNightrider:\r
2920         return PM_WH;\r
2921     case WhiteAlfil:\r
2922         return PM_WE;\r
2923     case WhiteWazir:\r
2924         return PM_WW;\r
2925     case WhiteUnicorn:\r
2926         return PM_WU;\r
2927     case WhiteCannon:\r
2928         return PM_WO;\r
2929     case WhiteGrasshopper:\r
2930         return PM_WG;\r
2931     case WhiteMan:\r
2932         return PM_WM;\r
2933     case WhiteSilver:\r
2934         return PM_WSG;\r
2935     case WhiteLance:\r
2936         return PM_WL;\r
2937     case WhiteFalcon:\r
2938         return PM_WV;\r
2939     case WhiteCobra:\r
2940         return PM_WS;\r
2941     case WhiteCardinal:\r
2942         return PM_WAB;\r
2943     case WhiteDragon:\r
2944         return PM_WD;\r
2945     }\r
2946 \r
2947     return 0;\r
2948 }\r
2949 \r
2950 void CreatePiecesFromFont()\r
2951 {\r
2952     LOGFONT lf;\r
2953     HDC hdc_window = NULL;\r
2954     HDC hdc = NULL;\r
2955     HFONT hfont_old;\r
2956     int fontHeight;\r
2957     int i;\r
2958 \r
2959     if( fontBitmapSquareSize < 0 ) {\r
2960         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2961         return;\r
2962     }\r
2963 \r
2964     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2965         fontBitmapSquareSize = -1;\r
2966         return;\r
2967     }\r
2968 \r
2969     if( fontBitmapSquareSize != squareSize ) {\r
2970         hdc_window = GetDC( hwndMain );\r
2971         hdc = CreateCompatibleDC( hdc_window );\r
2972 \r
2973         if( hPieceFont != NULL ) {\r
2974             DeleteObject( hPieceFont );\r
2975         }\r
2976         else {\r
2977             for( i=0; i<=(int)BlackKing; i++ ) {\r
2978                 hPieceMask[i] = NULL;\r
2979                 hPieceFace[i] = NULL;\r
2980             }\r
2981         }\r
2982 \r
2983         fontHeight = 75;\r
2984 \r
2985         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2986             fontHeight = appData.fontPieceSize;\r
2987         }\r
2988 \r
2989         fontHeight = (fontHeight * squareSize) / 100;\r
2990 \r
2991         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2992         lf.lfWidth = 0;\r
2993         lf.lfEscapement = 0;\r
2994         lf.lfOrientation = 0;\r
2995         lf.lfWeight = FW_NORMAL;\r
2996         lf.lfItalic = 0;\r
2997         lf.lfUnderline = 0;\r
2998         lf.lfStrikeOut = 0;\r
2999         lf.lfCharSet = DEFAULT_CHARSET;\r
3000         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
3001         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
3002         lf.lfQuality = PROOF_QUALITY;\r
3003         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
3004         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
3005         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
3006 \r
3007         hPieceFont = CreateFontIndirect( &lf );\r
3008 \r
3009         if( hPieceFont == NULL ) {\r
3010             fontBitmapSquareSize = -2;\r
3011         }\r
3012         else {\r
3013             /* Setup font-to-piece character table */\r
3014             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
3015                 /* No (or wrong) global settings, try to detect the font */\r
3016                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
3017                     /* Alpha */\r
3018                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
3019                 }\r
3020                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3021                     /* DiagramTT* family */\r
3022                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3023                 }\r
3024                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3025                     /* Fairy symbols */\r
3026                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3027                 }\r
3028                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3029                     /* Good Companion (Some characters get warped as literal :-( */\r
3030                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3031                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3032                     SetCharTable(pieceToFontChar, s);\r
3033                 }\r
3034                 else {\r
3035                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3036                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3037                 }\r
3038             }\r
3039 \r
3040             /* Create bitmaps */\r
3041             hfont_old = SelectObject( hdc, hPieceFont );\r
3042             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3043                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3044                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3045 \r
3046             SelectObject( hdc, hfont_old );\r
3047 \r
3048             fontBitmapSquareSize = squareSize;\r
3049         }\r
3050     }\r
3051 \r
3052     if( hdc != NULL ) {\r
3053         DeleteDC( hdc );\r
3054     }\r
3055 \r
3056     if( hdc_window != NULL ) {\r
3057         ReleaseDC( hwndMain, hdc_window );\r
3058     }\r
3059 }\r
3060 \r
3061 HBITMAP\r
3062 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3063 {\r
3064   char name[128];\r
3065 \r
3066   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3067   if (gameInfo.event &&\r
3068       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3069       strcmp(name, "k80s") == 0) {\r
3070     strcpy(name, "tim");\r
3071   }\r
3072   return LoadBitmap(hinst, name);\r
3073 }\r
3074 \r
3075 \r
3076 /* Insert a color into the program's logical palette\r
3077    structure.  This code assumes the given color is\r
3078    the result of the RGB or PALETTERGB macro, and it\r
3079    knows how those macros work (which is documented).\r
3080 */\r
3081 VOID\r
3082 InsertInPalette(COLORREF color)\r
3083 {\r
3084   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3085 \r
3086   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3087     DisplayFatalError("Too many colors", 0, 1);\r
3088     pLogPal->palNumEntries--;\r
3089     return;\r
3090   }\r
3091 \r
3092   pe->peFlags = (char) 0;\r
3093   pe->peRed = (char) (0xFF & color);\r
3094   pe->peGreen = (char) (0xFF & (color >> 8));\r
3095   pe->peBlue = (char) (0xFF & (color >> 16));\r
3096   return;\r
3097 }\r
3098 \r
3099 \r
3100 VOID\r
3101 InitDrawingColors()\r
3102 {\r
3103   if (pLogPal == NULL) {\r
3104     /* Allocate enough memory for a logical palette with\r
3105      * PALETTESIZE entries and set the size and version fields\r
3106      * of the logical palette structure.\r
3107      */\r
3108     pLogPal = (NPLOGPALETTE)\r
3109       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3110                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3111     pLogPal->palVersion    = 0x300;\r
3112   }\r
3113   pLogPal->palNumEntries = 0;\r
3114 \r
3115   InsertInPalette(lightSquareColor);\r
3116   InsertInPalette(darkSquareColor);\r
3117   InsertInPalette(whitePieceColor);\r
3118   InsertInPalette(blackPieceColor);\r
3119   InsertInPalette(highlightSquareColor);\r
3120   InsertInPalette(premoveHighlightColor);\r
3121 \r
3122   /*  create a logical color palette according the information\r
3123    *  in the LOGPALETTE structure.\r
3124    */\r
3125   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3126 \r
3127   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3128   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3129   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3130   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3131   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3132   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3133   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3134   /* [AS] Force rendering of the font-based pieces */\r
3135   if( fontBitmapSquareSize > 0 ) {\r
3136     fontBitmapSquareSize = 0;\r
3137   }\r
3138 }\r
3139 \r
3140 \r
3141 int\r
3142 BoardWidth(int boardSize, int n)\r
3143 { /* [HGM] argument n added to allow different width and height */\r
3144   int lineGap = sizeInfo[boardSize].lineGap;\r
3145 \r
3146   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3147       lineGap = appData.overrideLineGap;\r
3148   }\r
3149 \r
3150   return (n + 1) * lineGap +\r
3151           n * sizeInfo[boardSize].squareSize;\r
3152 }\r
3153 \r
3154 /* Respond to board resize by dragging edge */\r
3155 VOID\r
3156 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3157 {\r
3158   BoardSize newSize = NUM_SIZES - 1;\r
3159   static int recurse = 0;\r
3160   if (IsIconic(hwndMain)) return;\r
3161   if (recurse > 0) return;\r
3162   recurse++;\r
3163   while (newSize > 0) {\r
3164         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3165         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3166            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3167     newSize--;\r
3168   } \r
3169   boardSize = newSize;\r
3170   InitDrawingSizes(boardSize, flags);\r
3171   recurse--;\r
3172 }\r
3173 \r
3174 \r
3175 \r
3176 VOID\r
3177 InitDrawingSizes(BoardSize boardSize, int flags)\r
3178 {\r
3179   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3180   ChessSquare piece;\r
3181   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3182   HDC hdc;\r
3183   SIZE clockSize, messageSize;\r
3184   HFONT oldFont;\r
3185   char buf[MSG_SIZ];\r
3186   char *str;\r
3187   HMENU hmenu = GetMenu(hwndMain);\r
3188   RECT crect, wrect, oldRect;\r
3189   int offby;\r
3190   LOGBRUSH logbrush;\r
3191 \r
3192   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3193   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3194 \r
3195   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3196   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3197 \r
3198   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3199   oldRect.top = boardY;\r
3200   oldRect.right = boardX + winWidth;\r
3201   oldRect.bottom = boardY + winHeight;\r
3202 \r
3203   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3204   smallLayout = sizeInfo[boardSize].smallLayout;\r
3205   squareSize = sizeInfo[boardSize].squareSize;\r
3206   lineGap = sizeInfo[boardSize].lineGap;\r
3207   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3208 \r
3209   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3210       lineGap = appData.overrideLineGap;\r
3211   }\r
3212 \r
3213   if (tinyLayout != oldTinyLayout) {\r
3214     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3215     if (tinyLayout) {\r
3216       style &= ~WS_SYSMENU;\r
3217       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3218                  "&Minimize\tCtrl+F4");\r
3219     } else {\r
3220       style |= WS_SYSMENU;\r
3221       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3222     }\r
3223     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3224 \r
3225     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3226       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3227         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3228     }\r
3229     DrawMenuBar(hwndMain);\r
3230   }\r
3231 \r
3232   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3233   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3234 \r
3235   /* Get text area sizes */\r
3236   hdc = GetDC(hwndMain);\r
3237   if (appData.clockMode) {\r
3238     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3239   } else {\r
3240     sprintf(buf, "White");\r
3241   }\r
3242   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3243   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3244   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3245   str = "We only care about the height here";\r
3246   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3247   SelectObject(hdc, oldFont);\r
3248   ReleaseDC(hwndMain, hdc);\r
3249 \r
3250   /* Compute where everything goes */\r
3251   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3252         /* [HGM] logo: if either logo is on, reserve space for it */\r
3253         logoHeight =  2*clockSize.cy;\r
3254         leftLogoRect.left   = OUTER_MARGIN;\r
3255         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3256         leftLogoRect.top    = OUTER_MARGIN;\r
3257         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3258 \r
3259         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3260         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3261         rightLogoRect.top    = OUTER_MARGIN;\r
3262         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3263 \r
3264 \r
3265     whiteRect.left = leftLogoRect.right;\r
3266     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3267     whiteRect.top = OUTER_MARGIN;\r
3268     whiteRect.bottom = whiteRect.top + logoHeight;\r
3269 \r
3270     blackRect.right = rightLogoRect.left;\r
3271     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3272     blackRect.top = whiteRect.top;\r
3273     blackRect.bottom = whiteRect.bottom;\r
3274   } else {\r
3275     whiteRect.left = OUTER_MARGIN;\r
3276     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3277     whiteRect.top = OUTER_MARGIN;\r
3278     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3279 \r
3280     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3281     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3282     blackRect.top = whiteRect.top;\r
3283     blackRect.bottom = whiteRect.bottom;\r
3284   }\r
3285 \r
3286   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3287   if (appData.showButtonBar) {\r
3288     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3289       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3290   } else {\r
3291     messageRect.right = OUTER_MARGIN + boardWidth;\r
3292   }\r
3293   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3294   messageRect.bottom = messageRect.top + messageSize.cy;\r
3295 \r
3296   boardRect.left = OUTER_MARGIN;\r
3297   boardRect.right = boardRect.left + boardWidth;\r
3298   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3299   boardRect.bottom = boardRect.top + boardHeight;\r
3300 \r
3301   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3302   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3303   oldBoardSize = boardSize;\r
3304   oldTinyLayout = tinyLayout;\r
3305   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3306   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3307     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3308   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3309   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3310   winHeight = winH; //       without disturbing window attachments\r
3311   GetWindowRect(hwndMain, &wrect);\r
3312   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3313                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3314 \r
3315   // [HGM] placement: let attached windows follow size change.\r
3316   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3317   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3318   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3319   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3320   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3321 \r
3322   /* compensate if menu bar wrapped */\r
3323   GetClientRect(hwndMain, &crect);\r
3324   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3325   winHeight += offby;\r
3326   switch (flags) {\r
3327   case WMSZ_TOPLEFT:\r
3328     SetWindowPos(hwndMain, NULL, \r
3329                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3330                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3331     break;\r
3332 \r
3333   case WMSZ_TOPRIGHT:\r
3334   case WMSZ_TOP:\r
3335     SetWindowPos(hwndMain, NULL, \r
3336                  wrect.left, wrect.bottom - winHeight, \r
3337                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3338     break;\r
3339 \r
3340   case WMSZ_BOTTOMLEFT:\r
3341   case WMSZ_LEFT:\r
3342     SetWindowPos(hwndMain, NULL, \r
3343                  wrect.right - winWidth, wrect.top, \r
3344                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3345     break;\r
3346 \r
3347   case WMSZ_BOTTOMRIGHT:\r
3348   case WMSZ_BOTTOM:\r
3349   case WMSZ_RIGHT:\r
3350   default:\r
3351     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3352                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3353     break;\r
3354   }\r
3355 \r
3356   hwndPause = NULL;\r
3357   for (i = 0; i < N_BUTTONS; i++) {\r
3358     if (buttonDesc[i].hwnd != NULL) {\r
3359       DestroyWindow(buttonDesc[i].hwnd);\r
3360       buttonDesc[i].hwnd = NULL;\r
3361     }\r
3362     if (appData.showButtonBar) {\r
3363       buttonDesc[i].hwnd =\r
3364         CreateWindow("BUTTON", buttonDesc[i].label,\r
3365                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3366                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3367                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3368                      (HMENU) buttonDesc[i].id,\r
3369                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3370       if (tinyLayout) {\r
3371         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3372                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3373                     MAKELPARAM(FALSE, 0));\r
3374       }\r
3375       if (buttonDesc[i].id == IDM_Pause)\r
3376         hwndPause = buttonDesc[i].hwnd;\r
3377       buttonDesc[i].wndproc = (WNDPROC)\r
3378         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3379     }\r
3380   }\r
3381   if (gridPen != NULL) DeleteObject(gridPen);\r
3382   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3383   if (premovePen != NULL) DeleteObject(premovePen);\r
3384   if (lineGap != 0) {\r
3385     logbrush.lbStyle = BS_SOLID;\r
3386     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3387     gridPen =\r
3388       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3389                    lineGap, &logbrush, 0, NULL);\r
3390     logbrush.lbColor = highlightSquareColor;\r
3391     highlightPen =\r
3392       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3393                    lineGap, &logbrush, 0, NULL);\r
3394 \r
3395     logbrush.lbColor = premoveHighlightColor; \r
3396     premovePen =\r
3397       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3398                    lineGap, &logbrush, 0, NULL);\r
3399 \r
3400     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3401     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3402       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3403       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3404         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3405       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3406         BOARD_WIDTH * (squareSize + lineGap);\r
3407       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3408     }\r
3409     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3410       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3411       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3412         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3413         lineGap / 2 + (i * (squareSize + lineGap));\r
3414       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3415         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3416       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3417     }\r
3418   }\r
3419 \r
3420   /* [HGM] Licensing requirement */\r
3421 #ifdef GOTHIC\r
3422   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3423 #endif\r
3424 #ifdef FALCON\r
3425   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3426 #endif\r
3427   GothicPopUp( "", VariantNormal);\r
3428 \r
3429 \r
3430 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3431 \r
3432   /* Load piece bitmaps for this board size */\r
3433   for (i=0; i<=2; i++) {\r
3434     for (piece = WhitePawn;\r
3435          (int) piece < (int) BlackPawn;\r
3436          piece = (ChessSquare) ((int) piece + 1)) {\r
3437       if (pieceBitmap[i][piece] != NULL)\r
3438         DeleteObject(pieceBitmap[i][piece]);\r
3439     }\r
3440   }\r
3441 \r
3442   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3443   // Orthodox Chess pieces\r
3444   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3445   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3446   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3447   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3448   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3449   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3450   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3451   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3452   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3453   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3454   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3455   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3456   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3457   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3458   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3459   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3460     // in Shogi, Hijack the unused Queen for Lance\r
3461     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3462     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3463     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3464   } else {\r
3465     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3466     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3467     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3468   }\r
3469 \r
3470   if(squareSize <= 72 && squareSize >= 33) { \r
3471     /* A & C are available in most sizes now */\r
3472     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3473       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3474       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3475       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3476       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3477       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3478       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3479       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3480       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3481       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3482       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3483       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3484       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3485     } else { // Smirf-like\r
3486       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3487       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3488       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3489     }\r
3490     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3491       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3492       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3493       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3494     } else { // WinBoard standard\r
3495       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3496       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3497       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3498     }\r
3499   }\r
3500 \r
3501 \r
3502   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3503     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3504     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3505     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3506     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3507     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3508     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3509     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3510     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3511     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3512     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3513     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3514     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3515     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3516     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3517     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3518     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3519     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3520     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3521     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3522     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3523     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3524     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3525     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3526     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3527     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3528     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3529     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3530     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3531     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3532     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3533 \r
3534     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3535       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3536       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3537       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3538       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3539       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3540       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3541       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3542       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3543       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3544       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3545       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3546       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3547     } else {\r
3548       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3549       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3550       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3551       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3552       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3553       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3554       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3555       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3556       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3557       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3558       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3559       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3560     }\r
3561 \r
3562   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3563     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3564     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3565     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3566     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3567     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3568     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3569     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3570     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3571     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3572     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3573     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3574     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3575     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3576     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3577   }\r
3578 \r
3579 \r
3580   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3581   /* special Shogi support in this size */\r
3582   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3583       for (piece = WhitePawn;\r
3584            (int) piece < (int) BlackPawn;\r
3585            piece = (ChessSquare) ((int) piece + 1)) {\r
3586         if (pieceBitmap[i][piece] != NULL)\r
3587           DeleteObject(pieceBitmap[i][piece]);\r
3588       }\r
3589     }\r
3590   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3591   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3592   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3593   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3594   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3595   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3596   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3597   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3598   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3599   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3600   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3601   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3602   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3603   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3604   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3605   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3606   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3607   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3608   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3609   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3610   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3611   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3612   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3613   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3614   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3615   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3616   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3617   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3618   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3619   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3620   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3621   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3622   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3623   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3624   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3625   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3626   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3627   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3628   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3629   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3630   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3631   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3632   minorSize = 0;\r
3633   }\r
3634 }\r
3635 \r
3636 HBITMAP\r
3637 PieceBitmap(ChessSquare p, int kind)\r
3638 {\r
3639   if ((int) p >= (int) BlackPawn)\r
3640     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3641 \r
3642   return pieceBitmap[kind][(int) p];\r
3643 }\r
3644 \r
3645 /***************************************************************/\r
3646 \r
3647 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3648 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3649 /*\r
3650 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3651 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3652 */\r
3653 \r
3654 VOID\r
3655 SquareToPos(int row, int column, int * x, int * y)\r
3656 {\r
3657   if (flipView) {\r
3658     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3659     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3660   } else {\r
3661     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3662     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3663   }\r
3664 }\r
3665 \r
3666 VOID\r
3667 DrawCoordsOnDC(HDC hdc)\r
3668 {\r
3669   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
3670   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
3671   char str[2] = { NULLCHAR, NULLCHAR };\r
3672   int oldMode, oldAlign, x, y, start, i;\r
3673   HFONT oldFont;\r
3674   HBRUSH oldBrush;\r
3675 \r
3676   if (!appData.showCoords)\r
3677     return;\r
3678 \r
3679   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3680 \r
3681   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3682   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3683   oldAlign = GetTextAlign(hdc);\r
3684   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3685 \r
3686   y = boardRect.top + lineGap;\r
3687   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3688 \r
3689   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3690   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3691     str[0] = files[start + i];\r
3692     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3693     y += squareSize + lineGap;\r
3694   }\r
3695 \r
3696   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3697 \r
3698   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3699   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3700     str[0] = ranks[start + i];\r
3701     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3702     x += squareSize + lineGap;\r
3703   }    \r
3704 \r
3705   SelectObject(hdc, oldBrush);\r
3706   SetBkMode(hdc, oldMode);\r
3707   SetTextAlign(hdc, oldAlign);\r
3708   SelectObject(hdc, oldFont);\r
3709 }\r
3710 \r
3711 VOID\r
3712 DrawGridOnDC(HDC hdc)\r
3713 {\r
3714   HPEN oldPen;\r
3715  \r
3716   if (lineGap != 0) {\r
3717     oldPen = SelectObject(hdc, gridPen);\r
3718     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3719     SelectObject(hdc, oldPen);\r
3720   }\r
3721 }\r
3722 \r
3723 #define HIGHLIGHT_PEN 0\r
3724 #define PREMOVE_PEN   1\r
3725 \r
3726 VOID\r
3727 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3728 {\r
3729   int x1, y1;\r
3730   HPEN oldPen, hPen;\r
3731   if (lineGap == 0) return;\r
3732   if (flipView) {\r
3733     x1 = boardRect.left +\r
3734       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3735     y1 = boardRect.top +\r
3736       lineGap/2 + y * (squareSize + lineGap);\r
3737   } else {\r
3738     x1 = boardRect.left +\r
3739       lineGap/2 + x * (squareSize + lineGap);\r
3740     y1 = boardRect.top +\r
3741       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3742   }\r
3743   hPen = pen ? premovePen : highlightPen;\r
3744   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3745   MoveToEx(hdc, x1, y1, NULL);\r
3746   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3747   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3748   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3749   LineTo(hdc, x1, y1);\r
3750   SelectObject(hdc, oldPen);\r
3751 }\r
3752 \r
3753 VOID\r
3754 DrawHighlightsOnDC(HDC hdc)\r
3755 {\r
3756   int i;\r
3757   for (i=0; i<2; i++) {\r
3758     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3759       DrawHighlightOnDC(hdc, TRUE,\r
3760                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3761                         HIGHLIGHT_PEN);\r
3762   }\r
3763   for (i=0; i<2; i++) {\r
3764     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3765         premoveHighlightInfo.sq[i].y >= 0) {\r
3766         DrawHighlightOnDC(hdc, TRUE,\r
3767                           premoveHighlightInfo.sq[i].x, \r
3768                           premoveHighlightInfo.sq[i].y,\r
3769                           PREMOVE_PEN);\r
3770     }\r
3771   }\r
3772 }\r
3773 \r
3774 /* Note: sqcolor is used only in monoMode */\r
3775 /* Note that this code is largely duplicated in woptions.c,\r
3776    function DrawSampleSquare, so that needs to be updated too */\r
3777 VOID\r
3778 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3779 {\r
3780   HBITMAP oldBitmap;\r
3781   HBRUSH oldBrush;\r
3782   int tmpSize;\r
3783 \r
3784   if (appData.blindfold) return;\r
3785 \r
3786   /* [AS] Use font-based pieces if needed */\r
3787   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3788     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3789     CreatePiecesFromFont();\r
3790 \r
3791     if( fontBitmapSquareSize == squareSize ) {\r
3792         int index = TranslatePieceToFontPiece(piece);\r
3793 \r
3794         SelectObject( tmphdc, hPieceMask[ index ] );\r
3795 \r
3796         BitBlt( hdc,\r
3797             x, y,\r
3798             squareSize, squareSize,\r
3799             tmphdc,\r
3800             0, 0,\r
3801             SRCAND );\r
3802 \r
3803         SelectObject( tmphdc, hPieceFace[ index ] );\r
3804 \r
3805         BitBlt( hdc,\r
3806             x, y,\r
3807             squareSize, squareSize,\r
3808             tmphdc,\r
3809             0, 0,\r
3810             SRCPAINT );\r
3811 \r
3812         return;\r
3813     }\r
3814   }\r
3815 \r
3816   if (appData.monoMode) {\r
3817     SelectObject(tmphdc, PieceBitmap(piece, \r
3818       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3819     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3820            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3821   } else {\r
3822     tmpSize = squareSize;\r
3823     if(minorSize &&\r
3824         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3825          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3826       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3827       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3828       x += (squareSize - minorSize)>>1;\r
3829       y += squareSize - minorSize - 2;\r
3830       tmpSize = minorSize;\r
3831     }\r
3832     if (color || appData.allWhite ) {\r
3833       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3834       if( color )\r
3835               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3836       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3837       if(appData.upsideDown && color==flipView)\r
3838         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3839       else\r
3840         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3841       /* Use black for outline of white pieces */\r
3842       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3843       if(appData.upsideDown && color==flipView)\r
3844         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3845       else\r
3846         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3847     } else {\r
3848       /* Use square color for details of black pieces */\r
3849       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3850       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3851       if(appData.upsideDown && !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     }\r
3856     SelectObject(hdc, oldBrush);\r
3857     SelectObject(tmphdc, oldBitmap);\r
3858   }\r
3859 }\r
3860 \r
3861 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3862 int GetBackTextureMode( int algo )\r
3863 {\r
3864     int result = BACK_TEXTURE_MODE_DISABLED;\r
3865 \r
3866     switch( algo ) \r
3867     {\r
3868         case BACK_TEXTURE_MODE_PLAIN:\r
3869             result = 1; /* Always use identity map */\r
3870             break;\r
3871         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3872             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3873             break;\r
3874     }\r
3875 \r
3876     return result;\r
3877 }\r
3878 \r
3879 /* \r
3880     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3881     to handle redraws cleanly (as random numbers would always be different).\r
3882 */\r
3883 VOID RebuildTextureSquareInfo()\r
3884 {\r
3885     BITMAP bi;\r
3886     int lite_w = 0;\r
3887     int lite_h = 0;\r
3888     int dark_w = 0;\r
3889     int dark_h = 0;\r
3890     int row;\r
3891     int col;\r
3892 \r
3893     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3894 \r
3895     if( liteBackTexture != NULL ) {\r
3896         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3897             lite_w = bi.bmWidth;\r
3898             lite_h = bi.bmHeight;\r
3899         }\r
3900     }\r
3901 \r
3902     if( darkBackTexture != NULL ) {\r
3903         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3904             dark_w = bi.bmWidth;\r
3905             dark_h = bi.bmHeight;\r
3906         }\r
3907     }\r
3908 \r
3909     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3910         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3911             if( (col + row) & 1 ) {\r
3912                 /* Lite square */\r
3913                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3914                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3915                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3916                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3917                 }\r
3918             }\r
3919             else {\r
3920                 /* Dark square */\r
3921                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3922                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3923                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3924                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3925                 }\r
3926             }\r
3927         }\r
3928     }\r
3929 }\r
3930 \r
3931 /* [AS] Arrow highlighting support */\r
3932 \r
3933 static int A_WIDTH = 5; /* Width of arrow body */\r
3934 \r
3935 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3936 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3937 \r
3938 static double Sqr( double x )\r
3939 {\r
3940     return x*x;\r
3941 }\r
3942 \r
3943 static int Round( double x )\r
3944 {\r
3945     return (int) (x + 0.5);\r
3946 }\r
3947 \r
3948 /* Draw an arrow between two points using current settings */\r
3949 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
3950 {\r
3951     POINT arrow[7];\r
3952     double dx, dy, j, k, x, y;\r
3953 \r
3954     if( d_x == s_x ) {\r
3955         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3956 \r
3957         arrow[0].x = s_x + A_WIDTH;\r
3958         arrow[0].y = s_y;\r
3959 \r
3960         arrow[1].x = s_x + A_WIDTH;\r
3961         arrow[1].y = d_y - h;\r
3962 \r
3963         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
3964         arrow[2].y = d_y - h;\r
3965 \r
3966         arrow[3].x = d_x;\r
3967         arrow[3].y = d_y;\r
3968 \r
3969         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
3970         arrow[4].y = d_y - h;\r
3971 \r
3972         arrow[5].x = s_x - A_WIDTH;\r
3973         arrow[5].y = d_y - h;\r
3974 \r
3975         arrow[6].x = s_x - A_WIDTH;\r
3976         arrow[6].y = s_y;\r
3977     }\r
3978     else if( d_y == s_y ) {\r
3979         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
3980 \r
3981         arrow[0].x = s_x;\r
3982         arrow[0].y = s_y + A_WIDTH;\r
3983 \r
3984         arrow[1].x = d_x - w;\r
3985         arrow[1].y = s_y + A_WIDTH;\r
3986 \r
3987         arrow[2].x = d_x - w;\r
3988         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
3989 \r
3990         arrow[3].x = d_x;\r
3991         arrow[3].y = d_y;\r
3992 \r
3993         arrow[4].x = d_x - w;\r
3994         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
3995 \r
3996         arrow[5].x = d_x - w;\r
3997         arrow[5].y = s_y - A_WIDTH;\r
3998 \r
3999         arrow[6].x = s_x;\r
4000         arrow[6].y = s_y - A_WIDTH;\r
4001     }\r
4002     else {\r
4003         /* [AS] Needed a lot of paper for this! :-) */\r
4004         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4005         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4006   \r
4007         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4008 \r
4009         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4010 \r
4011         x = s_x;\r
4012         y = s_y;\r
4013 \r
4014         arrow[0].x = Round(x - j);\r
4015         arrow[0].y = Round(y + j*dx);\r
4016 \r
4017         arrow[1].x = Round(x + j);\r
4018         arrow[1].y = Round(y - j*dx);\r
4019 \r
4020         if( d_x > s_x ) {\r
4021             x = (double) d_x - k;\r
4022             y = (double) d_y - k*dy;\r
4023         }\r
4024         else {\r
4025             x = (double) d_x + k;\r
4026             y = (double) d_y + k*dy;\r
4027         }\r
4028 \r
4029         arrow[2].x = Round(x + j);\r
4030         arrow[2].y = Round(y - j*dx);\r
4031 \r
4032         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4033         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4034 \r
4035         arrow[4].x = d_x;\r
4036         arrow[4].y = d_y;\r
4037 \r
4038         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4039         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4040 \r
4041         arrow[6].x = Round(x - j);\r
4042         arrow[6].y = Round(y + j*dx);\r
4043     }\r
4044 \r
4045     Polygon( hdc, arrow, 7 );\r
4046 }\r
4047 \r
4048 /* [AS] Draw an arrow between two squares */\r
4049 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4050 {\r
4051     int s_x, s_y, d_x, d_y;\r
4052     HPEN hpen;\r
4053     HPEN holdpen;\r
4054     HBRUSH hbrush;\r
4055     HBRUSH holdbrush;\r
4056     LOGBRUSH stLB;\r
4057 \r
4058     if( s_col == d_col && s_row == d_row ) {\r
4059         return;\r
4060     }\r
4061 \r
4062     /* Get source and destination points */\r
4063     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4064     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4065 \r
4066     if( d_y > s_y ) {\r
4067         d_y += squareSize / 4;\r
4068     }\r
4069     else if( d_y < s_y ) {\r
4070         d_y += 3 * squareSize / 4;\r
4071     }\r
4072     else {\r
4073         d_y += squareSize / 2;\r
4074     }\r
4075 \r
4076     if( d_x > s_x ) {\r
4077         d_x += squareSize / 4;\r
4078     }\r
4079     else if( d_x < s_x ) {\r
4080         d_x += 3 * squareSize / 4;\r
4081     }\r
4082     else {\r
4083         d_x += squareSize / 2;\r
4084     }\r
4085 \r
4086     s_x += squareSize / 2;\r
4087     s_y += squareSize / 2;\r
4088 \r
4089     /* Adjust width */\r
4090     A_WIDTH = squareSize / 14;\r
4091 \r
4092     /* Draw */\r
4093     stLB.lbStyle = BS_SOLID;\r
4094     stLB.lbColor = appData.highlightArrowColor;\r
4095     stLB.lbHatch = 0;\r
4096 \r
4097     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4098     holdpen = SelectObject( hdc, hpen );\r
4099     hbrush = CreateBrushIndirect( &stLB );\r
4100     holdbrush = SelectObject( hdc, hbrush );\r
4101 \r
4102     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4103 \r
4104     SelectObject( hdc, holdpen );\r
4105     SelectObject( hdc, holdbrush );\r
4106     DeleteObject( hpen );\r
4107     DeleteObject( hbrush );\r
4108 }\r
4109 \r
4110 BOOL HasHighlightInfo()\r
4111 {\r
4112     BOOL result = FALSE;\r
4113 \r
4114     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4115         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4116     {\r
4117         result = TRUE;\r
4118     }\r
4119 \r
4120     return result;\r
4121 }\r
4122 \r
4123 BOOL IsDrawArrowEnabled()\r
4124 {\r
4125     BOOL result = FALSE;\r
4126 \r
4127     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4128         result = TRUE;\r
4129     }\r
4130 \r
4131     return result;\r
4132 }\r
4133 \r
4134 VOID DrawArrowHighlight( HDC hdc )\r
4135 {\r
4136     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4137         DrawArrowBetweenSquares( hdc,\r
4138             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4139             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4140     }\r
4141 }\r
4142 \r
4143 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4144 {\r
4145     HRGN result = NULL;\r
4146 \r
4147     if( HasHighlightInfo() ) {\r
4148         int x1, y1, x2, y2;\r
4149         int sx, sy, dx, dy;\r
4150 \r
4151         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4152         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4153 \r
4154         sx = MIN( x1, x2 );\r
4155         sy = MIN( y1, y2 );\r
4156         dx = MAX( x1, x2 ) + squareSize;\r
4157         dy = MAX( y1, y2 ) + squareSize;\r
4158 \r
4159         result = CreateRectRgn( sx, sy, dx, dy );\r
4160     }\r
4161 \r
4162     return result;\r
4163 }\r
4164 \r
4165 /*\r
4166     Warning: this function modifies the behavior of several other functions. \r
4167     \r
4168     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4169     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4170     repaint is scattered all over the place, which is not good for features such as\r
4171     "arrow highlighting" that require a full repaint of the board.\r
4172 \r
4173     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4174     user interaction, when speed is not so important) but especially to avoid errors\r
4175     in the displayed graphics.\r
4176 \r
4177     In such patched places, I always try refer to this function so there is a single\r
4178     place to maintain knowledge.\r
4179     \r
4180     To restore the original behavior, just return FALSE unconditionally.\r
4181 */\r
4182 BOOL IsFullRepaintPreferrable()\r
4183 {\r
4184     BOOL result = FALSE;\r
4185 \r
4186     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4187         /* Arrow may appear on the board */\r
4188         result = TRUE;\r
4189     }\r
4190 \r
4191     return result;\r
4192 }\r
4193 \r
4194 /* \r
4195     This function is called by DrawPosition to know whether a full repaint must\r
4196     be forced or not.\r
4197 \r
4198     Only DrawPosition may directly call this function, which makes use of \r
4199     some state information. Other function should call DrawPosition specifying \r
4200     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4201 */\r
4202 BOOL DrawPositionNeedsFullRepaint()\r
4203 {\r
4204     BOOL result = FALSE;\r
4205 \r
4206     /* \r
4207         Probably a slightly better policy would be to trigger a full repaint\r
4208         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4209         but animation is fast enough that it's difficult to notice.\r
4210     */\r
4211     if( animInfo.piece == EmptySquare ) {\r
4212         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4213             result = TRUE;\r
4214         }\r
4215     }\r
4216 \r
4217     return result;\r
4218 }\r
4219 \r
4220 VOID\r
4221 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4222 {\r
4223   int row, column, x, y, square_color, piece_color;\r
4224   ChessSquare piece;\r
4225   HBRUSH oldBrush;\r
4226   HDC texture_hdc = NULL;\r
4227 \r
4228   /* [AS] Initialize background textures if needed */\r
4229   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4230       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4231       if( backTextureSquareSize != squareSize \r
4232        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4233           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4234           backTextureSquareSize = squareSize;\r
4235           RebuildTextureSquareInfo();\r
4236       }\r
4237 \r
4238       texture_hdc = CreateCompatibleDC( hdc );\r
4239   }\r
4240 \r
4241   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4242     for (column = 0; column < BOARD_WIDTH; column++) {\r
4243   \r
4244       SquareToPos(row, column, &x, &y);\r
4245 \r
4246       piece = board[row][column];\r
4247 \r
4248       square_color = ((column + row) % 2) == 1;\r
4249       if( gameInfo.variant == VariantXiangqi ) {\r
4250           square_color = !InPalace(row, column);\r
4251           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4252           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4253       }\r
4254       piece_color = (int) piece < (int) BlackPawn;\r
4255 \r
4256 \r
4257       /* [HGM] holdings file: light square or black */\r
4258       if(column == BOARD_LEFT-2) {\r
4259             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4260                 square_color = 1;\r
4261             else {\r
4262                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4263                 continue;\r
4264             }\r
4265       } else\r
4266       if(column == BOARD_RGHT + 1 ) {\r
4267             if( row < gameInfo.holdingsSize )\r
4268                 square_color = 1;\r
4269             else {\r
4270                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4271                 continue;\r
4272             }\r
4273       }\r
4274       if(column == BOARD_LEFT-1 ) /* left align */\r
4275             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4276       else if( column == BOARD_RGHT) /* right align */\r
4277             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4278       else\r
4279       if (appData.monoMode) {\r
4280         if (piece == EmptySquare) {\r
4281           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4282                  square_color ? WHITENESS : BLACKNESS);\r
4283         } else {\r
4284           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4285         }\r
4286       } \r
4287       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4288           /* [AS] Draw the square using a texture bitmap */\r
4289           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4290           int r = row, c = column; // [HGM] do not flip board in flipView\r
4291           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4292 \r
4293           DrawTile( x, y, \r
4294               squareSize, squareSize, \r
4295               hdc, \r
4296               texture_hdc,\r
4297               backTextureSquareInfo[r][c].mode,\r
4298               backTextureSquareInfo[r][c].x,\r
4299               backTextureSquareInfo[r][c].y );\r
4300 \r
4301           SelectObject( texture_hdc, hbm );\r
4302 \r
4303           if (piece != EmptySquare) {\r
4304               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4305           }\r
4306       }\r
4307       else {\r
4308         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4309 \r
4310         oldBrush = SelectObject(hdc, brush );\r
4311         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4312         SelectObject(hdc, oldBrush);\r
4313         if (piece != EmptySquare)\r
4314           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4315       }\r
4316     }\r
4317   }\r
4318 \r
4319   if( texture_hdc != NULL ) {\r
4320     DeleteDC( texture_hdc );\r
4321   }\r
4322 }\r
4323 \r
4324 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4325 void fputDW(FILE *f, int x)\r
4326 {\r
4327         fputc(x     & 255, f);\r
4328         fputc(x>>8  & 255, f);\r
4329         fputc(x>>16 & 255, f);\r
4330         fputc(x>>24 & 255, f);\r
4331 }\r
4332 \r
4333 #define MAX_CLIPS 200   /* more than enough */\r
4334 \r
4335 VOID\r
4336 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4337 {\r
4338 //  HBITMAP bufferBitmap;\r
4339   BITMAP bi;\r
4340 //  RECT Rect;\r
4341   HDC tmphdc;\r
4342   HBITMAP hbm;\r
4343   int w = 100, h = 50;\r
4344 \r
4345   if(logo == NULL) return;\r
4346 //  GetClientRect(hwndMain, &Rect);\r
4347 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4348 //                                      Rect.bottom-Rect.top+1);\r
4349   tmphdc = CreateCompatibleDC(hdc);\r
4350   hbm = SelectObject(tmphdc, logo);\r
4351   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4352             w = bi.bmWidth;\r
4353             h = bi.bmHeight;\r
4354   }\r
4355   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4356                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4357   SelectObject(tmphdc, hbm);\r
4358   DeleteDC(tmphdc);\r
4359 }\r
4360 \r
4361 VOID\r
4362 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4363 {\r
4364   static Board lastReq, lastDrawn;\r
4365   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4366   static int lastDrawnFlipView = 0;\r
4367   static int lastReqValid = 0, lastDrawnValid = 0;\r
4368   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4369   HDC tmphdc;\r
4370   HDC hdcmem;\r
4371   HBITMAP bufferBitmap;\r
4372   HBITMAP oldBitmap;\r
4373   RECT Rect;\r
4374   HRGN clips[MAX_CLIPS];\r
4375   ChessSquare dragged_piece = EmptySquare;\r
4376 \r
4377   /* I'm undecided on this - this function figures out whether a full\r
4378    * repaint is necessary on its own, so there's no real reason to have the\r
4379    * caller tell it that.  I think this can safely be set to FALSE - but\r
4380    * if we trust the callers not to request full repaints unnessesarily, then\r
4381    * we could skip some clipping work.  In other words, only request a full\r
4382    * redraw when the majority of pieces have changed positions (ie. flip, \r
4383    * gamestart and similar)  --Hawk\r
4384    */\r
4385   Boolean fullrepaint = repaint;\r
4386 \r
4387   if( DrawPositionNeedsFullRepaint() ) {\r
4388       fullrepaint = TRUE;\r
4389   }\r
4390 \r
4391   if (board == NULL) {\r
4392     if (!lastReqValid) {\r
4393       return;\r
4394     }\r
4395     board = lastReq;\r
4396   } else {\r
4397     CopyBoard(lastReq, board);\r
4398     lastReqValid = 1;\r
4399   }\r
4400 \r
4401   if (doingSizing) {\r
4402     return;\r
4403   }\r
4404 \r
4405   if (IsIconic(hwndMain)) {\r
4406     return;\r
4407   }\r
4408 \r
4409   if (hdc == NULL) {\r
4410     hdc = GetDC(hwndMain);\r
4411     if (!appData.monoMode) {\r
4412       SelectPalette(hdc, hPal, FALSE);\r
4413       RealizePalette(hdc);\r
4414     }\r
4415     releaseDC = TRUE;\r
4416   } else {\r
4417     releaseDC = FALSE;\r
4418   }\r
4419 \r
4420   /* Create some work-DCs */\r
4421   hdcmem = CreateCompatibleDC(hdc);\r
4422   tmphdc = CreateCompatibleDC(hdc);\r
4423 \r
4424   /* If dragging is in progress, we temporarely remove the piece */\r
4425   /* [HGM] or temporarily decrease count if stacked              */\r
4426   /*       !! Moved to before board compare !!                   */\r
4427   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4428     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4429     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4430             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4431         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4432     } else \r
4433     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4434             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4435         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4436     } else \r
4437         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4438   }\r
4439 \r
4440   /* Figure out which squares need updating by comparing the \r
4441    * newest board with the last drawn board and checking if\r
4442    * flipping has changed.\r
4443    */\r
4444   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4445     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4446       for (column = 0; column < BOARD_WIDTH; column++) {\r
4447         if (lastDrawn[row][column] != board[row][column]) {\r
4448           SquareToPos(row, column, &x, &y);\r
4449           clips[num_clips++] =\r
4450             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4451         }\r
4452       }\r
4453     }\r
4454     for (i=0; i<2; i++) {\r
4455       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4456           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4457         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4458             lastDrawnHighlight.sq[i].y >= 0) {\r
4459           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4460                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4461           clips[num_clips++] =\r
4462             CreateRectRgn(x - lineGap, y - lineGap, \r
4463                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4464         }\r
4465         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4466           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4467           clips[num_clips++] =\r
4468             CreateRectRgn(x - lineGap, y - lineGap, \r
4469                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4470         }\r
4471       }\r
4472     }\r
4473     for (i=0; i<2; i++) {\r
4474       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4475           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4476         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4477             lastDrawnPremove.sq[i].y >= 0) {\r
4478           SquareToPos(lastDrawnPremove.sq[i].y,\r
4479                       lastDrawnPremove.sq[i].x, &x, &y);\r
4480           clips[num_clips++] =\r
4481             CreateRectRgn(x - lineGap, y - lineGap, \r
4482                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4483         }\r
4484         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4485             premoveHighlightInfo.sq[i].y >= 0) {\r
4486           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4487                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4488           clips[num_clips++] =\r
4489             CreateRectRgn(x - lineGap, y - lineGap, \r
4490                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4491         }\r
4492       }\r
4493     }\r
4494   } else {\r
4495     fullrepaint = TRUE;\r
4496   }\r
4497 \r
4498   /* Create a buffer bitmap - this is the actual bitmap\r
4499    * being written to.  When all the work is done, we can\r
4500    * copy it to the real DC (the screen).  This avoids\r
4501    * the problems with flickering.\r
4502    */\r
4503   GetClientRect(hwndMain, &Rect);\r
4504   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4505                                         Rect.bottom-Rect.top+1);\r
4506   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4507   if (!appData.monoMode) {\r
4508     SelectPalette(hdcmem, hPal, FALSE);\r
4509   }\r
4510 \r
4511   /* Create clips for dragging */\r
4512   if (!fullrepaint) {\r
4513     if (dragInfo.from.x >= 0) {\r
4514       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4515       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4516     }\r
4517     if (dragInfo.start.x >= 0) {\r
4518       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4519       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4520     }\r
4521     if (dragInfo.pos.x >= 0) {\r
4522       x = dragInfo.pos.x - squareSize / 2;\r
4523       y = dragInfo.pos.y - squareSize / 2;\r
4524       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4525     }\r
4526     if (dragInfo.lastpos.x >= 0) {\r
4527       x = dragInfo.lastpos.x - squareSize / 2;\r
4528       y = dragInfo.lastpos.y - squareSize / 2;\r
4529       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4530     }\r
4531   }\r
4532 \r
4533   /* Are we animating a move?  \r
4534    * If so, \r
4535    *   - remove the piece from the board (temporarely)\r
4536    *   - calculate the clipping region\r
4537    */\r
4538   if (!fullrepaint) {\r
4539     if (animInfo.piece != EmptySquare) {\r
4540       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4541       x = boardRect.left + animInfo.lastpos.x;\r
4542       y = boardRect.top + animInfo.lastpos.y;\r
4543       x2 = boardRect.left + animInfo.pos.x;\r
4544       y2 = boardRect.top + animInfo.pos.y;\r
4545       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4546       /* Slight kludge.  The real problem is that after AnimateMove is\r
4547          done, the position on the screen does not match lastDrawn.\r
4548          This currently causes trouble only on e.p. captures in\r
4549          atomic, where the piece moves to an empty square and then\r
4550          explodes.  The old and new positions both had an empty square\r
4551          at the destination, but animation has drawn a piece there and\r
4552          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4553       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4554     }\r
4555   }\r
4556 \r
4557   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4558   if (num_clips == 0)\r
4559     fullrepaint = TRUE;\r
4560 \r
4561   /* Set clipping on the memory DC */\r
4562   if (!fullrepaint) {\r
4563     SelectClipRgn(hdcmem, clips[0]);\r
4564     for (x = 1; x < num_clips; x++) {\r
4565       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4566         abort();  // this should never ever happen!\r
4567     }\r
4568   }\r
4569 \r
4570   /* Do all the drawing to the memory DC */\r
4571   if(explodeInfo.radius) { // [HGM] atomic\r
4572         HBRUSH oldBrush;\r
4573         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4574         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4575         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4576         x += squareSize/2;\r
4577         y += squareSize/2;\r
4578         if(!fullrepaint) {\r
4579           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4580           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4581         }\r
4582         DrawGridOnDC(hdcmem);\r
4583         DrawHighlightsOnDC(hdcmem);\r
4584         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4585         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4586         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4587         SelectObject(hdcmem, oldBrush);\r
4588   } else {\r
4589     DrawGridOnDC(hdcmem);\r
4590     DrawHighlightsOnDC(hdcmem);\r
4591     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4592   }\r
4593   if(logoHeight) {\r
4594         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4595         if(appData.autoLogo) {\r
4596           \r
4597           switch(gameMode) { // pick logos based on game mode\r
4598             case IcsObserving:\r
4599                 whiteLogo = second.programLogo; // ICS logo\r
4600                 blackLogo = second.programLogo;\r
4601             default:\r
4602                 break;\r
4603             case IcsPlayingWhite:\r
4604                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4605                 blackLogo = second.programLogo; // ICS logo\r
4606                 break;\r
4607             case IcsPlayingBlack:\r
4608                 whiteLogo = second.programLogo; // ICS logo\r
4609                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4610                 break;\r
4611             case TwoMachinesPlay:\r
4612                 if(first.twoMachinesColor[0] == 'b') {\r
4613                     whiteLogo = second.programLogo;\r
4614                     blackLogo = first.programLogo;\r
4615                 }\r
4616                 break;\r
4617             case MachinePlaysWhite:\r
4618                 blackLogo = userLogo;\r
4619                 break;\r
4620             case MachinePlaysBlack:\r
4621                 whiteLogo = userLogo;\r
4622                 blackLogo = first.programLogo;\r
4623           }\r
4624         }\r
4625         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4626         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4627   }\r
4628 \r
4629   if( appData.highlightMoveWithArrow ) {\r
4630     DrawArrowHighlight(hdcmem);\r
4631   }\r
4632 \r
4633   DrawCoordsOnDC(hdcmem);\r
4634 \r
4635   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4636                  /* to make sure lastDrawn contains what is actually drawn */\r
4637 \r
4638   /* Put the dragged piece back into place and draw it (out of place!) */\r
4639     if (dragged_piece != EmptySquare) {\r
4640     /* [HGM] or restack */\r
4641     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4642                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4643     else\r
4644     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4645                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4646     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4647     x = dragInfo.pos.x - squareSize / 2;\r
4648     y = dragInfo.pos.y - squareSize / 2;\r
4649     DrawPieceOnDC(hdcmem, dragged_piece,\r
4650                   ((int) dragged_piece < (int) BlackPawn), \r
4651                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4652   }   \r
4653   \r
4654   /* Put the animated piece back into place and draw it */\r
4655   if (animInfo.piece != EmptySquare) {\r
4656     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4657     x = boardRect.left + animInfo.pos.x;\r
4658     y = boardRect.top + animInfo.pos.y;\r
4659     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4660                   ((int) animInfo.piece < (int) BlackPawn),\r
4661                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4662   }\r
4663 \r
4664   /* Release the bufferBitmap by selecting in the old bitmap \r
4665    * and delete the memory DC\r
4666    */\r
4667   SelectObject(hdcmem, oldBitmap);\r
4668   DeleteDC(hdcmem);\r
4669 \r
4670   /* Set clipping on the target DC */\r
4671   if (!fullrepaint) {\r
4672     SelectClipRgn(hdc, clips[0]);\r
4673     for (x = 1; x < num_clips; x++) {\r
4674       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4675         abort();   // this should never ever happen!\r
4676     } \r
4677   }\r
4678 \r
4679   /* Copy the new bitmap onto the screen in one go.\r
4680    * This way we avoid any flickering\r
4681    */\r
4682   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4683   BitBlt(hdc, boardRect.left, boardRect.top,\r
4684          boardRect.right - boardRect.left,\r
4685          boardRect.bottom - boardRect.top,\r
4686          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4687   if(saveDiagFlag) { \r
4688     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4689     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4690 \r
4691     GetObject(bufferBitmap, sizeof(b), &b);\r
4692     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4693         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4694         bih.biWidth = b.bmWidth;\r
4695         bih.biHeight = b.bmHeight;\r
4696         bih.biPlanes = 1;\r
4697         bih.biBitCount = b.bmBitsPixel;\r
4698         bih.biCompression = 0;\r
4699         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4700         bih.biXPelsPerMeter = 0;\r
4701         bih.biYPelsPerMeter = 0;\r
4702         bih.biClrUsed = 0;\r
4703         bih.biClrImportant = 0;\r
4704 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4705 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4706         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4707 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4708 \r
4709         wb = b.bmWidthBytes;\r
4710         // count colors\r
4711         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4712                 int k = ((int*) pData)[i];\r
4713                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4714                 if(j >= 16) break;\r
4715                 color[j] = k;\r
4716                 if(j >= nrColors) nrColors = j+1;\r
4717         }\r
4718         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4719                 INT p = 0;\r
4720                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4721                     for(w=0; w<(wb>>2); w+=2) {\r
4722                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4723                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4724                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4725                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4726                         pData[p++] = m | j<<4;\r
4727                     }\r
4728                     while(p&3) pData[p++] = 0;\r
4729                 }\r
4730                 fac = 3;\r
4731                 wb = ((wb+31)>>5)<<2;\r
4732         }\r
4733         // write BITMAPFILEHEADER\r
4734         fprintf(diagFile, "BM");\r
4735         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4736         fputDW(diagFile, 0);\r
4737         fputDW(diagFile, 0x36 + (fac?64:0));\r
4738         // write BITMAPINFOHEADER\r
4739         fputDW(diagFile, 40);\r
4740         fputDW(diagFile, b.bmWidth);\r
4741         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4742         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4743         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4744         fputDW(diagFile, 0);\r
4745         fputDW(diagFile, 0);\r
4746         fputDW(diagFile, 0);\r
4747         fputDW(diagFile, 0);\r
4748         fputDW(diagFile, 0);\r
4749         fputDW(diagFile, 0);\r
4750         // write color table\r
4751         if(fac)\r
4752         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4753         // write bitmap data\r
4754         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4755                 fputc(pData[i], diagFile);\r
4756      }\r
4757   }\r
4758 \r
4759   SelectObject(tmphdc, oldBitmap);\r
4760 \r
4761   /* Massive cleanup */\r
4762   for (x = 0; x < num_clips; x++)\r
4763     DeleteObject(clips[x]);\r
4764 \r
4765   DeleteDC(tmphdc);\r
4766   DeleteObject(bufferBitmap);\r
4767 \r
4768   if (releaseDC) \r
4769     ReleaseDC(hwndMain, hdc);\r
4770   \r
4771   if (lastDrawnFlipView != flipView) {\r
4772     if (flipView)\r
4773       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4774     else\r
4775       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4776   }\r
4777 \r
4778 /*  CopyBoard(lastDrawn, board);*/\r
4779   lastDrawnHighlight = highlightInfo;\r
4780   lastDrawnPremove   = premoveHighlightInfo;\r
4781   lastDrawnFlipView = flipView;\r
4782   lastDrawnValid = 1;\r
4783 }\r
4784 \r
4785 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4786 int\r
4787 SaveDiagram(f)\r
4788      FILE *f;\r
4789 {\r
4790     saveDiagFlag = 1; diagFile = f;\r
4791     HDCDrawPosition(NULL, TRUE, NULL);\r
4792 \r
4793     saveDiagFlag = 0;\r
4794 \r
4795 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4796     \r
4797     fclose(f);\r
4798     return TRUE;\r
4799 }\r
4800 \r
4801 \r
4802 /*---------------------------------------------------------------------------*\\r
4803 | CLIENT PAINT PROCEDURE\r
4804 |   This is the main event-handler for the WM_PAINT message.\r
4805 |\r
4806 \*---------------------------------------------------------------------------*/\r
4807 VOID\r
4808 PaintProc(HWND hwnd)\r
4809 {\r
4810   HDC         hdc;\r
4811   PAINTSTRUCT ps;\r
4812   HFONT       oldFont;\r
4813 \r
4814   if((hdc = BeginPaint(hwnd, &ps))) {\r
4815     if (IsIconic(hwnd)) {\r
4816       DrawIcon(hdc, 2, 2, iconCurrent);\r
4817     } else {\r
4818       if (!appData.monoMode) {\r
4819         SelectPalette(hdc, hPal, FALSE);\r
4820         RealizePalette(hdc);\r
4821       }\r
4822       HDCDrawPosition(hdc, 1, NULL);\r
4823       oldFont =\r
4824         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4825       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4826                  ETO_CLIPPED|ETO_OPAQUE,\r
4827                  &messageRect, messageText, strlen(messageText), NULL);\r
4828       SelectObject(hdc, oldFont);\r
4829       DisplayBothClocks();\r
4830     }\r
4831     EndPaint(hwnd,&ps);\r
4832   }\r
4833 \r
4834   return;\r
4835 }\r
4836 \r
4837 \r
4838 /*\r
4839  * If the user selects on a border boundary, return -1; if off the board,\r
4840  *   return -2.  Otherwise map the event coordinate to the square.\r
4841  * The offset boardRect.left or boardRect.top must already have been\r
4842  *   subtracted from x.\r
4843  */\r
4844 int EventToSquare(x, limit)\r
4845      int x;\r
4846 {\r
4847   if (x <= 0)\r
4848     return -2;\r
4849   if (x < lineGap)\r
4850     return -1;\r
4851   x -= lineGap;\r
4852   if ((x % (squareSize + lineGap)) >= squareSize)\r
4853     return -1;\r
4854   x /= (squareSize + lineGap);\r
4855     if (x >= limit)\r
4856     return -2;\r
4857   return x;\r
4858 }\r
4859 \r
4860 typedef struct {\r
4861   char piece;\r
4862   int command;\r
4863   char* name;\r
4864 } DropEnable;\r
4865 \r
4866 DropEnable dropEnables[] = {\r
4867   { 'P', DP_Pawn, "Pawn" },\r
4868   { 'N', DP_Knight, "Knight" },\r
4869   { 'B', DP_Bishop, "Bishop" },\r
4870   { 'R', DP_Rook, "Rook" },\r
4871   { 'Q', DP_Queen, "Queen" },\r
4872 };\r
4873 \r
4874 VOID\r
4875 SetupDropMenu(HMENU hmenu)\r
4876 {\r
4877   int i, count, enable;\r
4878   char *p;\r
4879   extern char white_holding[], black_holding[];\r
4880   char item[MSG_SIZ];\r
4881 \r
4882   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4883     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4884                dropEnables[i].piece);\r
4885     count = 0;\r
4886     while (p && *p++ == dropEnables[i].piece) count++;\r
4887     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4888     enable = count > 0 || !appData.testLegality\r
4889       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4890                       && !appData.icsActive);\r
4891     ModifyMenu(hmenu, dropEnables[i].command,\r
4892                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4893                dropEnables[i].command, item);\r
4894   }\r
4895 }\r
4896 \r
4897 void DragPieceBegin(int x, int y)\r
4898 {\r
4899       dragInfo.lastpos.x = boardRect.left + x;\r
4900       dragInfo.lastpos.y = boardRect.top + y;\r
4901       dragInfo.from.x = fromX;\r
4902       dragInfo.from.y = fromY;\r
4903       dragInfo.start = dragInfo.from;\r
4904       SetCapture(hwndMain);\r
4905 }\r
4906 \r
4907 void DragPieceEnd(int x, int y)\r
4908 {\r
4909     ReleaseCapture();\r
4910     dragInfo.start.x = dragInfo.start.y = -1;\r
4911     dragInfo.from = dragInfo.start;\r
4912     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
4913 }\r
4914 \r
4915 /* Event handler for mouse messages */\r
4916 VOID\r
4917 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4918 {\r
4919   int x, y;\r
4920   POINT pt;\r
4921   static int recursive = 0;\r
4922   HMENU hmenu;\r
4923   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
4924 \r
4925   if (recursive) {\r
4926     if (message == WM_MBUTTONUP) {\r
4927       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
4928          to the middle button: we simulate pressing the left button too!\r
4929          */\r
4930       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
4931       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
4932     }\r
4933     return;\r
4934   }\r
4935   recursive++;\r
4936   \r
4937   pt.x = LOWORD(lParam);\r
4938   pt.y = HIWORD(lParam);\r
4939   x = EventToSquare(pt.x - boardRect.left, BOARD_WIDTH);\r
4940   y = EventToSquare(pt.y - boardRect.top, BOARD_HEIGHT);\r
4941   if (!flipView && y >= 0) {\r
4942     y = BOARD_HEIGHT - 1 - y;\r
4943   }\r
4944   if (flipView && x >= 0) {\r
4945     x = BOARD_WIDTH - 1 - x;\r
4946   }\r
4947 \r
4948   switch (message) {\r
4949   case WM_LBUTTONDOWN:\r
4950       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
4951         if (gameMode == EditPosition) {\r
4952           SetWhiteToPlayEvent();\r
4953         } else if (gameMode == IcsPlayingBlack ||\r
4954                    gameMode == MachinePlaysWhite) {\r
4955           CallFlagEvent();\r
4956         } else if (gameMode == EditGame) {\r
4957           AdjustClock(flipClock, -1);\r
4958         }\r
4959       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
4960         if (gameMode == EditPosition) {\r
4961           SetBlackToPlayEvent();\r
4962         } else if (gameMode == IcsPlayingWhite ||\r
4963                    gameMode == MachinePlaysBlack) {\r
4964           CallFlagEvent();\r
4965         } else if (gameMode == EditGame) {\r
4966           AdjustClock(!flipClock, -1);\r
4967         }\r
4968       }\r
4969       dragInfo.start.x = dragInfo.start.y = -1;\r
4970       dragInfo.from = dragInfo.start;\r
4971     if(fromX == -1 && frozen) { // not sure where this is for\r
4972                 fromX = fromY = -1; \r
4973       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
4974       break;\r
4975     }\r
4976       LeftClick(Press, pt.x - boardRect.left, pt.y - boardRect.top);\r
4977       DrawPosition(TRUE, NULL);\r
4978     break;\r
4979 \r
4980   case WM_LBUTTONUP:\r
4981       LeftClick(Release, pt.x - boardRect.left, pt.y - boardRect.top);\r
4982       DrawPosition(TRUE, NULL);\r
4983     break;\r
4984 \r
4985   case WM_MOUSEMOVE:\r
4986     if ((appData.animateDragging || appData.highlightDragging)\r
4987         && (wParam & MK_LBUTTON)\r
4988         && dragInfo.from.x >= 0) \r
4989     {\r
4990       BOOL full_repaint = FALSE;\r
4991 \r
4992       if (appData.animateDragging) {\r
4993         dragInfo.pos = pt;\r
4994       }\r
4995       if (appData.highlightDragging) {\r
4996         SetHighlights(fromX, fromY, x, y);\r
4997         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
4998             full_repaint = TRUE;\r
4999         }\r
5000       }\r
5001       \r
5002       DrawPosition( full_repaint, NULL);\r
5003       \r
5004       dragInfo.lastpos = dragInfo.pos;\r
5005     }\r
5006     break;\r
5007 \r
5008   case WM_MOUSEWHEEL: // [DM]\r
5009     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5010        /* Mouse Wheel is being rolled forward\r
5011         * Play moves forward\r
5012         */\r
5013        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5014                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5015        /* Mouse Wheel is being rolled backward\r
5016         * Play moves backward\r
5017         */\r
5018        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5019                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5020     }\r
5021     break;\r
5022 \r
5023   case WM_MBUTTONDOWN:\r
5024   case WM_RBUTTONDOWN:\r
5025     ErrorPopDown();\r
5026     ReleaseCapture();\r
5027     fromX = fromY = -1;\r
5028     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5029     dragInfo.start.x = dragInfo.start.y = -1;\r
5030     dragInfo.from = dragInfo.start;\r
5031     dragInfo.lastpos = dragInfo.pos;\r
5032     if (appData.highlightDragging) {\r
5033       ClearHighlights();\r
5034     }\r
5035     if(y == -2) {\r
5036       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5037       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5038           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5039       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5040           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5041       }\r
5042     }\r
5043     DrawPosition(TRUE, NULL);\r
5044 \r
5045     switch (gameMode) {\r
5046     case EditPosition:\r
5047     case IcsExamining:\r
5048       if (x < 0 || y < 0) break;\r
5049       fromX = x;\r
5050       fromY = y;\r
5051       if (message == WM_MBUTTONDOWN) {\r
5052         buttonCount = 3;  /* even if system didn't think so */\r
5053         if (wParam & MK_SHIFT) \r
5054           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5055         else\r
5056           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5057       } else { /* message == WM_RBUTTONDOWN */\r
5058         /* Just have one menu, on the right button.  Windows users don't\r
5059            think to try the middle one, and sometimes other software steals\r
5060            it, or it doesn't really exist. */\r
5061         if(gameInfo.variant != VariantShogi)\r
5062             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5063         else\r
5064             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5065       }\r
5066       break;\r
5067     case IcsPlayingWhite:\r
5068     case IcsPlayingBlack:\r
5069     case EditGame:\r
5070     case MachinePlaysWhite:\r
5071     case MachinePlaysBlack:\r
5072       if (appData.testLegality &&\r
5073           gameInfo.variant != VariantBughouse &&\r
5074           gameInfo.variant != VariantCrazyhouse) break;\r
5075       if (x < 0 || y < 0) break;\r
5076       fromX = x;\r
5077       fromY = y;\r
5078       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5079       SetupDropMenu(hmenu);\r
5080       MenuPopup(hwnd, pt, hmenu, -1);\r
5081       break;\r
5082     default:\r
5083       break;\r
5084     }\r
5085     break;\r
5086   }\r
5087 \r
5088   recursive--;\r
5089 }\r
5090 \r
5091 /* Preprocess messages for buttons in main window */\r
5092 LRESULT CALLBACK\r
5093 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5094 {\r
5095   int id = GetWindowLong(hwnd, GWL_ID);\r
5096   int i, dir;\r
5097 \r
5098   for (i=0; i<N_BUTTONS; i++) {\r
5099     if (buttonDesc[i].id == id) break;\r
5100   }\r
5101   if (i == N_BUTTONS) return 0;\r
5102   switch (message) {\r
5103   case WM_KEYDOWN:\r
5104     switch (wParam) {\r
5105     case VK_LEFT:\r
5106     case VK_RIGHT:\r
5107       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5108       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5109       return TRUE;\r
5110     }\r
5111     break;\r
5112   case WM_CHAR:\r
5113     switch (wParam) {\r
5114     case '\r':\r
5115       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5116       return TRUE;\r
5117     default:\r
5118       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5119         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5120         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5121         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5122         SetFocus(h);\r
5123         SendMessage(h, WM_CHAR, wParam, lParam);\r
5124         return TRUE;\r
5125       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5126         PopUpMoveDialog((char)wParam);\r
5127       }\r
5128       break;\r
5129     }\r
5130     break;\r
5131   }\r
5132   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5133 }\r
5134 \r
5135 /* Process messages for Promotion dialog box */\r
5136 LRESULT CALLBACK\r
5137 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5138 {\r
5139   char promoChar;\r
5140 \r
5141   switch (message) {\r
5142   case WM_INITDIALOG: /* message: initialize dialog box */\r
5143     /* Center the dialog over the application window */\r
5144     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5145     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5146       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5147        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5148                SW_SHOW : SW_HIDE);\r
5149     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5150     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5151        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5152          PieceToChar(WhiteAngel) != '~') ||\r
5153         (PieceToChar(BlackAngel) >= 'A' &&\r
5154          PieceToChar(BlackAngel) != '~')   ) ?\r
5155                SW_SHOW : SW_HIDE);\r
5156     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5157        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5158          PieceToChar(WhiteMarshall) != '~') ||\r
5159         (PieceToChar(BlackMarshall) >= 'A' &&\r
5160          PieceToChar(BlackMarshall) != '~')   ) ?\r
5161                SW_SHOW : SW_HIDE);\r
5162     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5163     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5164        gameInfo.variant != VariantShogi ?\r
5165                SW_SHOW : SW_HIDE);\r
5166     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5167        gameInfo.variant != VariantShogi ?\r
5168                SW_SHOW : SW_HIDE);\r
5169     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5170        gameInfo.variant == VariantShogi ?\r
5171                SW_SHOW : SW_HIDE);\r
5172     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5173        gameInfo.variant == VariantShogi ?\r
5174                SW_SHOW : SW_HIDE);\r
5175     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5176        gameInfo.variant == VariantSuper ?\r
5177                SW_SHOW : SW_HIDE);\r
5178     return TRUE;\r
5179 \r
5180   case WM_COMMAND: /* message: received a command */\r
5181     switch (LOWORD(wParam)) {\r
5182     case IDCANCEL:\r
5183       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5184       ClearHighlights();\r
5185       DrawPosition(FALSE, NULL);\r
5186       return TRUE;\r
5187     case PB_King:\r
5188       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5189       break;\r
5190     case PB_Queen:\r
5191       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5192       break;\r
5193     case PB_Rook:\r
5194       promoChar = PieceToChar(BlackRook);\r
5195       break;\r
5196     case PB_Bishop:\r
5197       promoChar = PieceToChar(BlackBishop);\r
5198       break;\r
5199     case PB_Chancellor:\r
5200       promoChar = PieceToChar(BlackMarshall);\r
5201       break;\r
5202     case PB_Archbishop:\r
5203       promoChar = PieceToChar(BlackAngel);\r
5204       break;\r
5205     case PB_Knight:\r
5206       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5207       break;\r
5208     default:\r
5209       return FALSE;\r
5210     }\r
5211     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5212     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5213        only show the popup when we are already sure the move is valid or\r
5214        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5215        will figure out it is a promotion from the promoChar. */\r
5216     UserMoveEvent(fromX, fromY, toX, toY, promoChar);\r
5217     fromX = fromY = -1;\r
5218     if (!appData.highlightLastMove) {\r
5219       ClearHighlights();\r
5220       DrawPosition(FALSE, NULL);\r
5221     }\r
5222     return TRUE;\r
5223   }\r
5224   return FALSE;\r
5225 }\r
5226 \r
5227 /* Pop up promotion dialog */\r
5228 VOID\r
5229 PromotionPopup(HWND hwnd)\r
5230 {\r
5231   FARPROC lpProc;\r
5232 \r
5233   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5234   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5235     hwnd, (DLGPROC)lpProc);\r
5236   FreeProcInstance(lpProc);\r
5237 }\r
5238 \r
5239 void\r
5240 PromotionPopUp()\r
5241 {\r
5242   DrawPosition(TRUE, NULL);\r
5243   PromotionPopup(hwndMain);\r
5244 }\r
5245 \r
5246 /* Toggle ShowThinking */\r
5247 VOID\r
5248 ToggleShowThinking()\r
5249 {\r
5250   appData.showThinking = !appData.showThinking;\r
5251   ShowThinkingEvent();\r
5252 }\r
5253 \r
5254 VOID\r
5255 LoadGameDialog(HWND hwnd, char* title)\r
5256 {\r
5257   UINT number = 0;\r
5258   FILE *f;\r
5259   char fileTitle[MSG_SIZ];\r
5260   f = OpenFileDialog(hwnd, "rb", "",\r
5261                      appData.oldSaveStyle ? "gam" : "pgn",\r
5262                      GAME_FILT,\r
5263                      title, &number, fileTitle, NULL);\r
5264   if (f != NULL) {\r
5265     cmailMsgLoaded = FALSE;\r
5266     if (number == 0) {\r
5267       int error = GameListBuild(f);\r
5268       if (error) {\r
5269         DisplayError("Cannot build game list", error);\r
5270       } else if (!ListEmpty(&gameList) &&\r
5271                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5272         GameListPopUp(f, fileTitle);\r
5273         return;\r
5274       }\r
5275       GameListDestroy();\r
5276       number = 1;\r
5277     }\r
5278     LoadGame(f, number, fileTitle, FALSE);\r
5279   }\r
5280 }\r
5281 \r
5282 int get_term_width()\r
5283 {\r
5284     HDC hdc;\r
5285     TEXTMETRIC tm;\r
5286     RECT rc;\r
5287     HFONT hfont, hold_font;\r
5288     LOGFONT lf;\r
5289     HWND hText;\r
5290 \r
5291     if (hwndConsole)\r
5292         hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5293     else\r
5294         return 79;\r
5295 \r
5296     // get the text metrics\r
5297     hdc = GetDC(hText);\r
5298     lf = font[boardSize][CONSOLE_FONT]->lf;\r
5299     if (consoleCF.dwEffects & CFE_BOLD)\r
5300         lf.lfWeight = FW_BOLD;\r
5301     if (consoleCF.dwEffects & CFE_ITALIC)\r
5302         lf.lfItalic = TRUE;\r
5303     if (consoleCF.dwEffects & CFE_STRIKEOUT)\r
5304         lf.lfStrikeOut = TRUE;\r
5305     if (consoleCF.dwEffects & CFE_UNDERLINE)\r
5306         lf.lfUnderline = TRUE;\r
5307     hfont = CreateFontIndirect(&lf);\r
5308     hold_font = SelectObject(hdc, hfont);\r
5309     GetTextMetrics(hdc, &tm);\r
5310     SelectObject(hdc, hold_font);\r
5311     DeleteObject(hfont);\r
5312     ReleaseDC(hText, hdc);\r
5313 \r
5314     // get the rectangle\r
5315     SendMessage(hText, EM_GETRECT, 0, (LPARAM)&rc);\r
5316 \r
5317     return (rc.right-rc.left) / tm.tmAveCharWidth;\r
5318 }\r
5319 \r
5320 void UpdateICSWidth(HWND hText)\r
5321 {\r
5322     LONG old_width, new_width;\r
5323 \r
5324     new_width = get_term_width(hText, FALSE);\r
5325     old_width = GetWindowLong(hText, GWL_USERDATA);\r
5326     if (new_width != old_width)\r
5327     {\r
5328         ics_update_width(new_width);\r
5329         SetWindowLong(hText, GWL_USERDATA, new_width);\r
5330     }\r
5331 }\r
5332 \r
5333 VOID\r
5334 ChangedConsoleFont()\r
5335 {\r
5336   CHARFORMAT cfmt;\r
5337   CHARRANGE tmpsel, sel;\r
5338   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5339   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5340   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5341   PARAFORMAT paraf;\r
5342 \r
5343   cfmt.cbSize = sizeof(CHARFORMAT);\r
5344   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5345   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5346   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5347    * size.  This was undocumented in the version of MSVC++ that I had\r
5348    * when I wrote the code, but is apparently documented now.\r
5349    */\r
5350   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5351   cfmt.bCharSet = f->lf.lfCharSet;\r
5352   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5353   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5354   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5355   /* Why are the following seemingly needed too? */\r
5356   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5357   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5358   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5359   tmpsel.cpMin = 0;\r
5360   tmpsel.cpMax = -1; /*999999?*/\r
5361   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5362   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5363   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5364    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5365    */\r
5366   paraf.cbSize = sizeof(paraf);\r
5367   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5368   paraf.dxStartIndent = 0;\r
5369   paraf.dxOffset = WRAP_INDENT;\r
5370   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5371   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5372   UpdateICSWidth(hText);\r
5373 }\r
5374 \r
5375 /*---------------------------------------------------------------------------*\\r
5376  *\r
5377  * Window Proc for main window\r
5378  *\r
5379 \*---------------------------------------------------------------------------*/\r
5380 \r
5381 /* Process messages for main window, etc. */\r
5382 LRESULT CALLBACK\r
5383 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5384 {\r
5385   FARPROC lpProc;\r
5386   int wmId, wmEvent;\r
5387   char *defName;\r
5388   FILE *f;\r
5389   UINT number;\r
5390   char fileTitle[MSG_SIZ];\r
5391   char buf[MSG_SIZ];\r
5392   static SnapData sd;\r
5393 \r
5394   switch (message) {\r
5395 \r
5396   case WM_PAINT: /* message: repaint portion of window */\r
5397     PaintProc(hwnd);\r
5398     break;\r
5399 \r
5400   case WM_ERASEBKGND:\r
5401     if (IsIconic(hwnd)) {\r
5402       /* Cheat; change the message */\r
5403       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5404     } else {\r
5405       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5406     }\r
5407     break;\r
5408 \r
5409   case WM_LBUTTONDOWN:\r
5410   case WM_MBUTTONDOWN:\r
5411   case WM_RBUTTONDOWN:\r
5412   case WM_LBUTTONUP:\r
5413   case WM_MBUTTONUP:\r
5414   case WM_RBUTTONUP:\r
5415   case WM_MOUSEMOVE:\r
5416   case WM_MOUSEWHEEL:\r
5417     MouseEvent(hwnd, message, wParam, lParam);\r
5418     break;\r
5419 \r
5420   JAWS_KB_NAVIGATION\r
5421 \r
5422   case WM_CHAR:\r
5423     \r
5424     JAWS_ALT_INTERCEPT\r
5425 \r
5426     if (appData.icsActive && (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9')) { \r
5427         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5428         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5429         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5430         SetFocus(h);\r
5431         SendMessage(h, message, wParam, lParam);\r
5432     } else if(lParam != KF_REPEAT) {\r
5433         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5434                 PopUpMoveDialog((char)wParam);\r
5435         } else if((char)wParam == 003) CopyGameToClipboard();\r
5436          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5437     }\r
5438 \r
5439     break;\r
5440 \r
5441   case WM_PALETTECHANGED:\r
5442     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5443       int nnew;\r
5444       HDC hdc = GetDC(hwndMain);\r
5445       SelectPalette(hdc, hPal, TRUE);\r
5446       nnew = RealizePalette(hdc);\r
5447       if (nnew > 0) {\r
5448         paletteChanged = TRUE;\r
5449         InvalidateRect(hwnd, &boardRect, FALSE);\r
5450       }\r
5451       ReleaseDC(hwnd, hdc);\r
5452     }\r
5453     break;\r
5454 \r
5455   case WM_QUERYNEWPALETTE:\r
5456     if (!appData.monoMode /*&& paletteChanged*/) {\r
5457       int nnew;\r
5458       HDC hdc = GetDC(hwndMain);\r
5459       paletteChanged = FALSE;\r
5460       SelectPalette(hdc, hPal, FALSE);\r
5461       nnew = RealizePalette(hdc);\r
5462       if (nnew > 0) {\r
5463         InvalidateRect(hwnd, &boardRect, FALSE);\r
5464       }\r
5465       ReleaseDC(hwnd, hdc);\r
5466       return TRUE;\r
5467     }\r
5468     return FALSE;\r
5469 \r
5470   case WM_COMMAND: /* message: command from application menu */\r
5471     wmId    = LOWORD(wParam);\r
5472     wmEvent = HIWORD(wParam);\r
5473 \r
5474     switch (wmId) {\r
5475     case IDM_NewGame:\r
5476       ResetGameEvent();\r
5477       SAY("new game enter a move to play against the computer with white");\r
5478       break;\r
5479 \r
5480     case IDM_NewGameFRC:\r
5481       if( NewGameFRC() == 0 ) {\r
5482         ResetGameEvent();\r
5483       }\r
5484       break;\r
5485 \r
5486     case IDM_NewVariant:\r
5487       NewVariantPopup(hwnd);\r
5488       break;\r
5489 \r
5490     case IDM_LoadGame:\r
5491       LoadGameDialog(hwnd, "Load Game from File");\r
5492       break;\r
5493 \r
5494     case IDM_LoadNextGame:\r
5495       ReloadGame(1);\r
5496       break;\r
5497 \r
5498     case IDM_LoadPrevGame:\r
5499       ReloadGame(-1);\r
5500       break;\r
5501 \r
5502     case IDM_ReloadGame:\r
5503       ReloadGame(0);\r
5504       break;\r
5505 \r
5506     case IDM_LoadPosition:\r
5507       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5508         Reset(FALSE, TRUE);\r
5509       }\r
5510       number = 1;\r
5511       f = OpenFileDialog(hwnd, "rb", "",\r
5512                          appData.oldSaveStyle ? "pos" : "fen",\r
5513                          POSITION_FILT,\r
5514                          "Load Position from File", &number, fileTitle, NULL);\r
5515       if (f != NULL) {\r
5516         LoadPosition(f, number, fileTitle);\r
5517       }\r
5518       break;\r
5519 \r
5520     case IDM_LoadNextPosition:\r
5521       ReloadPosition(1);\r
5522       break;\r
5523 \r
5524     case IDM_LoadPrevPosition:\r
5525       ReloadPosition(-1);\r
5526       break;\r
5527 \r
5528     case IDM_ReloadPosition:\r
5529       ReloadPosition(0);\r
5530       break;\r
5531 \r
5532     case IDM_SaveGame:\r
5533       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5534       f = OpenFileDialog(hwnd, "a", defName,\r
5535                          appData.oldSaveStyle ? "gam" : "pgn",\r
5536                          GAME_FILT,\r
5537                          "Save Game to File", NULL, fileTitle, NULL);\r
5538       if (f != NULL) {\r
5539         SaveGame(f, 0, "");\r
5540       }\r
5541       break;\r
5542 \r
5543     case IDM_SavePosition:\r
5544       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5545       f = OpenFileDialog(hwnd, "a", defName,\r
5546                          appData.oldSaveStyle ? "pos" : "fen",\r
5547                          POSITION_FILT,\r
5548                          "Save Position to File", NULL, fileTitle, NULL);\r
5549       if (f != NULL) {\r
5550         SavePosition(f, 0, "");\r
5551       }\r
5552       break;\r
5553 \r
5554     case IDM_SaveDiagram:\r
5555       defName = "diagram";\r
5556       f = OpenFileDialog(hwnd, "wb", defName,\r
5557                          "bmp",\r
5558                          DIAGRAM_FILT,\r
5559                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5560       if (f != NULL) {\r
5561         SaveDiagram(f);\r
5562       }\r
5563       break;\r
5564 \r
5565     case IDM_CopyGame:\r
5566       CopyGameToClipboard();\r
5567       break;\r
5568 \r
5569     case IDM_PasteGame:\r
5570       PasteGameFromClipboard();\r
5571       break;\r
5572 \r
5573     case IDM_CopyGameListToClipboard:\r
5574       CopyGameListToClipboard();\r
5575       break;\r
5576 \r
5577     /* [AS] Autodetect FEN or PGN data */\r
5578     case IDM_PasteAny:\r
5579       PasteGameOrFENFromClipboard();\r
5580       break;\r
5581 \r
5582     /* [AS] Move history */\r
5583     case IDM_ShowMoveHistory:\r
5584         if( MoveHistoryIsUp() ) {\r
5585             MoveHistoryPopDown();\r
5586         }\r
5587         else {\r
5588             MoveHistoryPopUp();\r
5589         }\r
5590         break;\r
5591 \r
5592     /* [AS] Eval graph */\r
5593     case IDM_ShowEvalGraph:\r
5594         if( EvalGraphIsUp() ) {\r
5595             EvalGraphPopDown();\r
5596         }\r
5597         else {\r
5598             EvalGraphPopUp();\r
5599             SetFocus(hwndMain);\r
5600         }\r
5601         break;\r
5602 \r
5603     /* [AS] Engine output */\r
5604     case IDM_ShowEngineOutput:\r
5605         if( EngineOutputIsUp() ) {\r
5606             EngineOutputPopDown();\r
5607         }\r
5608         else {\r
5609             EngineOutputPopUp();\r
5610         }\r
5611         break;\r
5612 \r
5613     /* [AS] User adjudication */\r
5614     case IDM_UserAdjudication_White:\r
5615         UserAdjudicationEvent( +1 );\r
5616         break;\r
5617 \r
5618     case IDM_UserAdjudication_Black:\r
5619         UserAdjudicationEvent( -1 );\r
5620         break;\r
5621 \r
5622     case IDM_UserAdjudication_Draw:\r
5623         UserAdjudicationEvent( 0 );\r
5624         break;\r
5625 \r
5626     /* [AS] Game list options dialog */\r
5627     case IDM_GameListOptions:\r
5628       GameListOptions();\r
5629       break;\r
5630 \r
5631     case IDM_NewChat:\r
5632       ChatPopUp();\r
5633       break;\r
5634 \r
5635     case IDM_CopyPosition:\r
5636       CopyFENToClipboard();\r
5637       break;\r
5638 \r
5639     case IDM_PastePosition:\r
5640       PasteFENFromClipboard();\r
5641       break;\r
5642 \r
5643     case IDM_MailMove:\r
5644       MailMoveEvent();\r
5645       break;\r
5646 \r
5647     case IDM_ReloadCMailMsg:\r
5648       Reset(TRUE, TRUE);\r
5649       ReloadCmailMsgEvent(FALSE);\r
5650       break;\r
5651 \r
5652     case IDM_Minimize:\r
5653       ShowWindow(hwnd, SW_MINIMIZE);\r
5654       break;\r
5655 \r
5656     case IDM_Exit:\r
5657       ExitEvent(0);\r
5658       break;\r
5659 \r
5660     case IDM_MachineWhite:\r
5661       MachineWhiteEvent();\r
5662       /*\r
5663        * refresh the tags dialog only if it's visible\r
5664        */\r
5665       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5666           char *tags;\r
5667           tags = PGNTags(&gameInfo);\r
5668           TagsPopUp(tags, CmailMsg());\r
5669           free(tags);\r
5670       }\r
5671       SAY("computer starts playing white");\r
5672       break;\r
5673 \r
5674     case IDM_MachineBlack:\r
5675       MachineBlackEvent();\r
5676       /*\r
5677        * refresh the tags dialog only if it's visible\r
5678        */\r
5679       if (gameMode == MachinePlaysBlack && 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 black");\r
5686       break;\r
5687 \r
5688     case IDM_TwoMachines:\r
5689       TwoMachinesEvent();\r
5690       /*\r
5691        * refresh the tags dialog only if it's visible\r
5692        */\r
5693       if (gameMode == TwoMachinesPlay && 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("programs start playing each other");\r
5700       break;\r
5701 \r
5702     case IDM_AnalysisMode:\r
5703       if (!first.analysisSupport) {\r
5704         sprintf(buf, "%s does not support analysis", first.tidy);\r
5705         DisplayError(buf, 0);\r
5706       } else {\r
5707         SAY("analyzing current position");\r
5708         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5709         if (appData.icsActive) {\r
5710                if (gameMode != IcsObserving) {\r
5711                        sprintf(buf, "You are not observing a game");\r
5712                        DisplayError(buf, 0);\r
5713                        /* secure check */\r
5714                        if (appData.icsEngineAnalyze) {\r
5715                                if (appData.debugMode) \r
5716                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5717                                ExitAnalyzeMode();\r
5718                                ModeHighlight();\r
5719                                break;\r
5720                        }\r
5721                        break;\r
5722                } else {\r
5723                        /* if enable, user want disable icsEngineAnalyze */\r
5724                        if (appData.icsEngineAnalyze) {\r
5725                                ExitAnalyzeMode();\r
5726                                ModeHighlight();\r
5727                                break;\r
5728                        }\r
5729                        appData.icsEngineAnalyze = TRUE;\r
5730                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5731                }\r
5732         } \r
5733         if (!appData.showThinking) ToggleShowThinking();\r
5734         AnalyzeModeEvent();\r
5735       }\r
5736       break;\r
5737 \r
5738     case IDM_AnalyzeFile:\r
5739       if (!first.analysisSupport) {\r
5740         char buf[MSG_SIZ];\r
5741         sprintf(buf, "%s does not support analysis", first.tidy);\r
5742         DisplayError(buf, 0);\r
5743       } else {\r
5744         if (!appData.showThinking) ToggleShowThinking();\r
5745         AnalyzeFileEvent();\r
5746         LoadGameDialog(hwnd, "Analyze Game from File");\r
5747         AnalysisPeriodicEvent(1);\r
5748       }\r
5749       break;\r
5750 \r
5751     case IDM_IcsClient:\r
5752       IcsClientEvent();\r
5753       break;\r
5754 \r
5755     case IDM_EditGame:\r
5756       EditGameEvent();\r
5757       SAY("edit game");\r
5758       break;\r
5759 \r
5760     case IDM_EditPosition:\r
5761       EditPositionEvent();\r
5762       SAY("to set up a position type a FEN");\r
5763       break;\r
5764 \r
5765     case IDM_Training:\r
5766       TrainingEvent();\r
5767       break;\r
5768 \r
5769     case IDM_ShowGameList:\r
5770       ShowGameListProc();\r
5771       break;\r
5772 \r
5773     case IDM_EditTags:\r
5774       EditTagsProc();\r
5775       break;\r
5776 \r
5777     case IDM_EditComment:\r
5778       if (commentDialogUp && editComment) {\r
5779         CommentPopDown();\r
5780       } else {\r
5781         EditCommentEvent();\r
5782       }\r
5783       break;\r
5784 \r
5785     case IDM_Pause:\r
5786       PauseEvent();\r
5787       break;\r
5788 \r
5789     case IDM_Accept:\r
5790       AcceptEvent();\r
5791       break;\r
5792 \r
5793     case IDM_Decline:\r
5794       DeclineEvent();\r
5795       break;\r
5796 \r
5797     case IDM_Rematch:\r
5798       RematchEvent();\r
5799       break;\r
5800 \r
5801     case IDM_CallFlag:\r
5802       CallFlagEvent();\r
5803       break;\r
5804 \r
5805     case IDM_Draw:\r
5806       DrawEvent();\r
5807       break;\r
5808 \r
5809     case IDM_Adjourn:\r
5810       AdjournEvent();\r
5811       break;\r
5812 \r
5813     case IDM_Abort:\r
5814       AbortEvent();\r
5815       break;\r
5816 \r
5817     case IDM_Resign:\r
5818       ResignEvent();\r
5819       break;\r
5820 \r
5821     case IDM_StopObserving:\r
5822       StopObservingEvent();\r
5823       break;\r
5824 \r
5825     case IDM_StopExamining:\r
5826       StopExaminingEvent();\r
5827       break;\r
5828 \r
5829     case IDM_TypeInMove:\r
5830       PopUpMoveDialog('\000');\r
5831       break;\r
5832 \r
5833     case IDM_TypeInName:\r
5834       PopUpNameDialog('\000');\r
5835       break;\r
5836 \r
5837     case IDM_Backward:\r
5838       BackwardEvent();\r
5839       SetFocus(hwndMain);\r
5840       break;\r
5841 \r
5842     JAWS_MENU_ITEMS\r
5843 \r
5844     case IDM_Forward:\r
5845       ForwardEvent();\r
5846       SetFocus(hwndMain);\r
5847       break;\r
5848 \r
5849     case IDM_ToStart:\r
5850       ToStartEvent();\r
5851       SetFocus(hwndMain);\r
5852       break;\r
5853 \r
5854     case IDM_ToEnd:\r
5855       ToEndEvent();\r
5856       SetFocus(hwndMain);\r
5857       break;\r
5858 \r
5859     case IDM_Revert:\r
5860       RevertEvent();\r
5861       break;\r
5862 \r
5863     case IDM_TruncateGame:\r
5864       TruncateGameEvent();\r
5865       break;\r
5866 \r
5867     case IDM_MoveNow:\r
5868       MoveNowEvent();\r
5869       break;\r
5870 \r
5871     case IDM_RetractMove:\r
5872       RetractMoveEvent();\r
5873       break;\r
5874 \r
5875     case IDM_FlipView:\r
5876       flipView = !flipView;\r
5877       DrawPosition(FALSE, NULL);\r
5878       break;\r
5879 \r
5880     case IDM_FlipClock:\r
5881       flipClock = !flipClock;\r
5882       DisplayBothClocks();\r
5883       DrawPosition(FALSE, NULL);\r
5884       break;\r
5885 \r
5886     case IDM_MuteSounds:\r
5887       mute = !mute; // [HGM] mute: keep track of global muting variable\r
5888       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
5889                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
5890       break;\r
5891 \r
5892     case IDM_GeneralOptions:\r
5893       GeneralOptionsPopup(hwnd);\r
5894       DrawPosition(TRUE, NULL);\r
5895       break;\r
5896 \r
5897     case IDM_BoardOptions:\r
5898       BoardOptionsPopup(hwnd);\r
5899       break;\r
5900 \r
5901     case IDM_EnginePlayOptions:\r
5902       EnginePlayOptionsPopup(hwnd);\r
5903       break;\r
5904 \r
5905     case IDM_Engine1Options:\r
5906       EngineOptionsPopup(hwnd, &first);\r
5907       break;\r
5908 \r
5909     case IDM_Engine2Options:\r
5910       EngineOptionsPopup(hwnd, &second);\r
5911       break;\r
5912 \r
5913     case IDM_OptionsUCI:\r
5914       UciOptionsPopup(hwnd);\r
5915       break;\r
5916 \r
5917     case IDM_IcsOptions:\r
5918       IcsOptionsPopup(hwnd);\r
5919       break;\r
5920 \r
5921     case IDM_Fonts:\r
5922       FontsOptionsPopup(hwnd);\r
5923       break;\r
5924 \r
5925     case IDM_Sounds:\r
5926       SoundOptionsPopup(hwnd);\r
5927       break;\r
5928 \r
5929     case IDM_CommPort:\r
5930       CommPortOptionsPopup(hwnd);\r
5931       break;\r
5932 \r
5933     case IDM_LoadOptions:\r
5934       LoadOptionsPopup(hwnd);\r
5935       break;\r
5936 \r
5937     case IDM_SaveOptions:\r
5938       SaveOptionsPopup(hwnd);\r
5939       break;\r
5940 \r
5941     case IDM_TimeControl:\r
5942       TimeControlOptionsPopup(hwnd);\r
5943       break;\r
5944 \r
5945     case IDM_SaveSettings:\r
5946       SaveSettings(settingsFileName);\r
5947       break;\r
5948 \r
5949     case IDM_SaveSettingsOnExit:\r
5950       saveSettingsOnExit = !saveSettingsOnExit;\r
5951       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
5952                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
5953                                          MF_CHECKED : MF_UNCHECKED));\r
5954       break;\r
5955 \r
5956     case IDM_Hint:\r
5957       HintEvent();\r
5958       break;\r
5959 \r
5960     case IDM_Book:\r
5961       BookEvent();\r
5962       break;\r
5963 \r
5964     case IDM_AboutGame:\r
5965       AboutGameEvent();\r
5966       break;\r
5967 \r
5968     case IDM_Debug:\r
5969       appData.debugMode = !appData.debugMode;\r
5970       if (appData.debugMode) {\r
5971         char dir[MSG_SIZ];\r
5972         GetCurrentDirectory(MSG_SIZ, dir);\r
5973         SetCurrentDirectory(installDir);\r
5974         debugFP = fopen(appData.nameOfDebugFile, "w");\r
5975         SetCurrentDirectory(dir);\r
5976         setbuf(debugFP, NULL);\r
5977       } else {\r
5978         fclose(debugFP);\r
5979         debugFP = NULL;\r
5980       }\r
5981       break;\r
5982 \r
5983     case IDM_HELPCONTENTS:\r
5984       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
5985           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
5986           MessageBox (GetFocus(),\r
5987                     "Unable to activate help",\r
5988                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5989       }\r
5990       break;\r
5991 \r
5992     case IDM_HELPSEARCH:\r
5993         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
5994             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
5995         MessageBox (GetFocus(),\r
5996                     "Unable to activate help",\r
5997                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
5998       }\r
5999       break;\r
6000 \r
6001     case IDM_HELPHELP:\r
6002       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6003         MessageBox (GetFocus(),\r
6004                     "Unable to activate help",\r
6005                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6006       }\r
6007       break;\r
6008 \r
6009     case IDM_ABOUT:\r
6010       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6011       DialogBox(hInst, \r
6012         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6013         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6014       FreeProcInstance(lpProc);\r
6015       break;\r
6016 \r
6017     case IDM_DirectCommand1:\r
6018       AskQuestionEvent("Direct Command",\r
6019                        "Send to chess program:", "", "1");\r
6020       break;\r
6021     case IDM_DirectCommand2:\r
6022       AskQuestionEvent("Direct Command",\r
6023                        "Send to second chess program:", "", "2");\r
6024       break;\r
6025 \r
6026     case EP_WhitePawn:\r
6027       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6028       fromX = fromY = -1;\r
6029       break;\r
6030 \r
6031     case EP_WhiteKnight:\r
6032       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6033       fromX = fromY = -1;\r
6034       break;\r
6035 \r
6036     case EP_WhiteBishop:\r
6037       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6038       fromX = fromY = -1;\r
6039       break;\r
6040 \r
6041     case EP_WhiteRook:\r
6042       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6043       fromX = fromY = -1;\r
6044       break;\r
6045 \r
6046     case EP_WhiteQueen:\r
6047       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6048       fromX = fromY = -1;\r
6049       break;\r
6050 \r
6051     case EP_WhiteFerz:\r
6052       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6053       fromX = fromY = -1;\r
6054       break;\r
6055 \r
6056     case EP_WhiteWazir:\r
6057       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6058       fromX = fromY = -1;\r
6059       break;\r
6060 \r
6061     case EP_WhiteAlfil:\r
6062       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6063       fromX = fromY = -1;\r
6064       break;\r
6065 \r
6066     case EP_WhiteCannon:\r
6067       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6068       fromX = fromY = -1;\r
6069       break;\r
6070 \r
6071     case EP_WhiteCardinal:\r
6072       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6073       fromX = fromY = -1;\r
6074       break;\r
6075 \r
6076     case EP_WhiteMarshall:\r
6077       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6078       fromX = fromY = -1;\r
6079       break;\r
6080 \r
6081     case EP_WhiteKing:\r
6082       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6083       fromX = fromY = -1;\r
6084       break;\r
6085 \r
6086     case EP_BlackPawn:\r
6087       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6088       fromX = fromY = -1;\r
6089       break;\r
6090 \r
6091     case EP_BlackKnight:\r
6092       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6093       fromX = fromY = -1;\r
6094       break;\r
6095 \r
6096     case EP_BlackBishop:\r
6097       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6098       fromX = fromY = -1;\r
6099       break;\r
6100 \r
6101     case EP_BlackRook:\r
6102       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6103       fromX = fromY = -1;\r
6104       break;\r
6105 \r
6106     case EP_BlackQueen:\r
6107       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6108       fromX = fromY = -1;\r
6109       break;\r
6110 \r
6111     case EP_BlackFerz:\r
6112       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6113       fromX = fromY = -1;\r
6114       break;\r
6115 \r
6116     case EP_BlackWazir:\r
6117       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6118       fromX = fromY = -1;\r
6119       break;\r
6120 \r
6121     case EP_BlackAlfil:\r
6122       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6123       fromX = fromY = -1;\r
6124       break;\r
6125 \r
6126     case EP_BlackCannon:\r
6127       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6128       fromX = fromY = -1;\r
6129       break;\r
6130 \r
6131     case EP_BlackCardinal:\r
6132       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6133       fromX = fromY = -1;\r
6134       break;\r
6135 \r
6136     case EP_BlackMarshall:\r
6137       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6138       fromX = fromY = -1;\r
6139       break;\r
6140 \r
6141     case EP_BlackKing:\r
6142       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6143       fromX = fromY = -1;\r
6144       break;\r
6145 \r
6146     case EP_EmptySquare:\r
6147       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6148       fromX = fromY = -1;\r
6149       break;\r
6150 \r
6151     case EP_ClearBoard:\r
6152       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6153       fromX = fromY = -1;\r
6154       break;\r
6155 \r
6156     case EP_White:\r
6157       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6158       fromX = fromY = -1;\r
6159       break;\r
6160 \r
6161     case EP_Black:\r
6162       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6163       fromX = fromY = -1;\r
6164       break;\r
6165 \r
6166     case EP_Promote:\r
6167       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6168       fromX = fromY = -1;\r
6169       break;\r
6170 \r
6171     case EP_Demote:\r
6172       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6173       fromX = fromY = -1;\r
6174       break;\r
6175 \r
6176     case DP_Pawn:\r
6177       DropMenuEvent(WhitePawn, fromX, fromY);\r
6178       fromX = fromY = -1;\r
6179       break;\r
6180 \r
6181     case DP_Knight:\r
6182       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6183       fromX = fromY = -1;\r
6184       break;\r
6185 \r
6186     case DP_Bishop:\r
6187       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6188       fromX = fromY = -1;\r
6189       break;\r
6190 \r
6191     case DP_Rook:\r
6192       DropMenuEvent(WhiteRook, fromX, fromY);\r
6193       fromX = fromY = -1;\r
6194       break;\r
6195 \r
6196     case DP_Queen:\r
6197       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6198       fromX = fromY = -1;\r
6199       break;\r
6200 \r
6201     default:\r
6202       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6203     }\r
6204     break;\r
6205 \r
6206   case WM_TIMER:\r
6207     switch (wParam) {\r
6208     case CLOCK_TIMER_ID:\r
6209       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6210       clockTimerEvent = 0;\r
6211       DecrementClocks(); /* call into back end */\r
6212       break;\r
6213     case LOAD_GAME_TIMER_ID:\r
6214       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6215       loadGameTimerEvent = 0;\r
6216       AutoPlayGameLoop(); /* call into back end */\r
6217       break;\r
6218     case ANALYSIS_TIMER_ID:\r
6219       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6220                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6221         AnalysisPeriodicEvent(0);\r
6222       } else {\r
6223         KillTimer(hwnd, analysisTimerEvent);\r
6224         analysisTimerEvent = 0;\r
6225       }\r
6226       break;\r
6227     case DELAYED_TIMER_ID:\r
6228       KillTimer(hwnd, delayedTimerEvent);\r
6229       delayedTimerEvent = 0;\r
6230       delayedTimerCallback();\r
6231       break;\r
6232     }\r
6233     break;\r
6234 \r
6235   case WM_USER_Input:\r
6236     InputEvent(hwnd, message, wParam, lParam);\r
6237     break;\r
6238 \r
6239   /* [AS] Also move "attached" child windows */\r
6240   case WM_WINDOWPOSCHANGING:\r
6241 \r
6242     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6243         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6244 \r
6245         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6246             /* Window is moving */\r
6247             RECT rcMain;\r
6248 \r
6249 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6250             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6251             rcMain.right  = boardX + winWidth;\r
6252             rcMain.top    = boardY;\r
6253             rcMain.bottom = boardY + winHeight;\r
6254             \r
6255             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6256             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6257             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6258             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6259             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6260             boardX = lpwp->x;\r
6261             boardY = lpwp->y;\r
6262         }\r
6263     }\r
6264     break;\r
6265 \r
6266   /* [AS] Snapping */\r
6267   case WM_ENTERSIZEMOVE:\r
6268     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6269     if (hwnd == hwndMain) {\r
6270       doingSizing = TRUE;\r
6271       lastSizing = 0;\r
6272     }\r
6273     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6274     break;\r
6275 \r
6276   case WM_SIZING:\r
6277     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6278     if (hwnd == hwndMain) {\r
6279       lastSizing = wParam;\r
6280     }\r
6281     break;\r
6282 \r
6283   case WM_MOVING:\r
6284     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6285       return OnMoving( &sd, hwnd, wParam, lParam );\r
6286 \r
6287   case WM_EXITSIZEMOVE:\r
6288     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6289     if (hwnd == hwndMain) {\r
6290       RECT client;\r
6291       doingSizing = FALSE;\r
6292       InvalidateRect(hwnd, &boardRect, FALSE);\r
6293       GetClientRect(hwnd, &client);\r
6294       ResizeBoard(client.right, client.bottom, lastSizing);\r
6295       lastSizing = 0;\r
6296       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6297     }\r
6298     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6299     break;\r
6300 \r
6301   case WM_DESTROY: /* message: window being destroyed */\r
6302     PostQuitMessage(0);\r
6303     break;\r
6304 \r
6305   case WM_CLOSE:\r
6306     if (hwnd == hwndMain) {\r
6307       ExitEvent(0);\r
6308     }\r
6309     break;\r
6310 \r
6311   default:      /* Passes it on if unprocessed */\r
6312     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6313   }\r
6314   return 0;\r
6315 }\r
6316 \r
6317 /*---------------------------------------------------------------------------*\\r
6318  *\r
6319  * Misc utility routines\r
6320  *\r
6321 \*---------------------------------------------------------------------------*/\r
6322 \r
6323 /*\r
6324  * Decent random number generator, at least not as bad as Windows\r
6325  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6326  */\r
6327 unsigned int randstate;\r
6328 \r
6329 int\r
6330 myrandom(void)\r
6331 {\r
6332   randstate = randstate * 1664525 + 1013904223;\r
6333   return (int) randstate & 0x7fffffff;\r
6334 }\r
6335 \r
6336 void\r
6337 mysrandom(unsigned int seed)\r
6338 {\r
6339   randstate = seed;\r
6340 }\r
6341 \r
6342 \r
6343 /* \r
6344  * returns TRUE if user selects a different color, FALSE otherwise \r
6345  */\r
6346 \r
6347 BOOL\r
6348 ChangeColor(HWND hwnd, COLORREF *which)\r
6349 {\r
6350   static BOOL firstTime = TRUE;\r
6351   static DWORD customColors[16];\r
6352   CHOOSECOLOR cc;\r
6353   COLORREF newcolor;\r
6354   int i;\r
6355   ColorClass ccl;\r
6356 \r
6357   if (firstTime) {\r
6358     /* Make initial colors in use available as custom colors */\r
6359     /* Should we put the compiled-in defaults here instead? */\r
6360     i = 0;\r
6361     customColors[i++] = lightSquareColor & 0xffffff;\r
6362     customColors[i++] = darkSquareColor & 0xffffff;\r
6363     customColors[i++] = whitePieceColor & 0xffffff;\r
6364     customColors[i++] = blackPieceColor & 0xffffff;\r
6365     customColors[i++] = highlightSquareColor & 0xffffff;\r
6366     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6367 \r
6368     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6369       customColors[i++] = textAttribs[ccl].color;\r
6370     }\r
6371     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6372     firstTime = FALSE;\r
6373   }\r
6374 \r
6375   cc.lStructSize = sizeof(cc);\r
6376   cc.hwndOwner = hwnd;\r
6377   cc.hInstance = NULL;\r
6378   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6379   cc.lpCustColors = (LPDWORD) customColors;\r
6380   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6381 \r
6382   if (!ChooseColor(&cc)) return FALSE;\r
6383 \r
6384   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6385   if (newcolor == *which) return FALSE;\r
6386   *which = newcolor;\r
6387   return TRUE;\r
6388 \r
6389   /*\r
6390   InitDrawingColors();\r
6391   InvalidateRect(hwnd, &boardRect, FALSE);\r
6392   */\r
6393 }\r
6394 \r
6395 BOOLEAN\r
6396 MyLoadSound(MySound *ms)\r
6397 {\r
6398   BOOL ok = FALSE;\r
6399   struct stat st;\r
6400   FILE *f;\r
6401 \r
6402   if (ms->data) free(ms->data);\r
6403   ms->data = NULL;\r
6404 \r
6405   switch (ms->name[0]) {\r
6406   case NULLCHAR:\r
6407     /* Silence */\r
6408     ok = TRUE;\r
6409     break;\r
6410   case '$':\r
6411     /* System sound from Control Panel.  Don't preload here. */\r
6412     ok = TRUE;\r
6413     break;\r
6414   case '!':\r
6415     if (ms->name[1] == NULLCHAR) {\r
6416       /* "!" alone = silence */\r
6417       ok = TRUE;\r
6418     } else {\r
6419       /* Builtin wave resource.  Error if not found. */\r
6420       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6421       if (h == NULL) break;\r
6422       ms->data = (void *)LoadResource(hInst, h);\r
6423       if (h == NULL) break;\r
6424       ok = TRUE;\r
6425     }\r
6426     break;\r
6427   default:\r
6428     /* .wav file.  Error if not found. */\r
6429     f = fopen(ms->name, "rb");\r
6430     if (f == NULL) break;\r
6431     if (fstat(fileno(f), &st) < 0) break;\r
6432     ms->data = malloc(st.st_size);\r
6433     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6434     fclose(f);\r
6435     ok = TRUE;\r
6436     break;\r
6437   }\r
6438   if (!ok) {\r
6439     char buf[MSG_SIZ];\r
6440     sprintf(buf, "Error loading sound %s", ms->name);\r
6441     DisplayError(buf, GetLastError());\r
6442   }\r
6443   return ok;\r
6444 }\r
6445 \r
6446 BOOLEAN\r
6447 MyPlaySound(MySound *ms)\r
6448 {\r
6449   BOOLEAN ok = FALSE;\r
6450 \r
6451   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6452   switch (ms->name[0]) {\r
6453   case NULLCHAR:\r
6454         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6455     /* Silence */\r
6456     ok = TRUE;\r
6457     break;\r
6458   case '$':\r
6459     /* System sound from Control Panel (deprecated feature).\r
6460        "$" alone or an unset sound name gets default beep (still in use). */\r
6461     if (ms->name[1]) {\r
6462       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6463     }\r
6464     if (!ok) ok = MessageBeep(MB_OK);\r
6465     break; \r
6466   case '!':\r
6467     /* Builtin wave resource, or "!" alone for silence */\r
6468     if (ms->name[1]) {\r
6469       if (ms->data == NULL) return FALSE;\r
6470       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6471     } else {\r
6472       ok = TRUE;\r
6473     }\r
6474     break;\r
6475   default:\r
6476     /* .wav file.  Error if not found. */\r
6477     if (ms->data == NULL) return FALSE;\r
6478     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6479     break;\r
6480   }\r
6481   /* Don't print an error: this can happen innocently if the sound driver\r
6482      is busy; for instance, if another instance of WinBoard is playing\r
6483      a sound at about the same time. */\r
6484   return ok;\r
6485 }\r
6486 \r
6487 \r
6488 LRESULT CALLBACK\r
6489 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6490 {\r
6491   BOOL ok;\r
6492   OPENFILENAME *ofn;\r
6493   static UINT *number; /* gross that this is static */\r
6494 \r
6495   switch (message) {\r
6496   case WM_INITDIALOG: /* message: initialize dialog box */\r
6497     /* Center the dialog over the application window */\r
6498     ofn = (OPENFILENAME *) lParam;\r
6499     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6500       number = (UINT *) ofn->lCustData;\r
6501       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6502     } else {\r
6503       number = NULL;\r
6504     }\r
6505     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6506     return FALSE;  /* Allow for further processing */\r
6507 \r
6508   case WM_COMMAND:\r
6509     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6510       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6511     }\r
6512     return FALSE;  /* Allow for further processing */\r
6513   }\r
6514   return FALSE;\r
6515 }\r
6516 \r
6517 UINT APIENTRY\r
6518 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6519 {\r
6520   static UINT *number;\r
6521   OPENFILENAME *ofname;\r
6522   OFNOTIFY *ofnot;\r
6523   switch (uiMsg) {\r
6524   case WM_INITDIALOG:\r
6525     ofname = (OPENFILENAME *)lParam;\r
6526     number = (UINT *)(ofname->lCustData);\r
6527     break;\r
6528   case WM_NOTIFY:\r
6529     ofnot = (OFNOTIFY *)lParam;\r
6530     if (ofnot->hdr.code == CDN_FILEOK) {\r
6531       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6532     }\r
6533     break;\r
6534   }\r
6535   return 0;\r
6536 }\r
6537 \r
6538 \r
6539 FILE *\r
6540 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6541                char *nameFilt, char *dlgTitle, UINT *number,\r
6542                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6543 {\r
6544   OPENFILENAME openFileName;\r
6545   char buf1[MSG_SIZ];\r
6546   FILE *f;\r
6547 \r
6548   if (fileName == NULL) fileName = buf1;\r
6549   if (defName == NULL) {\r
6550     strcpy(fileName, "*.");\r
6551     strcat(fileName, defExt);\r
6552   } else {\r
6553     strcpy(fileName, defName);\r
6554   }\r
6555   if (fileTitle) strcpy(fileTitle, "");\r
6556   if (number) *number = 0;\r
6557 \r
6558   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6559   openFileName.hwndOwner         = hwnd;\r
6560   openFileName.hInstance         = (HANDLE) hInst;\r
6561   openFileName.lpstrFilter       = nameFilt;\r
6562   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6563   openFileName.nMaxCustFilter    = 0L;\r
6564   openFileName.nFilterIndex      = 1L;\r
6565   openFileName.lpstrFile         = fileName;\r
6566   openFileName.nMaxFile          = MSG_SIZ;\r
6567   openFileName.lpstrFileTitle    = fileTitle;\r
6568   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6569   openFileName.lpstrInitialDir   = NULL;\r
6570   openFileName.lpstrTitle        = dlgTitle;\r
6571   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6572     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6573     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6574     | (oldDialog ? 0 : OFN_EXPLORER);\r
6575   openFileName.nFileOffset       = 0;\r
6576   openFileName.nFileExtension    = 0;\r
6577   openFileName.lpstrDefExt       = defExt;\r
6578   openFileName.lCustData         = (LONG) number;\r
6579   openFileName.lpfnHook          = oldDialog ?\r
6580     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6581   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6582 \r
6583   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6584                         GetOpenFileName(&openFileName)) {\r
6585     /* open the file */\r
6586     f = fopen(openFileName.lpstrFile, write);\r
6587     if (f == NULL) {\r
6588       MessageBox(hwnd, "File open failed", NULL,\r
6589                  MB_OK|MB_ICONEXCLAMATION);\r
6590       return NULL;\r
6591     }\r
6592   } else {\r
6593     int err = CommDlgExtendedError();\r
6594     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6595     return FALSE;\r
6596   }\r
6597   return f;\r
6598 }\r
6599 \r
6600 \r
6601 \r
6602 VOID APIENTRY\r
6603 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6604 {\r
6605   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6606 \r
6607   /*\r
6608    * Get the first pop-up menu in the menu template. This is the\r
6609    * menu that TrackPopupMenu displays.\r
6610    */\r
6611   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6612 \r
6613   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6614 \r
6615   /*\r
6616    * TrackPopup uses screen coordinates, so convert the\r
6617    * coordinates of the mouse click to screen coordinates.\r
6618    */\r
6619   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6620 \r
6621   /* Draw and track the floating pop-up menu. */\r
6622   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6623                  pt.x, pt.y, 0, hwnd, NULL);\r
6624 \r
6625   /* Destroy the menu.*/\r
6626   DestroyMenu(hmenu);\r
6627 }\r
6628    \r
6629 typedef struct {\r
6630   HWND hDlg, hText;\r
6631   int sizeX, sizeY, newSizeX, newSizeY;\r
6632   HDWP hdwp;\r
6633 } ResizeEditPlusButtonsClosure;\r
6634 \r
6635 BOOL CALLBACK\r
6636 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6637 {\r
6638   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6639   RECT rect;\r
6640   POINT pt;\r
6641 \r
6642   if (hChild == cl->hText) return TRUE;\r
6643   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6644   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6645   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6646   ScreenToClient(cl->hDlg, &pt);\r
6647   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6648     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6649   return TRUE;\r
6650 }\r
6651 \r
6652 /* Resize a dialog that has a (rich) edit field filling most of\r
6653    the top, with a row of buttons below */\r
6654 VOID\r
6655 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6656 {\r
6657   RECT rectText;\r
6658   int newTextHeight, newTextWidth;\r
6659   ResizeEditPlusButtonsClosure cl;\r
6660   \r
6661   /*if (IsIconic(hDlg)) return;*/\r
6662   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6663   \r
6664   cl.hdwp = BeginDeferWindowPos(8);\r
6665 \r
6666   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6667   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6668   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6669   if (newTextHeight < 0) {\r
6670     newSizeY += -newTextHeight;\r
6671     newTextHeight = 0;\r
6672   }\r
6673   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6674     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6675 \r
6676   cl.hDlg = hDlg;\r
6677   cl.hText = hText;\r
6678   cl.sizeX = sizeX;\r
6679   cl.sizeY = sizeY;\r
6680   cl.newSizeX = newSizeX;\r
6681   cl.newSizeY = newSizeY;\r
6682   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6683 \r
6684   EndDeferWindowPos(cl.hdwp);\r
6685 }\r
6686 \r
6687 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6688 {\r
6689     RECT    rChild, rParent;\r
6690     int     wChild, hChild, wParent, hParent;\r
6691     int     wScreen, hScreen, xNew, yNew;\r
6692     HDC     hdc;\r
6693 \r
6694     /* Get the Height and Width of the child window */\r
6695     GetWindowRect (hwndChild, &rChild);\r
6696     wChild = rChild.right - rChild.left;\r
6697     hChild = rChild.bottom - rChild.top;\r
6698 \r
6699     /* Get the Height and Width of the parent window */\r
6700     GetWindowRect (hwndParent, &rParent);\r
6701     wParent = rParent.right - rParent.left;\r
6702     hParent = rParent.bottom - rParent.top;\r
6703 \r
6704     /* Get the display limits */\r
6705     hdc = GetDC (hwndChild);\r
6706     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6707     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6708     ReleaseDC(hwndChild, hdc);\r
6709 \r
6710     /* Calculate new X position, then adjust for screen */\r
6711     xNew = rParent.left + ((wParent - wChild) /2);\r
6712     if (xNew < 0) {\r
6713         xNew = 0;\r
6714     } else if ((xNew+wChild) > wScreen) {\r
6715         xNew = wScreen - wChild;\r
6716     }\r
6717 \r
6718     /* Calculate new Y position, then adjust for screen */\r
6719     if( mode == 0 ) {\r
6720         yNew = rParent.top  + ((hParent - hChild) /2);\r
6721     }\r
6722     else {\r
6723         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6724     }\r
6725 \r
6726     if (yNew < 0) {\r
6727         yNew = 0;\r
6728     } else if ((yNew+hChild) > hScreen) {\r
6729         yNew = hScreen - hChild;\r
6730     }\r
6731 \r
6732     /* Set it, and return */\r
6733     return SetWindowPos (hwndChild, NULL,\r
6734                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6735 }\r
6736 \r
6737 /* Center one window over another */\r
6738 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6739 {\r
6740     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6741 }\r
6742 \r
6743 /*---------------------------------------------------------------------------*\\r
6744  *\r
6745  * Startup Dialog functions\r
6746  *\r
6747 \*---------------------------------------------------------------------------*/\r
6748 void\r
6749 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6750 {\r
6751   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6752 \r
6753   while (*cd != NULL) {\r
6754     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6755     cd++;\r
6756   }\r
6757 }\r
6758 \r
6759 void\r
6760 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6761 {\r
6762   char buf1[ARG_MAX];\r
6763   int len;\r
6764 \r
6765   if (str[0] == '@') {\r
6766     FILE* f = fopen(str + 1, "r");\r
6767     if (f == NULL) {\r
6768       DisplayFatalError(str + 1, errno, 2);\r
6769       return;\r
6770     }\r
6771     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
6772     fclose(f);\r
6773     buf1[len] = NULLCHAR;\r
6774     str = buf1;\r
6775   }\r
6776 \r
6777   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6778 \r
6779   for (;;) {\r
6780     char buf[MSG_SIZ];\r
6781     char *end = strchr(str, '\n');\r
6782     if (end == NULL) return;\r
6783     memcpy(buf, str, end - str);\r
6784     buf[end - str] = NULLCHAR;\r
6785     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
6786     str = end + 1;\r
6787   }\r
6788 }\r
6789 \r
6790 void\r
6791 SetStartupDialogEnables(HWND hDlg)\r
6792 {\r
6793   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
6794     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6795     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
6796   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6797     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
6798   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
6799     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
6800   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
6801     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
6802   EnableWindow(GetDlgItem(hDlg, IDOK),\r
6803     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
6804     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
6805     IsDlgButtonChecked(hDlg, OPT_View));\r
6806 }\r
6807 \r
6808 char *\r
6809 QuoteForFilename(char *filename)\r
6810 {\r
6811   int dquote, space;\r
6812   dquote = strchr(filename, '"') != NULL;\r
6813   space = strchr(filename, ' ') != NULL;\r
6814   if (dquote || space) {\r
6815     if (dquote) {\r
6816       return "'";\r
6817     } else {\r
6818       return "\"";\r
6819     }\r
6820   } else {\r
6821     return "";\r
6822   }\r
6823 }\r
6824 \r
6825 VOID\r
6826 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
6827 {\r
6828   char buf[MSG_SIZ];\r
6829   char *q;\r
6830 \r
6831   InitComboStringsFromOption(hwndCombo, nthnames);\r
6832   q = QuoteForFilename(nthcp);\r
6833   sprintf(buf, "%s%s%s", q, nthcp, q);\r
6834   if (*nthdir != NULLCHAR) {\r
6835     q = QuoteForFilename(nthdir);\r
6836     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
6837   }\r
6838   if (*nthcp == NULLCHAR) {\r
6839     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6840   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6841     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6842     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6843   }\r
6844 }\r
6845 \r
6846 LRESULT CALLBACK\r
6847 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6848 {\r
6849   char buf[MSG_SIZ];\r
6850   HANDLE hwndCombo;\r
6851   char *p;\r
6852 \r
6853   switch (message) {\r
6854   case WM_INITDIALOG:\r
6855     /* Center the dialog */\r
6856     CenterWindow (hDlg, GetDesktopWindow());\r
6857     /* Initialize the dialog items */\r
6858     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
6859                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
6860                   firstChessProgramNames);\r
6861     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
6862                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
6863                   secondChessProgramNames);\r
6864     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
6865     InitComboStringsFromOption(hwndCombo, icsNames);    \r
6866     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
6867     if (*appData.icsHelper != NULLCHAR) {\r
6868       char *q = QuoteForFilename(appData.icsHelper);\r
6869       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
6870     }\r
6871     if (*appData.icsHost == NULLCHAR) {\r
6872       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
6873       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
6874     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
6875       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
6876       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
6877     }\r
6878 \r
6879     if (appData.icsActive) {\r
6880       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
6881     }\r
6882     else if (appData.noChessProgram) {\r
6883       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
6884     }\r
6885     else {\r
6886       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
6887     }\r
6888 \r
6889     SetStartupDialogEnables(hDlg);\r
6890     return TRUE;\r
6891 \r
6892   case WM_COMMAND:\r
6893     switch (LOWORD(wParam)) {\r
6894     case IDOK:\r
6895       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
6896         strcpy(buf, "/fcp=");\r
6897         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6898         p = buf;\r
6899         ParseArgs(StringGet, &p);\r
6900         strcpy(buf, "/scp=");\r
6901         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6902         p = buf;\r
6903         ParseArgs(StringGet, &p);\r
6904         appData.noChessProgram = FALSE;\r
6905         appData.icsActive = FALSE;\r
6906       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
6907         strcpy(buf, "/ics /icshost=");\r
6908         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6909         p = buf;\r
6910         ParseArgs(StringGet, &p);\r
6911         if (appData.zippyPlay) {\r
6912           strcpy(buf, "/fcp=");\r
6913           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
6914           p = buf;\r
6915           ParseArgs(StringGet, &p);\r
6916         }\r
6917       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
6918         appData.noChessProgram = TRUE;\r
6919         appData.icsActive = FALSE;\r
6920       } else {\r
6921         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
6922                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
6923         return TRUE;\r
6924       }\r
6925       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
6926         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
6927         p = buf;\r
6928         ParseArgs(StringGet, &p);\r
6929       }\r
6930       EndDialog(hDlg, TRUE);\r
6931       return TRUE;\r
6932 \r
6933     case IDCANCEL:\r
6934       ExitEvent(0);\r
6935       return TRUE;\r
6936 \r
6937     case IDM_HELPCONTENTS:\r
6938       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
6939         MessageBox (GetFocus(),\r
6940                     "Unable to activate help",\r
6941                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6942       }\r
6943       break;\r
6944 \r
6945     default:\r
6946       SetStartupDialogEnables(hDlg);\r
6947       break;\r
6948     }\r
6949     break;\r
6950   }\r
6951   return FALSE;\r
6952 }\r
6953 \r
6954 /*---------------------------------------------------------------------------*\\r
6955  *\r
6956  * About box dialog functions\r
6957  *\r
6958 \*---------------------------------------------------------------------------*/\r
6959 \r
6960 /* Process messages for "About" dialog box */\r
6961 LRESULT CALLBACK\r
6962 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6963 {\r
6964   switch (message) {\r
6965   case WM_INITDIALOG: /* message: initialize dialog box */\r
6966     /* Center the dialog over the application window */\r
6967     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6968     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
6969     JAWS_COPYRIGHT\r
6970     return (TRUE);\r
6971 \r
6972   case WM_COMMAND: /* message: received a command */\r
6973     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
6974         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
6975       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
6976       return (TRUE);\r
6977     }\r
6978     break;\r
6979   }\r
6980   return (FALSE);\r
6981 }\r
6982 \r
6983 /*---------------------------------------------------------------------------*\\r
6984  *\r
6985  * Comment Dialog functions\r
6986  *\r
6987 \*---------------------------------------------------------------------------*/\r
6988 \r
6989 LRESULT CALLBACK\r
6990 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6991 {\r
6992   static HANDLE hwndText = NULL;\r
6993   int len, newSizeX, newSizeY, flags;\r
6994   static int sizeX, sizeY;\r
6995   char *str;\r
6996   RECT rect;\r
6997   MINMAXINFO *mmi;\r
6998 \r
6999   switch (message) {\r
7000   case WM_INITDIALOG: /* message: initialize dialog box */\r
7001     /* Initialize the dialog items */\r
7002     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7003     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7004     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7005     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7006     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7007     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7008     SetWindowText(hDlg, commentTitle);\r
7009     if (editComment) {\r
7010       SetFocus(hwndText);\r
7011     } else {\r
7012       SetFocus(GetDlgItem(hDlg, IDOK));\r
7013     }\r
7014     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7015                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7016                 MAKELPARAM(FALSE, 0));\r
7017     /* Size and position the dialog */\r
7018     if (!commentDialog) {\r
7019       commentDialog = hDlg;\r
7020       flags = SWP_NOZORDER;\r
7021       GetClientRect(hDlg, &rect);\r
7022       sizeX = rect.right;\r
7023       sizeY = rect.bottom;\r
7024       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7025           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7026         WINDOWPLACEMENT wp;\r
7027         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7028         wp.length = sizeof(WINDOWPLACEMENT);\r
7029         wp.flags = 0;\r
7030         wp.showCmd = SW_SHOW;\r
7031         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7032         wp.rcNormalPosition.left = commentX;\r
7033         wp.rcNormalPosition.right = commentX + commentW;\r
7034         wp.rcNormalPosition.top = commentY;\r
7035         wp.rcNormalPosition.bottom = commentY + commentH;\r
7036         SetWindowPlacement(hDlg, &wp);\r
7037 \r
7038         GetClientRect(hDlg, &rect);\r
7039         newSizeX = rect.right;\r
7040         newSizeY = rect.bottom;\r
7041         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7042                               newSizeX, newSizeY);\r
7043         sizeX = newSizeX;\r
7044         sizeY = newSizeY;\r
7045       }\r
7046     }\r
7047     return FALSE;\r
7048 \r
7049   case WM_COMMAND: /* message: received a command */\r
7050     switch (LOWORD(wParam)) {\r
7051     case IDOK:\r
7052       if (editComment) {\r
7053         char *p, *q;\r
7054         /* Read changed options from the dialog box */\r
7055         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7056         len = GetWindowTextLength(hwndText);\r
7057         str = (char *) malloc(len + 1);\r
7058         GetWindowText(hwndText, str, len + 1);\r
7059         p = q = str;\r
7060         while (*q) {\r
7061           if (*q == '\r')\r
7062             q++;\r
7063           else\r
7064             *p++ = *q++;\r
7065         }\r
7066         *p = NULLCHAR;\r
7067         ReplaceComment(commentIndex, str);\r
7068         free(str);\r
7069       }\r
7070       CommentPopDown();\r
7071       return TRUE;\r
7072 \r
7073     case IDCANCEL:\r
7074     case OPT_CancelComment:\r
7075       CommentPopDown();\r
7076       return TRUE;\r
7077 \r
7078     case OPT_ClearComment:\r
7079       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7080       break;\r
7081 \r
7082     case OPT_EditComment:\r
7083       EditCommentEvent();\r
7084       return TRUE;\r
7085 \r
7086     default:\r
7087       break;\r
7088     }\r
7089     break;\r
7090 \r
7091   case WM_SIZE:\r
7092     newSizeX = LOWORD(lParam);\r
7093     newSizeY = HIWORD(lParam);\r
7094     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7095     sizeX = newSizeX;\r
7096     sizeY = newSizeY;\r
7097     break;\r
7098 \r
7099   case WM_GETMINMAXINFO:\r
7100     /* Prevent resizing window too small */\r
7101     mmi = (MINMAXINFO *) lParam;\r
7102     mmi->ptMinTrackSize.x = 100;\r
7103     mmi->ptMinTrackSize.y = 100;\r
7104     break;\r
7105   }\r
7106   return FALSE;\r
7107 }\r
7108 \r
7109 VOID\r
7110 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7111 {\r
7112   FARPROC lpProc;\r
7113   char *p, *q;\r
7114 \r
7115   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7116 \r
7117   if (str == NULL) str = "";\r
7118   p = (char *) malloc(2 * strlen(str) + 2);\r
7119   q = p;\r
7120   while (*str) {\r
7121     if (*str == '\n') *q++ = '\r';\r
7122     *q++ = *str++;\r
7123   }\r
7124   *q = NULLCHAR;\r
7125   if (commentText != NULL) free(commentText);\r
7126 \r
7127   commentIndex = index;\r
7128   commentTitle = title;\r
7129   commentText = p;\r
7130   editComment = edit;\r
7131 \r
7132   if (commentDialog) {\r
7133     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7134     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7135   } else {\r
7136     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7137     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7138                  hwndMain, (DLGPROC)lpProc);\r
7139     FreeProcInstance(lpProc);\r
7140   }\r
7141   commentDialogUp = TRUE;\r
7142 }\r
7143 \r
7144 \r
7145 /*---------------------------------------------------------------------------*\\r
7146  *\r
7147  * Type-in move dialog functions\r
7148  * \r
7149 \*---------------------------------------------------------------------------*/\r
7150 \r
7151 LRESULT CALLBACK\r
7152 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7153 {\r
7154   char move[MSG_SIZ];\r
7155   HWND hInput;\r
7156   ChessMove moveType;\r
7157   int fromX, fromY, toX, toY;\r
7158   char promoChar;\r
7159 \r
7160   switch (message) {\r
7161   case WM_INITDIALOG:\r
7162     move[0] = (char) lParam;\r
7163     move[1] = NULLCHAR;\r
7164     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7165     hInput = GetDlgItem(hDlg, OPT_Move);\r
7166     SetWindowText(hInput, move);\r
7167     SetFocus(hInput);\r
7168     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7169     return FALSE;\r
7170 \r
7171   case WM_COMMAND:\r
7172     switch (LOWORD(wParam)) {\r
7173     case IDOK:\r
7174       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7175       { int n; Board board;\r
7176         // [HGM] FENedit\r
7177         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7178                 EditPositionPasteFEN(move);\r
7179                 EndDialog(hDlg, TRUE);\r
7180                 return TRUE;\r
7181         }\r
7182         // [HGM] movenum: allow move number to be typed in any mode\r
7183         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7184           currentMove = 2*n-1;\r
7185           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7186           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7187           EndDialog(hDlg, TRUE);\r
7188           DrawPosition(TRUE, boards[currentMove]);\r
7189           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7190           else DisplayMessage("", "");\r
7191           return TRUE;\r
7192         }\r
7193       }\r
7194       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7195         gameMode != Training) {\r
7196         DisplayMoveError("Displayed move is not current");\r
7197       } else {\r
7198 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7199         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7200           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7201         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7202         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7203           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7204           if (gameMode != Training)\r
7205               forwardMostMove = currentMove;\r
7206           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7207         } else {\r
7208           DisplayMoveError("Could not parse move");\r
7209         }\r
7210       }\r
7211       EndDialog(hDlg, TRUE);\r
7212       return TRUE;\r
7213     case IDCANCEL:\r
7214       EndDialog(hDlg, FALSE);\r
7215       return TRUE;\r
7216     default:\r
7217       break;\r
7218     }\r
7219     break;\r
7220   }\r
7221   return FALSE;\r
7222 }\r
7223 \r
7224 VOID\r
7225 PopUpMoveDialog(char firstchar)\r
7226 {\r
7227     FARPROC lpProc;\r
7228     \r
7229     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7230         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7231         gameMode == AnalyzeMode || gameMode == EditGame || \r
7232         gameMode == EditPosition || gameMode == IcsExamining ||\r
7233         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7234         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7235                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7236                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7237         gameMode == Training) {\r
7238       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7239       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7240         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7241       FreeProcInstance(lpProc);\r
7242     }\r
7243 }\r
7244 \r
7245 /*---------------------------------------------------------------------------*\\r
7246  *\r
7247  * Type-in name dialog functions\r
7248  * \r
7249 \*---------------------------------------------------------------------------*/\r
7250 \r
7251 LRESULT CALLBACK\r
7252 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7253 {\r
7254   char move[MSG_SIZ];\r
7255   HWND hInput;\r
7256 \r
7257   switch (message) {\r
7258   case WM_INITDIALOG:\r
7259     move[0] = (char) lParam;\r
7260     move[1] = NULLCHAR;\r
7261     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7262     hInput = GetDlgItem(hDlg, OPT_Name);\r
7263     SetWindowText(hInput, move);\r
7264     SetFocus(hInput);\r
7265     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7266     return FALSE;\r
7267 \r
7268   case WM_COMMAND:\r
7269     switch (LOWORD(wParam)) {\r
7270     case IDOK:\r
7271       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7272       appData.userName = strdup(move);\r
7273       SetUserLogo();\r
7274 \r
7275       EndDialog(hDlg, TRUE);\r
7276       return TRUE;\r
7277     case IDCANCEL:\r
7278       EndDialog(hDlg, FALSE);\r
7279       return TRUE;\r
7280     default:\r
7281       break;\r
7282     }\r
7283     break;\r
7284   }\r
7285   return FALSE;\r
7286 }\r
7287 \r
7288 VOID\r
7289 PopUpNameDialog(char firstchar)\r
7290 {\r
7291     FARPROC lpProc;\r
7292     \r
7293       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7294       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7295         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7296       FreeProcInstance(lpProc);\r
7297 }\r
7298 \r
7299 /*---------------------------------------------------------------------------*\\r
7300  *\r
7301  *  Error dialogs\r
7302  * \r
7303 \*---------------------------------------------------------------------------*/\r
7304 \r
7305 /* Nonmodal error box */\r
7306 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7307                              WPARAM wParam, LPARAM lParam);\r
7308 \r
7309 VOID\r
7310 ErrorPopUp(char *title, char *content)\r
7311 {\r
7312   FARPROC lpProc;\r
7313   char *p, *q;\r
7314   BOOLEAN modal = hwndMain == NULL;\r
7315 \r
7316   p = content;\r
7317   q = errorMessage;\r
7318   while (*p) {\r
7319     if (*p == '\n') {\r
7320       if (modal) {\r
7321         *q++ = ' ';\r
7322         p++;\r
7323       } else {\r
7324         *q++ = '\r';\r
7325         *q++ = *p++;\r
7326       }\r
7327     } else {\r
7328       *q++ = *p++;\r
7329     }\r
7330   }\r
7331   *q = NULLCHAR;\r
7332   strncpy(errorTitle, title, sizeof(errorTitle));\r
7333   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7334   \r
7335   if (modal) {\r
7336     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7337   } else {\r
7338     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7339     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7340                  hwndMain, (DLGPROC)lpProc);\r
7341     FreeProcInstance(lpProc);\r
7342   }\r
7343 }\r
7344 \r
7345 VOID\r
7346 ErrorPopDown()\r
7347 {\r
7348   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7349   if (errorDialog == NULL) return;\r
7350   DestroyWindow(errorDialog);\r
7351   errorDialog = NULL;\r
7352 }\r
7353 \r
7354 LRESULT CALLBACK\r
7355 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7356 {\r
7357   HANDLE hwndText;\r
7358   RECT rChild;\r
7359 \r
7360   switch (message) {\r
7361   case WM_INITDIALOG:\r
7362     GetWindowRect(hDlg, &rChild);\r
7363 \r
7364     /*\r
7365     SetWindowPos(hDlg, NULL, rChild.left,\r
7366       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7367       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7368     */\r
7369 \r
7370     /* \r
7371         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7372         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7373         and it doesn't work when you resize the dialog.\r
7374         For now, just give it a default position.\r
7375     */\r
7376     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7377 \r
7378     errorDialog = hDlg;\r
7379     SetWindowText(hDlg, errorTitle);\r
7380     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7381     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7382     return FALSE;\r
7383 \r
7384   case WM_COMMAND:\r
7385     switch (LOWORD(wParam)) {\r
7386     case IDOK:\r
7387     case IDCANCEL:\r
7388       if (errorDialog == hDlg) errorDialog = NULL;\r
7389       DestroyWindow(hDlg);\r
7390       return TRUE;\r
7391 \r
7392     default:\r
7393       break;\r
7394     }\r
7395     break;\r
7396   }\r
7397   return FALSE;\r
7398 }\r
7399 \r
7400 #ifdef GOTHIC\r
7401 HWND gothicDialog = NULL;\r
7402 \r
7403 LRESULT CALLBACK\r
7404 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7405 {\r
7406   HANDLE hwndText;\r
7407   RECT rChild;\r
7408   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7409 \r
7410   switch (message) {\r
7411   case WM_INITDIALOG:\r
7412     GetWindowRect(hDlg, &rChild);\r
7413 \r
7414     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7415                                                              SWP_NOZORDER);\r
7416 \r
7417     /* \r
7418         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7419         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7420         and it doesn't work when you resize the dialog.\r
7421         For now, just give it a default position.\r
7422     */\r
7423     gothicDialog = hDlg;\r
7424     SetWindowText(hDlg, errorTitle);\r
7425     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7426     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7427     return FALSE;\r
7428 \r
7429   case WM_COMMAND:\r
7430     switch (LOWORD(wParam)) {\r
7431     case IDOK:\r
7432     case IDCANCEL:\r
7433       if (errorDialog == hDlg) errorDialog = NULL;\r
7434       DestroyWindow(hDlg);\r
7435       return TRUE;\r
7436 \r
7437     default:\r
7438       break;\r
7439     }\r
7440     break;\r
7441   }\r
7442   return FALSE;\r
7443 }\r
7444 \r
7445 VOID\r
7446 GothicPopUp(char *title, VariantClass variant)\r
7447 {\r
7448   FARPROC lpProc;\r
7449   static char *lastTitle;\r
7450 \r
7451   strncpy(errorTitle, title, sizeof(errorTitle));\r
7452   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7453 \r
7454   if(lastTitle != title && gothicDialog != NULL) {\r
7455     DestroyWindow(gothicDialog);\r
7456     gothicDialog = NULL;\r
7457   }\r
7458   if(variant != VariantNormal && gothicDialog == NULL) {\r
7459     title = lastTitle;\r
7460     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7461     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7462                  hwndMain, (DLGPROC)lpProc);\r
7463     FreeProcInstance(lpProc);\r
7464   }\r
7465 }\r
7466 #endif\r
7467 \r
7468 /*---------------------------------------------------------------------------*\\r
7469  *\r
7470  *  Ics Interaction console functions\r
7471  *\r
7472 \*---------------------------------------------------------------------------*/\r
7473 \r
7474 #define HISTORY_SIZE 64\r
7475 static char *history[HISTORY_SIZE];\r
7476 int histIn = 0, histP = 0;\r
7477 \r
7478 VOID\r
7479 SaveInHistory(char *cmd)\r
7480 {\r
7481   if (history[histIn] != NULL) {\r
7482     free(history[histIn]);\r
7483     history[histIn] = NULL;\r
7484   }\r
7485   if (*cmd == NULLCHAR) return;\r
7486   history[histIn] = StrSave(cmd);\r
7487   histIn = (histIn + 1) % HISTORY_SIZE;\r
7488   if (history[histIn] != NULL) {\r
7489     free(history[histIn]);\r
7490     history[histIn] = NULL;\r
7491   }\r
7492   histP = histIn;\r
7493 }\r
7494 \r
7495 char *\r
7496 PrevInHistory(char *cmd)\r
7497 {\r
7498   int newhp;\r
7499   if (histP == histIn) {\r
7500     if (history[histIn] != NULL) free(history[histIn]);\r
7501     history[histIn] = StrSave(cmd);\r
7502   }\r
7503   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7504   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7505   histP = newhp;\r
7506   return history[histP];\r
7507 }\r
7508 \r
7509 char *\r
7510 NextInHistory()\r
7511 {\r
7512   if (histP == histIn) return NULL;\r
7513   histP = (histP + 1) % HISTORY_SIZE;\r
7514   return history[histP];\r
7515 }\r
7516 \r
7517 typedef struct {\r
7518   char *item;\r
7519   char *command;\r
7520   BOOLEAN getname;\r
7521   BOOLEAN immediate;\r
7522 } IcsTextMenuEntry;\r
7523 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7524 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7525 \r
7526 void\r
7527 ParseIcsTextMenu(char *icsTextMenuString)\r
7528 {\r
7529 //  int flags = 0;\r
7530   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7531   char *p = icsTextMenuString;\r
7532   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7533     free(e->item);\r
7534     e->item = NULL;\r
7535     if (e->command != NULL) {\r
7536       free(e->command);\r
7537       e->command = NULL;\r
7538     }\r
7539     e++;\r
7540   }\r
7541   e = icsTextMenuEntry;\r
7542   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7543     if (*p == ';' || *p == '\n') {\r
7544       e->item = strdup("-");\r
7545       e->command = NULL;\r
7546       p++;\r
7547     } else if (*p == '-') {\r
7548       e->item = strdup("-");\r
7549       e->command = NULL;\r
7550       p++;\r
7551       if (*p) p++;\r
7552     } else {\r
7553       char *q, *r, *s, *t;\r
7554       char c;\r
7555       q = strchr(p, ',');\r
7556       if (q == NULL) break;\r
7557       *q = NULLCHAR;\r
7558       r = strchr(q + 1, ',');\r
7559       if (r == NULL) break;\r
7560       *r = NULLCHAR;\r
7561       s = strchr(r + 1, ',');\r
7562       if (s == NULL) break;\r
7563       *s = NULLCHAR;\r
7564       c = ';';\r
7565       t = strchr(s + 1, c);\r
7566       if (t == NULL) {\r
7567         c = '\n';\r
7568         t = strchr(s + 1, c);\r
7569       }\r
7570       if (t != NULL) *t = NULLCHAR;\r
7571       e->item = strdup(p);\r
7572       e->command = strdup(q + 1);\r
7573       e->getname = *(r + 1) != '0';\r
7574       e->immediate = *(s + 1) != '0';\r
7575       *q = ',';\r
7576       *r = ',';\r
7577       *s = ',';\r
7578       if (t == NULL) break;\r
7579       *t = c;\r
7580       p = t + 1;\r
7581     }\r
7582     e++;\r
7583   } \r
7584 }\r
7585 \r
7586 HMENU\r
7587 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7588 {\r
7589   HMENU hmenu, h;\r
7590   int i = 0;\r
7591   hmenu = LoadMenu(hInst, "TextMenu");\r
7592   h = GetSubMenu(hmenu, 0);\r
7593   while (e->item) {\r
7594     if (strcmp(e->item, "-") == 0) {\r
7595       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7596     } else {\r
7597       if (e->item[0] == '|') {\r
7598         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7599                    IDM_CommandX + i, &e->item[1]);\r
7600       } else {\r
7601         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7602       }\r
7603     }\r
7604     e++;\r
7605     i++;\r
7606   } \r
7607   return hmenu;\r
7608 }\r
7609 \r
7610 WNDPROC consoleTextWindowProc;\r
7611 \r
7612 void\r
7613 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7614 {\r
7615   char buf[MSG_SIZ], name[MSG_SIZ];\r
7616   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7617   CHARRANGE sel;\r
7618 \r
7619   if (!getname) {\r
7620     SetWindowText(hInput, command);\r
7621     if (immediate) {\r
7622       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7623     } else {\r
7624       sel.cpMin = 999999;\r
7625       sel.cpMax = 999999;\r
7626       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7627       SetFocus(hInput);\r
7628     }\r
7629     return;\r
7630   }    \r
7631   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7632   if (sel.cpMin == sel.cpMax) {\r
7633     /* Expand to surrounding word */\r
7634     TEXTRANGE tr;\r
7635     do {\r
7636       tr.chrg.cpMax = sel.cpMin;\r
7637       tr.chrg.cpMin = --sel.cpMin;\r
7638       if (sel.cpMin < 0) break;\r
7639       tr.lpstrText = name;\r
7640       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7641     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7642     sel.cpMin++;\r
7643 \r
7644     do {\r
7645       tr.chrg.cpMin = sel.cpMax;\r
7646       tr.chrg.cpMax = ++sel.cpMax;\r
7647       tr.lpstrText = name;\r
7648       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7649     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7650     sel.cpMax--;\r
7651 \r
7652     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7653       MessageBeep(MB_ICONEXCLAMATION);\r
7654       return;\r
7655     }\r
7656     tr.chrg = sel;\r
7657     tr.lpstrText = name;\r
7658     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7659   } else {\r
7660     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7661       MessageBeep(MB_ICONEXCLAMATION);\r
7662       return;\r
7663     }\r
7664     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7665   }\r
7666   if (immediate) {\r
7667     sprintf(buf, "%s %s", command, name);\r
7668     SetWindowText(hInput, buf);\r
7669     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7670   } else {\r
7671     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7672     SetWindowText(hInput, buf);\r
7673     sel.cpMin = 999999;\r
7674     sel.cpMax = 999999;\r
7675     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7676     SetFocus(hInput);\r
7677   }\r
7678 }\r
7679 \r
7680 LRESULT CALLBACK \r
7681 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7682 {\r
7683   HWND hInput;\r
7684   CHARRANGE sel;\r
7685 \r
7686   switch (message) {\r
7687   case WM_KEYDOWN:\r
7688     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7689     switch (wParam) {\r
7690     case VK_PRIOR:\r
7691       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7692       return 0;\r
7693     case VK_NEXT:\r
7694       sel.cpMin = 999999;\r
7695       sel.cpMax = 999999;\r
7696       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7697       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7698       return 0;\r
7699     }\r
7700     break;\r
7701   case WM_CHAR:\r
7702    if(wParam != '\022') {\r
7703     if (wParam == '\t') {\r
7704       if (GetKeyState(VK_SHIFT) < 0) {\r
7705         /* shifted */\r
7706         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7707         if (buttonDesc[0].hwnd) {\r
7708           SetFocus(buttonDesc[0].hwnd);\r
7709         } else {\r
7710           SetFocus(hwndMain);\r
7711         }\r
7712       } else {\r
7713         /* unshifted */\r
7714         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7715       }\r
7716     } else {\r
7717       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7718       JAWS_DELETE( SetFocus(hInput); )\r
7719       SendMessage(hInput, message, wParam, lParam);\r
7720     }\r
7721     return 0;\r
7722    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7723   case WM_RBUTTONUP:\r
7724     if (GetKeyState(VK_SHIFT) & ~1) {\r
7725       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7726         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7727     } else {\r
7728       POINT pt;\r
7729       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7730       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7731       if (sel.cpMin == sel.cpMax) {\r
7732         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7733         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7734       }\r
7735       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7736         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7737       }\r
7738       pt.x = LOWORD(lParam);\r
7739       pt.y = HIWORD(lParam);\r
7740       MenuPopup(hwnd, pt, hmenu, -1);\r
7741     }\r
7742     return 0;\r
7743   case WM_PASTE:\r
7744     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7745     SetFocus(hInput);\r
7746     return SendMessage(hInput, message, wParam, lParam);\r
7747   case WM_MBUTTONDOWN:\r
7748     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7749   case WM_RBUTTONDOWN:\r
7750     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7751       /* Move selection here if it was empty */\r
7752       POINT pt;\r
7753       pt.x = LOWORD(lParam);\r
7754       pt.y = HIWORD(lParam);\r
7755       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7756       if (sel.cpMin == sel.cpMax) {\r
7757         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7758         sel.cpMax = sel.cpMin;\r
7759         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7760       }\r
7761       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7762     }\r
7763     return 0;\r
7764   case WM_COMMAND:\r
7765     switch (LOWORD(wParam)) {\r
7766     case IDM_QuickPaste:\r
7767       {\r
7768         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7769         if (sel.cpMin == sel.cpMax) {\r
7770           MessageBeep(MB_ICONEXCLAMATION);\r
7771           return 0;\r
7772         }\r
7773         SendMessage(hwnd, WM_COPY, 0, 0);\r
7774         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7775         SendMessage(hInput, WM_PASTE, 0, 0);\r
7776         SetFocus(hInput);\r
7777         return 0;\r
7778       }\r
7779     case IDM_Cut:\r
7780       SendMessage(hwnd, WM_CUT, 0, 0);\r
7781       return 0;\r
7782     case IDM_Paste:\r
7783       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7784       return 0;\r
7785     case IDM_Copy:\r
7786       SendMessage(hwnd, WM_COPY, 0, 0);\r
7787       return 0;\r
7788     default:\r
7789       {\r
7790         int i = LOWORD(wParam) - IDM_CommandX;\r
7791         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
7792             icsTextMenuEntry[i].command != NULL) {\r
7793           CommandX(hwnd, icsTextMenuEntry[i].command,\r
7794                    icsTextMenuEntry[i].getname,\r
7795                    icsTextMenuEntry[i].immediate);\r
7796           return 0;\r
7797         }\r
7798       }\r
7799       break;\r
7800     }\r
7801     break;\r
7802   }\r
7803   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
7804 }\r
7805 \r
7806 WNDPROC consoleInputWindowProc;\r
7807 \r
7808 LRESULT CALLBACK\r
7809 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7810 {\r
7811   char buf[MSG_SIZ];\r
7812   char *p;\r
7813   static BOOL sendNextChar = FALSE;\r
7814   static BOOL quoteNextChar = FALSE;\r
7815   InputSource *is = consoleInputSource;\r
7816   CHARFORMAT cf;\r
7817   CHARRANGE sel;\r
7818 \r
7819   switch (message) {\r
7820   case WM_CHAR:\r
7821     if (!appData.localLineEditing || sendNextChar) {\r
7822       is->buf[0] = (CHAR) wParam;\r
7823       is->count = 1;\r
7824       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7825       sendNextChar = FALSE;\r
7826       return 0;\r
7827     }\r
7828     if (quoteNextChar) {\r
7829       buf[0] = (char) wParam;\r
7830       buf[1] = NULLCHAR;\r
7831       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
7832       quoteNextChar = FALSE;\r
7833       return 0;\r
7834     }\r
7835     switch (wParam) {\r
7836     case '\r':   /* Enter key */\r
7837       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
7838       if (consoleEcho) SaveInHistory(is->buf);\r
7839       is->buf[is->count++] = '\n';\r
7840       is->buf[is->count] = NULLCHAR;\r
7841       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
7842       if (consoleEcho) {\r
7843         ConsoleOutput(is->buf, is->count, TRUE);\r
7844       } else if (appData.localLineEditing) {\r
7845         ConsoleOutput("\n", 1, TRUE);\r
7846       }\r
7847       /* fall thru */\r
7848     case '\033': /* Escape key */\r
7849       SetWindowText(hwnd, "");\r
7850       cf.cbSize = sizeof(CHARFORMAT);\r
7851       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
7852       if (consoleEcho) {\r
7853         cf.crTextColor = textAttribs[ColorNormal].color;\r
7854       } else {\r
7855         cf.crTextColor = COLOR_ECHOOFF;\r
7856       }\r
7857       cf.dwEffects = textAttribs[ColorNormal].effects;\r
7858       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
7859       return 0;\r
7860     case '\t':   /* Tab key */\r
7861       if (GetKeyState(VK_SHIFT) < 0) {\r
7862         /* shifted */\r
7863         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
7864       } else {\r
7865         /* unshifted */\r
7866         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7867         if (buttonDesc[0].hwnd) {\r
7868           SetFocus(buttonDesc[0].hwnd);\r
7869         } else {\r
7870           SetFocus(hwndMain);\r
7871         }\r
7872       }\r
7873       return 0;\r
7874     case '\023': /* Ctrl+S */\r
7875       sendNextChar = TRUE;\r
7876       return 0;\r
7877     case '\021': /* Ctrl+Q */\r
7878       quoteNextChar = TRUE;\r
7879       return 0;\r
7880     JAWS_REPLAY\r
7881     default:\r
7882       break;\r
7883     }\r
7884     break;\r
7885   case WM_KEYDOWN:\r
7886     switch (wParam) {\r
7887     case VK_UP:\r
7888       GetWindowText(hwnd, buf, MSG_SIZ);\r
7889       p = PrevInHistory(buf);\r
7890       if (p != NULL) {\r
7891         SetWindowText(hwnd, p);\r
7892         sel.cpMin = 999999;\r
7893         sel.cpMax = 999999;\r
7894         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7895         return 0;\r
7896       }\r
7897       break;\r
7898     case VK_DOWN:\r
7899       p = NextInHistory();\r
7900       if (p != NULL) {\r
7901         SetWindowText(hwnd, p);\r
7902         sel.cpMin = 999999;\r
7903         sel.cpMax = 999999;\r
7904         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7905         return 0;\r
7906       }\r
7907       break;\r
7908     case VK_HOME:\r
7909     case VK_END:\r
7910       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7911       /* fall thru */\r
7912     case VK_PRIOR:\r
7913     case VK_NEXT:\r
7914       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
7915       return 0;\r
7916     }\r
7917     break;\r
7918   case WM_MBUTTONDOWN:\r
7919     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7920       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7921     break;\r
7922   case WM_RBUTTONUP:\r
7923     if (GetKeyState(VK_SHIFT) & ~1) {\r
7924       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7925         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7926     } else {\r
7927       POINT pt;\r
7928       HMENU hmenu;\r
7929       hmenu = LoadMenu(hInst, "InputMenu");\r
7930       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7931       if (sel.cpMin == sel.cpMax) {\r
7932         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7933         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
7934       }\r
7935       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7936         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7937       }\r
7938       pt.x = LOWORD(lParam);\r
7939       pt.y = HIWORD(lParam);\r
7940       MenuPopup(hwnd, pt, hmenu, -1);\r
7941     }\r
7942     return 0;\r
7943   case WM_COMMAND:\r
7944     switch (LOWORD(wParam)) { \r
7945     case IDM_Undo:\r
7946       SendMessage(hwnd, EM_UNDO, 0, 0);\r
7947       return 0;\r
7948     case IDM_SelectAll:\r
7949       sel.cpMin = 0;\r
7950       sel.cpMax = -1; /*999999?*/\r
7951       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7952       return 0;\r
7953     case IDM_Cut:\r
7954       SendMessage(hwnd, WM_CUT, 0, 0);\r
7955       return 0;\r
7956     case IDM_Paste:\r
7957       SendMessage(hwnd, WM_PASTE, 0, 0);\r
7958       return 0;\r
7959     case IDM_Copy:\r
7960       SendMessage(hwnd, WM_COPY, 0, 0);\r
7961       return 0;\r
7962     }\r
7963     break;\r
7964   }\r
7965   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
7966 }\r
7967 \r
7968 #define CO_MAX  100000\r
7969 #define CO_TRIM   1000\r
7970 \r
7971 LRESULT CALLBACK\r
7972 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7973 {\r
7974   static SnapData sd;\r
7975   HWND hText, hInput;\r
7976   RECT rect;\r
7977   static int sizeX, sizeY;\r
7978   int newSizeX, newSizeY;\r
7979   MINMAXINFO *mmi;\r
7980   WORD wMask;\r
7981 \r
7982   hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
7983   hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
7984 \r
7985   switch (message) {\r
7986   case WM_NOTIFY:\r
7987     if (((NMHDR*)lParam)->code == EN_LINK)\r
7988     {\r
7989       ENLINK *pLink = (ENLINK*)lParam;\r
7990       if (pLink->msg == WM_LBUTTONUP)\r
7991       {\r
7992         TEXTRANGE tr;\r
7993 \r
7994         tr.chrg = pLink->chrg;\r
7995         tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin);\r
7996         SendMessage(hText, EM_GETTEXTRANGE, 0, (LPARAM)&tr);\r
7997         ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW);\r
7998         free(tr.lpstrText);\r
7999       }\r
8000     }\r
8001     break;\r
8002   case WM_INITDIALOG: /* message: initialize dialog box */\r
8003     hwndConsole = hDlg;\r
8004     SetFocus(hInput);\r
8005     consoleTextWindowProc = (WNDPROC)\r
8006       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8007     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8008     consoleInputWindowProc = (WNDPROC)\r
8009       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8010     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8011     Colorize(ColorNormal, TRUE);\r
8012     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8013     ChangedConsoleFont();\r
8014     GetClientRect(hDlg, &rect);\r
8015     sizeX = rect.right;\r
8016     sizeY = rect.bottom;\r
8017     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8018         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8019       WINDOWPLACEMENT wp;\r
8020       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8021       wp.length = sizeof(WINDOWPLACEMENT);\r
8022       wp.flags = 0;\r
8023       wp.showCmd = SW_SHOW;\r
8024       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8025       wp.rcNormalPosition.left = wpConsole.x;\r
8026       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8027       wp.rcNormalPosition.top = wpConsole.y;\r
8028       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8029       SetWindowPlacement(hDlg, &wp);\r
8030     }\r
8031 \r
8032    // [HGM] Chessknight's change 2004-07-13\r
8033    else { /* Determine Defaults */\r
8034        WINDOWPLACEMENT wp;\r
8035        wpConsole.x = winWidth + 1;\r
8036        wpConsole.y = boardY;\r
8037        wpConsole.width = screenWidth -  winWidth;\r
8038        wpConsole.height = winHeight;\r
8039        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8040        wp.length = sizeof(WINDOWPLACEMENT);\r
8041        wp.flags = 0;\r
8042        wp.showCmd = SW_SHOW;\r
8043        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8044        wp.rcNormalPosition.left = wpConsole.x;\r
8045        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8046        wp.rcNormalPosition.top = wpConsole.y;\r
8047        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8048        SetWindowPlacement(hDlg, &wp);\r
8049     }\r
8050 \r
8051    // Allow hText to highlight URLs and send notifications on them\r
8052    wMask = (WORD) SendMessage(hText, EM_GETEVENTMASK, 0, 0L);\r
8053    SendMessage(hText, EM_SETEVENTMASK, 0, wMask | ENM_LINK);\r
8054    SendMessage(hText, EM_AUTOURLDETECT, TRUE, 0L);\r
8055    SetWindowLong(hText, GWL_USERDATA, 79); // initialize the text window's width\r
8056 \r
8057     return FALSE;\r
8058 \r
8059   case WM_SETFOCUS:\r
8060     SetFocus(hInput);\r
8061     return 0;\r
8062 \r
8063   case WM_CLOSE:\r
8064     ExitEvent(0);\r
8065     /* not reached */\r
8066     break;\r
8067 \r
8068   case WM_SIZE:\r
8069     if (IsIconic(hDlg)) break;\r
8070     newSizeX = LOWORD(lParam);\r
8071     newSizeY = HIWORD(lParam);\r
8072     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8073       RECT rectText, rectInput;\r
8074       POINT pt;\r
8075       int newTextHeight, newTextWidth;\r
8076       GetWindowRect(hText, &rectText);\r
8077       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8078       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8079       if (newTextHeight < 0) {\r
8080         newSizeY += -newTextHeight;\r
8081         newTextHeight = 0;\r
8082       }\r
8083       SetWindowPos(hText, NULL, 0, 0,\r
8084         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8085       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8086       pt.x = rectInput.left;\r
8087       pt.y = rectInput.top + newSizeY - sizeY;\r
8088       ScreenToClient(hDlg, &pt);\r
8089       SetWindowPos(hInput, NULL, \r
8090         pt.x, pt.y, /* needs client coords */   \r
8091         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8092         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8093     }\r
8094     sizeX = newSizeX;\r
8095     sizeY = newSizeY;\r
8096     break;\r
8097 \r
8098   case WM_GETMINMAXINFO:\r
8099     /* Prevent resizing window too small */\r
8100     mmi = (MINMAXINFO *) lParam;\r
8101     mmi->ptMinTrackSize.x = 100;\r
8102     mmi->ptMinTrackSize.y = 100;\r
8103     break;\r
8104 \r
8105   /* [AS] Snapping */\r
8106   case WM_ENTERSIZEMOVE:\r
8107     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8108 \r
8109   case WM_SIZING:\r
8110     return OnSizing( &sd, hDlg, wParam, lParam );\r
8111 \r
8112   case WM_MOVING:\r
8113     return OnMoving( &sd, hDlg, wParam, lParam );\r
8114 \r
8115   case WM_EXITSIZEMOVE:\r
8116         UpdateICSWidth(hText);\r
8117     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8118   }\r
8119 \r
8120   return DefWindowProc(hDlg, message, wParam, lParam);\r
8121 }\r
8122 \r
8123 \r
8124 VOID\r
8125 ConsoleCreate()\r
8126 {\r
8127   HWND hCons;\r
8128   if (hwndConsole) return;\r
8129   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8130   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8131 }\r
8132 \r
8133 \r
8134 VOID\r
8135 ConsoleOutput(char* data, int length, int forceVisible)\r
8136 {\r
8137   HWND hText;\r
8138   int trim, exlen;\r
8139   char *p, *q;\r
8140   char buf[CO_MAX+1];\r
8141   POINT pEnd;\r
8142   RECT rect;\r
8143   static int delayLF = 0;\r
8144   CHARRANGE savesel, sel;\r
8145 \r
8146   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8147   p = data;\r
8148   q = buf;\r
8149   if (delayLF) {\r
8150     *q++ = '\r';\r
8151     *q++ = '\n';\r
8152     delayLF = 0;\r
8153   }\r
8154   while (length--) {\r
8155     if (*p == '\n') {\r
8156       if (*++p) {\r
8157         *q++ = '\r';\r
8158         *q++ = '\n';\r
8159       } else {\r
8160         delayLF = 1;\r
8161       }\r
8162     } else if (*p == '\007') {\r
8163        MyPlaySound(&sounds[(int)SoundBell]);\r
8164        p++;\r
8165     } else {\r
8166       *q++ = *p++;\r
8167     }\r
8168   }\r
8169   *q = NULLCHAR;\r
8170   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8171   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8172   /* Save current selection */\r
8173   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8174   exlen = GetWindowTextLength(hText);\r
8175   /* Find out whether current end of text is visible */\r
8176   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8177   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8178   /* Trim existing text if it's too long */\r
8179   if (exlen + (q - buf) > CO_MAX) {\r
8180     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8181     sel.cpMin = 0;\r
8182     sel.cpMax = trim;\r
8183     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8184     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8185     exlen -= trim;\r
8186     savesel.cpMin -= trim;\r
8187     savesel.cpMax -= trim;\r
8188     if (exlen < 0) exlen = 0;\r
8189     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8190     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8191   }\r
8192   /* Append the new text */\r
8193   sel.cpMin = exlen;\r
8194   sel.cpMax = exlen;\r
8195   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8196   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8197   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8198   if (forceVisible || exlen == 0 ||\r
8199       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8200        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8201     /* Scroll to make new end of text visible if old end of text\r
8202        was visible or new text is an echo of user typein */\r
8203     sel.cpMin = 9999999;\r
8204     sel.cpMax = 9999999;\r
8205     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8206     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8207     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8208     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8209   }\r
8210   if (savesel.cpMax == exlen || forceVisible) {\r
8211     /* Move insert point to new end of text if it was at the old\r
8212        end of text or if the new text is an echo of user typein */\r
8213     sel.cpMin = 9999999;\r
8214     sel.cpMax = 9999999;\r
8215     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8216   } else {\r
8217     /* Restore previous selection */\r
8218     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8219   }\r
8220   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8221 }\r
8222 \r
8223 /*---------*/\r
8224 \r
8225 \r
8226 void\r
8227 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8228 {\r
8229   char buf[100];\r
8230   char *str;\r
8231   COLORREF oldFg, oldBg;\r
8232   HFONT oldFont;\r
8233   RECT rect;\r
8234 \r
8235   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8236 \r
8237   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8238   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8239   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8240 \r
8241   rect.left = x;\r
8242   rect.right = x + squareSize;\r
8243   rect.top  = y;\r
8244   rect.bottom = y + squareSize;\r
8245   str = buf;\r
8246 \r
8247   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8248                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8249              y, ETO_CLIPPED|ETO_OPAQUE,\r
8250              &rect, str, strlen(str), NULL);\r
8251 \r
8252   (void) SetTextColor(hdc, oldFg);\r
8253   (void) SetBkColor(hdc, oldBg);\r
8254   (void) SelectObject(hdc, oldFont);\r
8255 }\r
8256 \r
8257 void\r
8258 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8259               RECT *rect, char *color, char *flagFell)\r
8260 {\r
8261   char buf[100];\r
8262   char *str;\r
8263   COLORREF oldFg, oldBg;\r
8264   HFONT oldFont;\r
8265 \r
8266   if (appData.clockMode) {\r
8267     if (tinyLayout)\r
8268       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8269     else\r
8270       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8271     str = buf;\r
8272   } else {\r
8273     str = color;\r
8274   }\r
8275 \r
8276   if (highlight) {\r
8277     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8278     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8279   } else {\r
8280     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8281     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8282   }\r
8283   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8284 \r
8285   JAWS_SILENCE\r
8286 \r
8287   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8288              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8289              rect, str, strlen(str), NULL);\r
8290   if(logoHeight > 0 && appData.clockMode) {\r
8291       RECT r;\r
8292       sprintf(buf, "%s %s", buf+7, flagFell);\r
8293       r.top = rect->top + logoHeight/2;\r
8294       r.left = rect->left;\r
8295       r.right = rect->right;\r
8296       r.bottom = rect->bottom;\r
8297       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8298                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8299                  &r, str, strlen(str), NULL);\r
8300   }\r
8301   (void) SetTextColor(hdc, oldFg);\r
8302   (void) SetBkColor(hdc, oldBg);\r
8303   (void) SelectObject(hdc, oldFont);\r
8304 }\r
8305 \r
8306 \r
8307 int\r
8308 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8309            OVERLAPPED *ovl)\r
8310 {\r
8311   int ok, err;\r
8312 \r
8313   /* [AS]  */\r
8314   if( count <= 0 ) {\r
8315     if (appData.debugMode) {\r
8316       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8317     }\r
8318 \r
8319     return ERROR_INVALID_USER_BUFFER;\r
8320   }\r
8321 \r
8322   ResetEvent(ovl->hEvent);\r
8323   ovl->Offset = ovl->OffsetHigh = 0;\r
8324   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8325   if (ok) {\r
8326     err = NO_ERROR;\r
8327   } else {\r
8328     err = GetLastError();\r
8329     if (err == ERROR_IO_PENDING) {\r
8330       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8331       if (ok)\r
8332         err = NO_ERROR;\r
8333       else\r
8334         err = GetLastError();\r
8335     }\r
8336   }\r
8337   return err;\r
8338 }\r
8339 \r
8340 int\r
8341 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8342             OVERLAPPED *ovl)\r
8343 {\r
8344   int ok, err;\r
8345 \r
8346   ResetEvent(ovl->hEvent);\r
8347   ovl->Offset = ovl->OffsetHigh = 0;\r
8348   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8349   if (ok) {\r
8350     err = NO_ERROR;\r
8351   } else {\r
8352     err = GetLastError();\r
8353     if (err == ERROR_IO_PENDING) {\r
8354       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8355       if (ok)\r
8356         err = NO_ERROR;\r
8357       else\r
8358         err = GetLastError();\r
8359     }\r
8360   }\r
8361   return err;\r
8362 }\r
8363 \r
8364 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8365 void CheckForInputBufferFull( InputSource * is )\r
8366 {\r
8367     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8368         /* Look for end of line */\r
8369         char * p = is->buf;\r
8370         \r
8371         while( p < is->next && *p != '\n' ) {\r
8372             p++;\r
8373         }\r
8374 \r
8375         if( p >= is->next ) {\r
8376             if (appData.debugMode) {\r
8377                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8378             }\r
8379 \r
8380             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8381             is->count = (DWORD) -1;\r
8382             is->next = is->buf;\r
8383         }\r
8384     }\r
8385 }\r
8386 \r
8387 DWORD\r
8388 InputThread(LPVOID arg)\r
8389 {\r
8390   InputSource *is;\r
8391   OVERLAPPED ovl;\r
8392 \r
8393   is = (InputSource *) arg;\r
8394   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8395   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8396   while (is->hThread != NULL) {\r
8397     is->error = DoReadFile(is->hFile, is->next,\r
8398                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8399                            &is->count, &ovl);\r
8400     if (is->error == NO_ERROR) {\r
8401       is->next += is->count;\r
8402     } else {\r
8403       if (is->error == ERROR_BROKEN_PIPE) {\r
8404         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8405         is->count = 0;\r
8406       } else {\r
8407         is->count = (DWORD) -1;\r
8408         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8409         break; \r
8410       }\r
8411     }\r
8412 \r
8413     CheckForInputBufferFull( is );\r
8414 \r
8415     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8416 \r
8417     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8418 \r
8419     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8420   }\r
8421 \r
8422   CloseHandle(ovl.hEvent);\r
8423   CloseHandle(is->hFile);\r
8424 \r
8425   if (appData.debugMode) {\r
8426     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8427   }\r
8428 \r
8429   return 0;\r
8430 }\r
8431 \r
8432 \r
8433 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8434 DWORD\r
8435 NonOvlInputThread(LPVOID arg)\r
8436 {\r
8437   InputSource *is;\r
8438   char *p, *q;\r
8439   int i;\r
8440   char prev;\r
8441 \r
8442   is = (InputSource *) arg;\r
8443   while (is->hThread != NULL) {\r
8444     is->error = ReadFile(is->hFile, is->next,\r
8445                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8446                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8447     if (is->error == NO_ERROR) {\r
8448       /* Change CRLF to LF */\r
8449       if (is->next > is->buf) {\r
8450         p = is->next - 1;\r
8451         i = is->count + 1;\r
8452       } else {\r
8453         p = is->next;\r
8454         i = is->count;\r
8455       }\r
8456       q = p;\r
8457       prev = NULLCHAR;\r
8458       while (i > 0) {\r
8459         if (prev == '\r' && *p == '\n') {\r
8460           *(q-1) = '\n';\r
8461           is->count--;\r
8462         } else { \r
8463           *q++ = *p;\r
8464         }\r
8465         prev = *p++;\r
8466         i--;\r
8467       }\r
8468       *q = NULLCHAR;\r
8469       is->next = q;\r
8470     } else {\r
8471       if (is->error == ERROR_BROKEN_PIPE) {\r
8472         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8473         is->count = 0; \r
8474       } else {\r
8475         is->count = (DWORD) -1;\r
8476       }\r
8477     }\r
8478 \r
8479     CheckForInputBufferFull( is );\r
8480 \r
8481     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8482 \r
8483     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8484 \r
8485     if (is->count < 0) break;  /* Quit on error */\r
8486   }\r
8487   CloseHandle(is->hFile);\r
8488   return 0;\r
8489 }\r
8490 \r
8491 DWORD\r
8492 SocketInputThread(LPVOID arg)\r
8493 {\r
8494   InputSource *is;\r
8495 \r
8496   is = (InputSource *) arg;\r
8497   while (is->hThread != NULL) {\r
8498     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8499     if ((int)is->count == SOCKET_ERROR) {\r
8500       is->count = (DWORD) -1;\r
8501       is->error = WSAGetLastError();\r
8502     } else {\r
8503       is->error = NO_ERROR;\r
8504       is->next += is->count;\r
8505       if (is->count == 0 && is->second == is) {\r
8506         /* End of file on stderr; quit with no message */\r
8507         break;\r
8508       }\r
8509     }\r
8510     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8511 \r
8512     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8513 \r
8514     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8515   }\r
8516   return 0;\r
8517 }\r
8518 \r
8519 VOID\r
8520 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8521 {\r
8522   InputSource *is;\r
8523 \r
8524   is = (InputSource *) lParam;\r
8525   if (is->lineByLine) {\r
8526     /* Feed in lines one by one */\r
8527     char *p = is->buf;\r
8528     char *q = p;\r
8529     while (q < is->next) {\r
8530       if (*q++ == '\n') {\r
8531         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8532         p = q;\r
8533       }\r
8534     }\r
8535     \r
8536     /* Move any partial line to the start of the buffer */\r
8537     q = is->buf;\r
8538     while (p < is->next) {\r
8539       *q++ = *p++;\r
8540     }\r
8541     is->next = q;\r
8542 \r
8543     if (is->error != NO_ERROR || is->count == 0) {\r
8544       /* Notify backend of the error.  Note: If there was a partial\r
8545          line at the end, it is not flushed through. */\r
8546       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8547     }\r
8548   } else {\r
8549     /* Feed in the whole chunk of input at once */\r
8550     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8551     is->next = is->buf;\r
8552   }\r
8553 }\r
8554 \r
8555 /*---------------------------------------------------------------------------*\\r
8556  *\r
8557  *  Menu enables. Used when setting various modes.\r
8558  *\r
8559 \*---------------------------------------------------------------------------*/\r
8560 \r
8561 typedef struct {\r
8562   int item;\r
8563   int flags;\r
8564 } Enables;\r
8565 \r
8566 VOID\r
8567 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8568 {\r
8569   while (enab->item > 0) {\r
8570     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8571     enab++;\r
8572   }\r
8573 }\r
8574 \r
8575 Enables gnuEnables[] = {\r
8576   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8577   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8578   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8579   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8580   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8581   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8582   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8583   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8584   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8585   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8586   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8587   { -1, -1 }\r
8588 };\r
8589 \r
8590 Enables icsEnables[] = {\r
8591   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8592   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8593   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8594   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8595   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8596   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8597   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8598   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8599   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8600   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8601   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8602   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8603   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8604   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8605   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8606   { -1, -1 }\r
8607 };\r
8608 \r
8609 #ifdef ZIPPY\r
8610 Enables zippyEnables[] = {\r
8611   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8612   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8613   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8614   { IDM_Engine1Options, MF_BYCOMMAND|MF_ENABLED },\r
8615   { -1, -1 }\r
8616 };\r
8617 #endif\r
8618 \r
8619 Enables ncpEnables[] = {\r
8620   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8621   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8622   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8623   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8624   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8625   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8626   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8627   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8628   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8629   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8630   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8631   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8632   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8633   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8634   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8635   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
8636   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
8637   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
8638   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
8639   { -1, -1 }\r
8640 };\r
8641 \r
8642 Enables trainingOnEnables[] = {\r
8643   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8644   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8645   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8646   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8647   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8648   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8649   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8650   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8651   { -1, -1 }\r
8652 };\r
8653 \r
8654 Enables trainingOffEnables[] = {\r
8655   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8656   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8657   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8658   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8659   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8660   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8661   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8662   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8663   { -1, -1 }\r
8664 };\r
8665 \r
8666 /* These modify either ncpEnables or gnuEnables */\r
8667 Enables cmailEnables[] = {\r
8668   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8669   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8670   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8671   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8672   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8673   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8674   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8675   { -1, -1 }\r
8676 };\r
8677 \r
8678 Enables machineThinkingEnables[] = {\r
8679   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8680   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8681   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8682   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8683   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8684   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8685   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8686   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8687   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8688   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8689   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8690   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8691   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8692   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8693   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8694   { -1, -1 }\r
8695 };\r
8696 \r
8697 Enables userThinkingEnables[] = {\r
8698   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8699   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8700   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8701   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8702   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8703   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8704   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8705   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8706   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8707   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8708   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8709   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8710   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8711   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8712   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8713   { -1, -1 }\r
8714 };\r
8715 \r
8716 /*---------------------------------------------------------------------------*\\r
8717  *\r
8718  *  Front-end interface functions exported by XBoard.\r
8719  *  Functions appear in same order as prototypes in frontend.h.\r
8720  * \r
8721 \*---------------------------------------------------------------------------*/\r
8722 VOID\r
8723 ModeHighlight()\r
8724 {\r
8725   static UINT prevChecked = 0;\r
8726   static int prevPausing = 0;\r
8727   UINT nowChecked;\r
8728 \r
8729   if (pausing != prevPausing) {\r
8730     prevPausing = pausing;\r
8731     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8732                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8733     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8734   }\r
8735 \r
8736   switch (gameMode) {\r
8737   case BeginningOfGame:\r
8738     if (appData.icsActive)\r
8739       nowChecked = IDM_IcsClient;\r
8740     else if (appData.noChessProgram)\r
8741       nowChecked = IDM_EditGame;\r
8742     else\r
8743       nowChecked = IDM_MachineBlack;\r
8744     break;\r
8745   case MachinePlaysBlack:\r
8746     nowChecked = IDM_MachineBlack;\r
8747     break;\r
8748   case MachinePlaysWhite:\r
8749     nowChecked = IDM_MachineWhite;\r
8750     break;\r
8751   case TwoMachinesPlay:\r
8752     nowChecked = IDM_TwoMachines;\r
8753     break;\r
8754   case AnalyzeMode:\r
8755     nowChecked = IDM_AnalysisMode;\r
8756     break;\r
8757   case AnalyzeFile:\r
8758     nowChecked = IDM_AnalyzeFile;\r
8759     break;\r
8760   case EditGame:\r
8761     nowChecked = IDM_EditGame;\r
8762     break;\r
8763   case PlayFromGameFile:\r
8764     nowChecked = IDM_LoadGame;\r
8765     break;\r
8766   case EditPosition:\r
8767     nowChecked = IDM_EditPosition;\r
8768     break;\r
8769   case Training:\r
8770     nowChecked = IDM_Training;\r
8771     break;\r
8772   case IcsPlayingWhite:\r
8773   case IcsPlayingBlack:\r
8774   case IcsObserving:\r
8775   case IcsIdle:\r
8776     nowChecked = IDM_IcsClient;\r
8777     break;\r
8778   default:\r
8779   case EndOfGame:\r
8780     nowChecked = 0;\r
8781     break;\r
8782   }\r
8783   if (prevChecked != 0)\r
8784     (void) CheckMenuItem(GetMenu(hwndMain),\r
8785                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8786   if (nowChecked != 0)\r
8787     (void) CheckMenuItem(GetMenu(hwndMain),\r
8788                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8789 \r
8790   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8791     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8792                           MF_BYCOMMAND|MF_ENABLED);\r
8793   } else {\r
8794     (void) EnableMenuItem(GetMenu(hwndMain), \r
8795                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8796   }\r
8797 \r
8798   prevChecked = nowChecked;\r
8799 \r
8800   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
8801   if (appData.icsActive) {\r
8802        if (appData.icsEngineAnalyze) {\r
8803                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8804                        MF_BYCOMMAND|MF_CHECKED);\r
8805        } else {\r
8806                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8807                        MF_BYCOMMAND|MF_UNCHECKED);\r
8808        }\r
8809   }\r
8810 }\r
8811 \r
8812 VOID\r
8813 SetICSMode()\r
8814 {\r
8815   HMENU hmenu = GetMenu(hwndMain);\r
8816   SetMenuEnables(hmenu, icsEnables);\r
8817   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
8818     MF_BYPOSITION|MF_ENABLED);\r
8819 #ifdef ZIPPY\r
8820   if (appData.zippyPlay) {\r
8821     SetMenuEnables(hmenu, zippyEnables);\r
8822     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
8823          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
8824           MF_BYCOMMAND|MF_ENABLED);\r
8825   }\r
8826 #endif\r
8827 }\r
8828 \r
8829 VOID\r
8830 SetGNUMode()\r
8831 {\r
8832   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
8833 }\r
8834 \r
8835 VOID\r
8836 SetNCPMode()\r
8837 {\r
8838   HMENU hmenu = GetMenu(hwndMain);\r
8839   SetMenuEnables(hmenu, ncpEnables);\r
8840   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
8841     MF_BYPOSITION|MF_GRAYED);\r
8842     DrawMenuBar(hwndMain);\r
8843 }\r
8844 \r
8845 VOID\r
8846 SetCmailMode()\r
8847 {\r
8848   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
8849 }\r
8850 \r
8851 VOID \r
8852 SetTrainingModeOn()\r
8853 {\r
8854   int i;\r
8855   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
8856   for (i = 0; i < N_BUTTONS; i++) {\r
8857     if (buttonDesc[i].hwnd != NULL)\r
8858       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
8859   }\r
8860   CommentPopDown();\r
8861 }\r
8862 \r
8863 VOID SetTrainingModeOff()\r
8864 {\r
8865   int i;\r
8866   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
8867   for (i = 0; i < N_BUTTONS; i++) {\r
8868     if (buttonDesc[i].hwnd != NULL)\r
8869       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
8870   }\r
8871 }\r
8872 \r
8873 \r
8874 VOID\r
8875 SetUserThinkingEnables()\r
8876 {\r
8877   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
8878 }\r
8879 \r
8880 VOID\r
8881 SetMachineThinkingEnables()\r
8882 {\r
8883   HMENU hMenu = GetMenu(hwndMain);\r
8884   int flags = MF_BYCOMMAND|MF_ENABLED;\r
8885 \r
8886   SetMenuEnables(hMenu, machineThinkingEnables);\r
8887 \r
8888   if (gameMode == MachinePlaysBlack) {\r
8889     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
8890   } else if (gameMode == MachinePlaysWhite) {\r
8891     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
8892   } else if (gameMode == TwoMachinesPlay) {\r
8893     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
8894   }\r
8895 }\r
8896 \r
8897 \r
8898 VOID\r
8899 DisplayTitle(char *str)\r
8900 {\r
8901   char title[MSG_SIZ], *host;\r
8902   if (str[0] != NULLCHAR) {\r
8903     strcpy(title, str);\r
8904   } else if (appData.icsActive) {\r
8905     if (appData.icsCommPort[0] != NULLCHAR)\r
8906       host = "ICS";\r
8907     else \r
8908       host = appData.icsHost;\r
8909     sprintf(title, "%s: %s", szTitle, host);\r
8910   } else if (appData.noChessProgram) {\r
8911     strcpy(title, szTitle);\r
8912   } else {\r
8913     strcpy(title, szTitle);\r
8914     strcat(title, ": ");\r
8915     strcat(title, first.tidy);\r
8916   }\r
8917   SetWindowText(hwndMain, title);\r
8918 }\r
8919 \r
8920 \r
8921 VOID\r
8922 DisplayMessage(char *str1, char *str2)\r
8923 {\r
8924   HDC hdc;\r
8925   HFONT oldFont;\r
8926   int remain = MESSAGE_TEXT_MAX - 1;\r
8927   int len;\r
8928 \r
8929   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
8930   messageText[0] = NULLCHAR;\r
8931   if (*str1) {\r
8932     len = strlen(str1);\r
8933     if (len > remain) len = remain;\r
8934     strncpy(messageText, str1, len);\r
8935     messageText[len] = NULLCHAR;\r
8936     remain -= len;\r
8937   }\r
8938   if (*str2 && remain >= 2) {\r
8939     if (*str1) {\r
8940       strcat(messageText, "  ");\r
8941       remain -= 2;\r
8942     }\r
8943     len = strlen(str2);\r
8944     if (len > remain) len = remain;\r
8945     strncat(messageText, str2, len);\r
8946   }\r
8947   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
8948 \r
8949   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
8950 \r
8951   SAYMACHINEMOVE();\r
8952 \r
8953   hdc = GetDC(hwndMain);\r
8954   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
8955   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
8956              &messageRect, messageText, strlen(messageText), NULL);\r
8957   (void) SelectObject(hdc, oldFont);\r
8958   (void) ReleaseDC(hwndMain, hdc);\r
8959 }\r
8960 \r
8961 VOID\r
8962 DisplayError(char *str, int error)\r
8963 {\r
8964   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
8965   int len;\r
8966 \r
8967   if (error == 0) {\r
8968     strcpy(buf, str);\r
8969   } else {\r
8970     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
8971                         NULL, error, LANG_NEUTRAL,\r
8972                         (LPSTR) buf2, MSG_SIZ, NULL);\r
8973     if (len > 0) {\r
8974       sprintf(buf, "%s:\n%s", str, buf2);\r
8975     } else {\r
8976       ErrorMap *em = errmap;\r
8977       while (em->err != 0 && em->err != error) em++;\r
8978       if (em->err != 0) {\r
8979         sprintf(buf, "%s:\n%s", str, em->msg);\r
8980       } else {\r
8981         sprintf(buf, "%s:\nError code %d", str, error);\r
8982       }\r
8983     }\r
8984   }\r
8985   \r
8986   ErrorPopUp("Error", buf);\r
8987 }\r
8988 \r
8989 \r
8990 VOID\r
8991 DisplayMoveError(char *str)\r
8992 {\r
8993   fromX = fromY = -1;\r
8994   ClearHighlights();\r
8995   DrawPosition(FALSE, NULL);\r
8996   if (appData.popupMoveErrors) {\r
8997     ErrorPopUp("Error", str);\r
8998   } else {\r
8999     DisplayMessage(str, "");\r
9000     moveErrorMessageUp = TRUE;\r
9001   }\r
9002 }\r
9003 \r
9004 VOID\r
9005 DisplayFatalError(char *str, int error, int exitStatus)\r
9006 {\r
9007   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9008   int len;\r
9009   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9010 \r
9011   if (error != 0) {\r
9012     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9013                         NULL, error, LANG_NEUTRAL,\r
9014                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9015     if (len > 0) {\r
9016       sprintf(buf, "%s:\n%s", str, buf2);\r
9017     } else {\r
9018       ErrorMap *em = errmap;\r
9019       while (em->err != 0 && em->err != error) em++;\r
9020       if (em->err != 0) {\r
9021         sprintf(buf, "%s:\n%s", str, em->msg);\r
9022       } else {\r
9023         sprintf(buf, "%s:\nError code %d", str, error);\r
9024       }\r
9025     }\r
9026     str = buf;\r
9027   }\r
9028   if (appData.debugMode) {\r
9029     fprintf(debugFP, "%s: %s\n", label, str);\r
9030   }\r
9031   if (appData.popupExitMessage) {\r
9032     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9033                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9034   }\r
9035   ExitEvent(exitStatus);\r
9036 }\r
9037 \r
9038 \r
9039 VOID\r
9040 DisplayInformation(char *str)\r
9041 {\r
9042   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9043 }\r
9044 \r
9045 \r
9046 VOID\r
9047 DisplayNote(char *str)\r
9048 {\r
9049   ErrorPopUp("Note", str);\r
9050 }\r
9051 \r
9052 \r
9053 typedef struct {\r
9054   char *title, *question, *replyPrefix;\r
9055   ProcRef pr;\r
9056 } QuestionParams;\r
9057 \r
9058 LRESULT CALLBACK\r
9059 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9060 {\r
9061   static QuestionParams *qp;\r
9062   char reply[MSG_SIZ];\r
9063   int len, err;\r
9064 \r
9065   switch (message) {\r
9066   case WM_INITDIALOG:\r
9067     qp = (QuestionParams *) lParam;\r
9068     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9069     SetWindowText(hDlg, qp->title);\r
9070     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9071     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9072     return FALSE;\r
9073 \r
9074   case WM_COMMAND:\r
9075     switch (LOWORD(wParam)) {\r
9076     case IDOK:\r
9077       strcpy(reply, qp->replyPrefix);\r
9078       if (*reply) strcat(reply, " ");\r
9079       len = strlen(reply);\r
9080       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9081       strcat(reply, "\n");\r
9082       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9083       EndDialog(hDlg, TRUE);\r
9084       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9085       return TRUE;\r
9086     case IDCANCEL:\r
9087       EndDialog(hDlg, FALSE);\r
9088       return TRUE;\r
9089     default:\r
9090       break;\r
9091     }\r
9092     break;\r
9093   }\r
9094   return FALSE;\r
9095 }\r
9096 \r
9097 VOID\r
9098 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9099 {\r
9100     QuestionParams qp;\r
9101     FARPROC lpProc;\r
9102     \r
9103     qp.title = title;\r
9104     qp.question = question;\r
9105     qp.replyPrefix = replyPrefix;\r
9106     qp.pr = pr;\r
9107     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9108     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9109       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9110     FreeProcInstance(lpProc);\r
9111 }\r
9112 \r
9113 /* [AS] Pick FRC position */\r
9114 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9115 {\r
9116     static int * lpIndexFRC;\r
9117     BOOL index_is_ok;\r
9118     char buf[16];\r
9119 \r
9120     switch( message )\r
9121     {\r
9122     case WM_INITDIALOG:\r
9123         lpIndexFRC = (int *) lParam;\r
9124 \r
9125         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9126 \r
9127         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9128         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9129         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9130         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9131 \r
9132         break;\r
9133 \r
9134     case WM_COMMAND:\r
9135         switch( LOWORD(wParam) ) {\r
9136         case IDOK:\r
9137             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9138             EndDialog( hDlg, 0 );\r
9139             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9140             return TRUE;\r
9141         case IDCANCEL:\r
9142             EndDialog( hDlg, 1 );   \r
9143             return TRUE;\r
9144         case IDC_NFG_Edit:\r
9145             if( HIWORD(wParam) == EN_CHANGE ) {\r
9146                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9147 \r
9148                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9149             }\r
9150             return TRUE;\r
9151         case IDC_NFG_Random:\r
9152             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9153             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9154             return TRUE;\r
9155         }\r
9156 \r
9157         break;\r
9158     }\r
9159 \r
9160     return FALSE;\r
9161 }\r
9162 \r
9163 int NewGameFRC()\r
9164 {\r
9165     int result;\r
9166     int index = appData.defaultFrcPosition;\r
9167     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9168 \r
9169     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9170 \r
9171     if( result == 0 ) {\r
9172         appData.defaultFrcPosition = index;\r
9173     }\r
9174 \r
9175     return result;\r
9176 }\r
9177 \r
9178 /* [AS] Game list options */\r
9179 typedef struct {\r
9180     char id;\r
9181     char * name;\r
9182 } GLT_Item;\r
9183 \r
9184 static GLT_Item GLT_ItemInfo[] = {\r
9185     { GLT_EVENT,      "Event" },\r
9186     { GLT_SITE,       "Site" },\r
9187     { GLT_DATE,       "Date" },\r
9188     { GLT_ROUND,      "Round" },\r
9189     { GLT_PLAYERS,    "Players" },\r
9190     { GLT_RESULT,     "Result" },\r
9191     { GLT_WHITE_ELO,  "White Rating" },\r
9192     { GLT_BLACK_ELO,  "Black Rating" },\r
9193     { GLT_TIME_CONTROL,"Time Control" },\r
9194     { GLT_VARIANT,    "Variant" },\r
9195     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9196     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9197     { 0, 0 }\r
9198 };\r
9199 \r
9200 const char * GLT_FindItem( char id )\r
9201 {\r
9202     const char * result = 0;\r
9203 \r
9204     GLT_Item * list = GLT_ItemInfo;\r
9205 \r
9206     while( list->id != 0 ) {\r
9207         if( list->id == id ) {\r
9208             result = list->name;\r
9209             break;\r
9210         }\r
9211 \r
9212         list++;\r
9213     }\r
9214 \r
9215     return result;\r
9216 }\r
9217 \r
9218 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9219 {\r
9220     const char * name = GLT_FindItem( id );\r
9221 \r
9222     if( name != 0 ) {\r
9223         if( index >= 0 ) {\r
9224             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9225         }\r
9226         else {\r
9227             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9228         }\r
9229     }\r
9230 }\r
9231 \r
9232 void GLT_TagsToList( HWND hDlg, char * tags )\r
9233 {\r
9234     char * pc = tags;\r
9235 \r
9236     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9237 \r
9238     while( *pc ) {\r
9239         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9240         pc++;\r
9241     }\r
9242 \r
9243     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9244 \r
9245     pc = GLT_ALL_TAGS;\r
9246 \r
9247     while( *pc ) {\r
9248         if( strchr( tags, *pc ) == 0 ) {\r
9249             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9250         }\r
9251         pc++;\r
9252     }\r
9253 \r
9254     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9255 }\r
9256 \r
9257 char GLT_ListItemToTag( HWND hDlg, int index )\r
9258 {\r
9259     char result = '\0';\r
9260     char name[128];\r
9261 \r
9262     GLT_Item * list = GLT_ItemInfo;\r
9263 \r
9264     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9265         while( list->id != 0 ) {\r
9266             if( strcmp( list->name, name ) == 0 ) {\r
9267                 result = list->id;\r
9268                 break;\r
9269             }\r
9270 \r
9271             list++;\r
9272         }\r
9273     }\r
9274 \r
9275     return result;\r
9276 }\r
9277 \r
9278 void GLT_MoveSelection( HWND hDlg, int delta )\r
9279 {\r
9280     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9281     int idx2 = idx1 + delta;\r
9282     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9283 \r
9284     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9285         char buf[128];\r
9286 \r
9287         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9288         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9289         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9290         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9291     }\r
9292 }\r
9293 \r
9294 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9295 {\r
9296     static char glt[64];\r
9297     static char * lpUserGLT;\r
9298 \r
9299     switch( message )\r
9300     {\r
9301     case WM_INITDIALOG:\r
9302         lpUserGLT = (char *) lParam;\r
9303         \r
9304         strcpy( glt, lpUserGLT );\r
9305 \r
9306         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9307 \r
9308         /* Initialize list */\r
9309         GLT_TagsToList( hDlg, glt );\r
9310 \r
9311         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9312 \r
9313         break;\r
9314 \r
9315     case WM_COMMAND:\r
9316         switch( LOWORD(wParam) ) {\r
9317         case IDOK:\r
9318             {\r
9319                 char * pc = lpUserGLT;\r
9320                 int idx = 0;\r
9321 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9322                 char id;\r
9323 \r
9324                 do {\r
9325                     id = GLT_ListItemToTag( hDlg, idx );\r
9326 \r
9327                     *pc++ = id;\r
9328                     idx++;\r
9329                 } while( id != '\0' );\r
9330             }\r
9331             EndDialog( hDlg, 0 );\r
9332             return TRUE;\r
9333         case IDCANCEL:\r
9334             EndDialog( hDlg, 1 );\r
9335             return TRUE;\r
9336 \r
9337         case IDC_GLT_Default:\r
9338             strcpy( glt, GLT_DEFAULT_TAGS );\r
9339             GLT_TagsToList( hDlg, glt );\r
9340             return TRUE;\r
9341 \r
9342         case IDC_GLT_Restore:\r
9343             strcpy( glt, lpUserGLT );\r
9344             GLT_TagsToList( hDlg, glt );\r
9345             return TRUE;\r
9346 \r
9347         case IDC_GLT_Up:\r
9348             GLT_MoveSelection( hDlg, -1 );\r
9349             return TRUE;\r
9350 \r
9351         case IDC_GLT_Down:\r
9352             GLT_MoveSelection( hDlg, +1 );\r
9353             return TRUE;\r
9354         }\r
9355 \r
9356         break;\r
9357     }\r
9358 \r
9359     return FALSE;\r
9360 }\r
9361 \r
9362 int GameListOptions()\r
9363 {\r
9364     char glt[64];\r
9365     int result;\r
9366     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9367 \r
9368     strcpy( glt, appData.gameListTags );\r
9369 \r
9370     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9371 \r
9372     if( result == 0 ) {\r
9373         /* [AS] Memory leak here! */\r
9374         appData.gameListTags = strdup( glt ); \r
9375     }\r
9376 \r
9377     return result;\r
9378 }\r
9379 \r
9380 \r
9381 VOID\r
9382 DisplayIcsInteractionTitle(char *str)\r
9383 {\r
9384   char consoleTitle[MSG_SIZ];\r
9385 \r
9386   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9387   SetWindowText(hwndConsole, consoleTitle);\r
9388 }\r
9389 \r
9390 void\r
9391 DrawPosition(int fullRedraw, Board board)\r
9392 {\r
9393   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9394 }\r
9395 \r
9396 void NotifyFrontendLogin()\r
9397 {\r
9398         if (hwndConsole)\r
9399                 UpdateICSWidth(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
9400 }\r
9401 \r
9402 VOID\r
9403 ResetFrontEnd()\r
9404 {\r
9405   fromX = fromY = -1;\r
9406   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9407     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9408     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9409     dragInfo.lastpos = dragInfo.pos;\r
9410     dragInfo.start.x = dragInfo.start.y = -1;\r
9411     dragInfo.from = dragInfo.start;\r
9412     ReleaseCapture();\r
9413     DrawPosition(TRUE, NULL);\r
9414   }\r
9415 }\r
9416 \r
9417 \r
9418 VOID\r
9419 CommentPopUp(char *title, char *str)\r
9420 {\r
9421   HWND hwnd = GetActiveWindow();\r
9422   EitherCommentPopUp(0, title, str, FALSE);\r
9423   SAY(str);\r
9424   SetActiveWindow(hwnd);\r
9425 }\r
9426 \r
9427 VOID\r
9428 CommentPopDown(void)\r
9429 {\r
9430   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9431   if (commentDialog) {\r
9432     ShowWindow(commentDialog, SW_HIDE);\r
9433   }\r
9434   commentDialogUp = FALSE;\r
9435 }\r
9436 \r
9437 VOID\r
9438 EditCommentPopUp(int index, char *title, char *str)\r
9439 {\r
9440   EitherCommentPopUp(index, title, str, TRUE);\r
9441 }\r
9442 \r
9443 \r
9444 VOID\r
9445 RingBell()\r
9446 {\r
9447   MyPlaySound(&sounds[(int)SoundMove]);\r
9448 }\r
9449 \r
9450 VOID PlayIcsWinSound()\r
9451 {\r
9452   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9453 }\r
9454 \r
9455 VOID PlayIcsLossSound()\r
9456 {\r
9457   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9458 }\r
9459 \r
9460 VOID PlayIcsDrawSound()\r
9461 {\r
9462   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9463 }\r
9464 \r
9465 VOID PlayIcsUnfinishedSound()\r
9466 {\r
9467   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9468 }\r
9469 \r
9470 VOID\r
9471 PlayAlarmSound()\r
9472 {\r
9473   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9474 }\r
9475 \r
9476 \r
9477 VOID\r
9478 EchoOn()\r
9479 {\r
9480   HWND hInput;\r
9481   consoleEcho = TRUE;\r
9482   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9483   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9484   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9485 }\r
9486 \r
9487 \r
9488 VOID\r
9489 EchoOff()\r
9490 {\r
9491   CHARFORMAT cf;\r
9492   HWND hInput;\r
9493   consoleEcho = FALSE;\r
9494   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9495   /* This works OK: set text and background both to the same color */\r
9496   cf = consoleCF;\r
9497   cf.crTextColor = COLOR_ECHOOFF;\r
9498   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9499   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9500 }\r
9501 \r
9502 /* No Raw()...? */\r
9503 \r
9504 void Colorize(ColorClass cc, int continuation)\r
9505 {\r
9506   currentColorClass = cc;\r
9507   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9508   consoleCF.crTextColor = textAttribs[cc].color;\r
9509   consoleCF.dwEffects = textAttribs[cc].effects;\r
9510   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9511 }\r
9512 \r
9513 char *\r
9514 UserName()\r
9515 {\r
9516   static char buf[MSG_SIZ];\r
9517   DWORD bufsiz = MSG_SIZ;\r
9518 \r
9519   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9520         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9521   }\r
9522   if (!GetUserName(buf, &bufsiz)) {\r
9523     /*DisplayError("Error getting user name", GetLastError());*/\r
9524     strcpy(buf, "User");\r
9525   }\r
9526   return buf;\r
9527 }\r
9528 \r
9529 char *\r
9530 HostName()\r
9531 {\r
9532   static char buf[MSG_SIZ];\r
9533   DWORD bufsiz = MSG_SIZ;\r
9534 \r
9535   if (!GetComputerName(buf, &bufsiz)) {\r
9536     /*DisplayError("Error getting host name", GetLastError());*/\r
9537     strcpy(buf, "Unknown");\r
9538   }\r
9539   return buf;\r
9540 }\r
9541 \r
9542 \r
9543 int\r
9544 ClockTimerRunning()\r
9545 {\r
9546   return clockTimerEvent != 0;\r
9547 }\r
9548 \r
9549 int\r
9550 StopClockTimer()\r
9551 {\r
9552   if (clockTimerEvent == 0) return FALSE;\r
9553   KillTimer(hwndMain, clockTimerEvent);\r
9554   clockTimerEvent = 0;\r
9555   return TRUE;\r
9556 }\r
9557 \r
9558 void\r
9559 StartClockTimer(long millisec)\r
9560 {\r
9561   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9562                              (UINT) millisec, NULL);\r
9563 }\r
9564 \r
9565 void\r
9566 DisplayWhiteClock(long timeRemaining, int highlight)\r
9567 {\r
9568   HDC hdc;\r
9569   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9570 \r
9571   if(appData.noGUI) return;\r
9572   hdc = GetDC(hwndMain);\r
9573   if (!IsIconic(hwndMain)) {\r
9574     DisplayAClock(hdc, timeRemaining, highlight, \r
9575                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9576   }\r
9577   if (highlight && iconCurrent == iconBlack) {\r
9578     iconCurrent = iconWhite;\r
9579     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9580     if (IsIconic(hwndMain)) {\r
9581       DrawIcon(hdc, 2, 2, iconCurrent);\r
9582     }\r
9583   }\r
9584   (void) ReleaseDC(hwndMain, hdc);\r
9585   if (hwndConsole)\r
9586     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9587 }\r
9588 \r
9589 void\r
9590 DisplayBlackClock(long timeRemaining, int highlight)\r
9591 {\r
9592   HDC hdc;\r
9593   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9594 \r
9595   if(appData.noGUI) return;\r
9596   hdc = GetDC(hwndMain);\r
9597   if (!IsIconic(hwndMain)) {\r
9598     DisplayAClock(hdc, timeRemaining, highlight, \r
9599                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9600   }\r
9601   if (highlight && iconCurrent == iconWhite) {\r
9602     iconCurrent = iconBlack;\r
9603     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9604     if (IsIconic(hwndMain)) {\r
9605       DrawIcon(hdc, 2, 2, iconCurrent);\r
9606     }\r
9607   }\r
9608   (void) ReleaseDC(hwndMain, hdc);\r
9609   if (hwndConsole)\r
9610     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9611 }\r
9612 \r
9613 \r
9614 int\r
9615 LoadGameTimerRunning()\r
9616 {\r
9617   return loadGameTimerEvent != 0;\r
9618 }\r
9619 \r
9620 int\r
9621 StopLoadGameTimer()\r
9622 {\r
9623   if (loadGameTimerEvent == 0) return FALSE;\r
9624   KillTimer(hwndMain, loadGameTimerEvent);\r
9625   loadGameTimerEvent = 0;\r
9626   return TRUE;\r
9627 }\r
9628 \r
9629 void\r
9630 StartLoadGameTimer(long millisec)\r
9631 {\r
9632   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9633                                 (UINT) millisec, NULL);\r
9634 }\r
9635 \r
9636 void\r
9637 AutoSaveGame()\r
9638 {\r
9639   char *defName;\r
9640   FILE *f;\r
9641   char fileTitle[MSG_SIZ];\r
9642 \r
9643   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9644   f = OpenFileDialog(hwndMain, "a", defName,\r
9645                      appData.oldSaveStyle ? "gam" : "pgn",\r
9646                      GAME_FILT, \r
9647                      "Save Game to File", NULL, fileTitle, NULL);\r
9648   if (f != NULL) {\r
9649     SaveGame(f, 0, "");\r
9650     fclose(f);\r
9651   }\r
9652 }\r
9653 \r
9654 \r
9655 void\r
9656 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9657 {\r
9658   if (delayedTimerEvent != 0) {\r
9659     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9660       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9661     }\r
9662     KillTimer(hwndMain, delayedTimerEvent);\r
9663     delayedTimerEvent = 0;\r
9664     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9665     delayedTimerCallback();\r
9666   }\r
9667   delayedTimerCallback = cb;\r
9668   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9669                                 (UINT) millisec, NULL);\r
9670 }\r
9671 \r
9672 DelayedEventCallback\r
9673 GetDelayedEvent()\r
9674 {\r
9675   if (delayedTimerEvent) {\r
9676     return delayedTimerCallback;\r
9677   } else {\r
9678     return NULL;\r
9679   }\r
9680 }\r
9681 \r
9682 void\r
9683 CancelDelayedEvent()\r
9684 {\r
9685   if (delayedTimerEvent) {\r
9686     KillTimer(hwndMain, delayedTimerEvent);\r
9687     delayedTimerEvent = 0;\r
9688   }\r
9689 }\r
9690 \r
9691 DWORD GetWin32Priority(int nice)\r
9692 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9693 /*\r
9694 REALTIME_PRIORITY_CLASS     0x00000100\r
9695 HIGH_PRIORITY_CLASS         0x00000080\r
9696 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9697 NORMAL_PRIORITY_CLASS       0x00000020\r
9698 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9699 IDLE_PRIORITY_CLASS         0x00000040\r
9700 */\r
9701         if (nice < -15) return 0x00000080;\r
9702         if (nice < 0)   return 0x00008000;\r
9703         if (nice == 0)  return 0x00000020;\r
9704         if (nice < 15)  return 0x00004000;\r
9705         return 0x00000040;\r
9706 }\r
9707 \r
9708 /* Start a child process running the given program.\r
9709    The process's standard output can be read from "from", and its\r
9710    standard input can be written to "to".\r
9711    Exit with fatal error if anything goes wrong.\r
9712    Returns an opaque pointer that can be used to destroy the process\r
9713    later.\r
9714 */\r
9715 int\r
9716 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9717 {\r
9718 #define BUFSIZE 4096\r
9719 \r
9720   HANDLE hChildStdinRd, hChildStdinWr,\r
9721     hChildStdoutRd, hChildStdoutWr;\r
9722   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9723   SECURITY_ATTRIBUTES saAttr;\r
9724   BOOL fSuccess;\r
9725   PROCESS_INFORMATION piProcInfo;\r
9726   STARTUPINFO siStartInfo;\r
9727   ChildProc *cp;\r
9728   char buf[MSG_SIZ];\r
9729   DWORD err;\r
9730 \r
9731   if (appData.debugMode) {\r
9732     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9733   }\r
9734 \r
9735   *pr = NoProc;\r
9736 \r
9737   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9738   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9739   saAttr.bInheritHandle = TRUE;\r
9740   saAttr.lpSecurityDescriptor = NULL;\r
9741 \r
9742   /*\r
9743    * The steps for redirecting child's STDOUT:\r
9744    *     1. Create anonymous pipe to be STDOUT for child.\r
9745    *     2. Create a noninheritable duplicate of read handle,\r
9746    *         and close the inheritable read handle.\r
9747    */\r
9748 \r
9749   /* Create a pipe for the child's STDOUT. */\r
9750   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9751     return GetLastError();\r
9752   }\r
9753 \r
9754   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9755   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9756                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9757                              FALSE,     /* not inherited */\r
9758                              DUPLICATE_SAME_ACCESS);\r
9759   if (! fSuccess) {\r
9760     return GetLastError();\r
9761   }\r
9762   CloseHandle(hChildStdoutRd);\r
9763 \r
9764   /*\r
9765    * The steps for redirecting child's STDIN:\r
9766    *     1. Create anonymous pipe to be STDIN for child.\r
9767    *     2. Create a noninheritable duplicate of write handle,\r
9768    *         and close the inheritable write handle.\r
9769    */\r
9770 \r
9771   /* Create a pipe for the child's STDIN. */\r
9772   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9773     return GetLastError();\r
9774   }\r
9775 \r
9776   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9777   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9778                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9779                              FALSE,     /* not inherited */\r
9780                              DUPLICATE_SAME_ACCESS);\r
9781   if (! fSuccess) {\r
9782     return GetLastError();\r
9783   }\r
9784   CloseHandle(hChildStdinWr);\r
9785 \r
9786   /* Arrange to (1) look in dir for the child .exe file, and\r
9787    * (2) have dir be the child's working directory.  Interpret\r
9788    * dir relative to the directory WinBoard loaded from. */\r
9789   GetCurrentDirectory(MSG_SIZ, buf);\r
9790   SetCurrentDirectory(installDir);\r
9791   SetCurrentDirectory(dir);\r
9792 \r
9793   /* Now create the child process. */\r
9794 \r
9795   siStartInfo.cb = sizeof(STARTUPINFO);\r
9796   siStartInfo.lpReserved = NULL;\r
9797   siStartInfo.lpDesktop = NULL;\r
9798   siStartInfo.lpTitle = NULL;\r
9799   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9800   siStartInfo.cbReserved2 = 0;\r
9801   siStartInfo.lpReserved2 = NULL;\r
9802   siStartInfo.hStdInput = hChildStdinRd;\r
9803   siStartInfo.hStdOutput = hChildStdoutWr;\r
9804   siStartInfo.hStdError = hChildStdoutWr;\r
9805 \r
9806   fSuccess = CreateProcess(NULL,\r
9807                            cmdLine,        /* command line */\r
9808                            NULL,           /* process security attributes */\r
9809                            NULL,           /* primary thread security attrs */\r
9810                            TRUE,           /* handles are inherited */\r
9811                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
9812                            NULL,           /* use parent's environment */\r
9813                            NULL,\r
9814                            &siStartInfo, /* STARTUPINFO pointer */\r
9815                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
9816 \r
9817   err = GetLastError();\r
9818   SetCurrentDirectory(buf); /* return to prev directory */\r
9819   if (! fSuccess) {\r
9820     return err;\r
9821   }\r
9822 \r
9823   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
9824     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
9825     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
9826   }\r
9827 \r
9828   /* Close the handles we don't need in the parent */\r
9829   CloseHandle(piProcInfo.hThread);\r
9830   CloseHandle(hChildStdinRd);\r
9831   CloseHandle(hChildStdoutWr);\r
9832 \r
9833   /* Prepare return value */\r
9834   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
9835   cp->kind = CPReal;\r
9836   cp->hProcess = piProcInfo.hProcess;\r
9837   cp->pid = piProcInfo.dwProcessId;\r
9838   cp->hFrom = hChildStdoutRdDup;\r
9839   cp->hTo = hChildStdinWrDup;\r
9840 \r
9841   *pr = (void *) cp;\r
9842 \r
9843   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
9844      2000 where engines sometimes don't see the initial command(s)\r
9845      from WinBoard and hang.  I don't understand how that can happen,\r
9846      but the Sleep is harmless, so I've put it in.  Others have also\r
9847      reported what may be the same problem, so hopefully this will fix\r
9848      it for them too.  */\r
9849   Sleep(500);\r
9850 \r
9851   return NO_ERROR;\r
9852 }\r
9853 \r
9854 \r
9855 void\r
9856 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
9857 {\r
9858   ChildProc *cp; int result;\r
9859 \r
9860   cp = (ChildProc *) pr;\r
9861   if (cp == NULL) return;\r
9862 \r
9863   switch (cp->kind) {\r
9864   case CPReal:\r
9865     /* TerminateProcess is considered harmful, so... */\r
9866     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
9867     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
9868     /* The following doesn't work because the chess program\r
9869        doesn't "have the same console" as WinBoard.  Maybe\r
9870        we could arrange for this even though neither WinBoard\r
9871        nor the chess program uses a console for stdio? */\r
9872     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
9873 \r
9874     /* [AS] Special termination modes for misbehaving programs... */\r
9875     if( signal == 9 ) { \r
9876         result = TerminateProcess( cp->hProcess, 0 );\r
9877 \r
9878         if ( appData.debugMode) {\r
9879             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
9880         }\r
9881     }\r
9882     else if( signal == 10 ) {\r
9883         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
9884 \r
9885         if( dw != WAIT_OBJECT_0 ) {\r
9886             result = TerminateProcess( cp->hProcess, 0 );\r
9887 \r
9888             if ( appData.debugMode) {\r
9889                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
9890             }\r
9891 \r
9892         }\r
9893     }\r
9894 \r
9895     CloseHandle(cp->hProcess);\r
9896     break;\r
9897 \r
9898   case CPComm:\r
9899     if (cp->hFrom) CloseHandle(cp->hFrom);\r
9900     break;\r
9901 \r
9902   case CPSock:\r
9903     closesocket(cp->sock);\r
9904     WSACleanup();\r
9905     break;\r
9906 \r
9907   case CPRcmd:\r
9908     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
9909     closesocket(cp->sock);\r
9910     closesocket(cp->sock2);\r
9911     WSACleanup();\r
9912     break;\r
9913   }\r
9914   free(cp);\r
9915 }\r
9916 \r
9917 void\r
9918 InterruptChildProcess(ProcRef pr)\r
9919 {\r
9920   ChildProc *cp;\r
9921 \r
9922   cp = (ChildProc *) pr;\r
9923   if (cp == NULL) return;\r
9924   switch (cp->kind) {\r
9925   case CPReal:\r
9926     /* The following doesn't work because the chess program\r
9927        doesn't "have the same console" as WinBoard.  Maybe\r
9928        we could arrange for this even though neither WinBoard\r
9929        nor the chess program uses a console for stdio */\r
9930     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
9931     break;\r
9932 \r
9933   case CPComm:\r
9934   case CPSock:\r
9935     /* Can't interrupt */\r
9936     break;\r
9937 \r
9938   case CPRcmd:\r
9939     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
9940     break;\r
9941   }\r
9942 }\r
9943 \r
9944 \r
9945 int\r
9946 OpenTelnet(char *host, char *port, ProcRef *pr)\r
9947 {\r
9948   char cmdLine[MSG_SIZ];\r
9949 \r
9950   if (port[0] == NULLCHAR) {\r
9951     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
9952   } else {\r
9953     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
9954   }\r
9955   return StartChildProcess(cmdLine, "", pr);\r
9956 }\r
9957 \r
9958 \r
9959 /* Code to open TCP sockets */\r
9960 \r
9961 int\r
9962 OpenTCP(char *host, char *port, ProcRef *pr)\r
9963 {\r
9964   ChildProc *cp;\r
9965   int err;\r
9966   SOCKET s;\r
9967   struct sockaddr_in sa, mysa;\r
9968   struct hostent FAR *hp;\r
9969   unsigned short uport;\r
9970   WORD wVersionRequested;\r
9971   WSADATA wsaData;\r
9972 \r
9973   /* Initialize socket DLL */\r
9974   wVersionRequested = MAKEWORD(1, 1);\r
9975   err = WSAStartup(wVersionRequested, &wsaData);\r
9976   if (err != 0) return err;\r
9977 \r
9978   /* Make socket */\r
9979   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
9980     err = WSAGetLastError();\r
9981     WSACleanup();\r
9982     return err;\r
9983   }\r
9984 \r
9985   /* Bind local address using (mostly) don't-care values.\r
9986    */\r
9987   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
9988   mysa.sin_family = AF_INET;\r
9989   mysa.sin_addr.s_addr = INADDR_ANY;\r
9990   uport = (unsigned short) 0;\r
9991   mysa.sin_port = htons(uport);\r
9992   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
9993       == SOCKET_ERROR) {\r
9994     err = WSAGetLastError();\r
9995     WSACleanup();\r
9996     return err;\r
9997   }\r
9998 \r
9999   /* Resolve remote host name */\r
10000   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10001   if (!(hp = gethostbyname(host))) {\r
10002     unsigned int b0, b1, b2, b3;\r
10003 \r
10004     err = WSAGetLastError();\r
10005 \r
10006     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10007       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10008       hp->h_addrtype = AF_INET;\r
10009       hp->h_length = 4;\r
10010       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10011       hp->h_addr_list[0] = (char *) malloc(4);\r
10012       hp->h_addr_list[0][0] = (char) b0;\r
10013       hp->h_addr_list[0][1] = (char) b1;\r
10014       hp->h_addr_list[0][2] = (char) b2;\r
10015       hp->h_addr_list[0][3] = (char) b3;\r
10016     } else {\r
10017       WSACleanup();\r
10018       return err;\r
10019     }\r
10020   }\r
10021   sa.sin_family = hp->h_addrtype;\r
10022   uport = (unsigned short) atoi(port);\r
10023   sa.sin_port = htons(uport);\r
10024   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10025 \r
10026   /* Make connection */\r
10027   if (connect(s, (struct sockaddr *) &sa,\r
10028               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10029     err = WSAGetLastError();\r
10030     WSACleanup();\r
10031     return err;\r
10032   }\r
10033 \r
10034   /* Prepare return value */\r
10035   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10036   cp->kind = CPSock;\r
10037   cp->sock = s;\r
10038   *pr = (ProcRef *) cp;\r
10039 \r
10040   return NO_ERROR;\r
10041 }\r
10042 \r
10043 int\r
10044 OpenCommPort(char *name, ProcRef *pr)\r
10045 {\r
10046   HANDLE h;\r
10047   COMMTIMEOUTS ct;\r
10048   ChildProc *cp;\r
10049   char fullname[MSG_SIZ];\r
10050 \r
10051   if (*name != '\\')\r
10052     sprintf(fullname, "\\\\.\\%s", name);\r
10053   else\r
10054     strcpy(fullname, name);\r
10055 \r
10056   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10057                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10058   if (h == (HANDLE) -1) {\r
10059     return GetLastError();\r
10060   }\r
10061   hCommPort = h;\r
10062 \r
10063   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10064 \r
10065   /* Accumulate characters until a 100ms pause, then parse */\r
10066   ct.ReadIntervalTimeout = 100;\r
10067   ct.ReadTotalTimeoutMultiplier = 0;\r
10068   ct.ReadTotalTimeoutConstant = 0;\r
10069   ct.WriteTotalTimeoutMultiplier = 0;\r
10070   ct.WriteTotalTimeoutConstant = 0;\r
10071   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10072 \r
10073   /* Prepare return value */\r
10074   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10075   cp->kind = CPComm;\r
10076   cp->hFrom = h;\r
10077   cp->hTo = h;\r
10078   *pr = (ProcRef *) cp;\r
10079 \r
10080   return NO_ERROR;\r
10081 }\r
10082 \r
10083 int\r
10084 OpenLoopback(ProcRef *pr)\r
10085 {\r
10086   DisplayFatalError("Not implemented", 0, 1);\r
10087   return NO_ERROR;\r
10088 }\r
10089 \r
10090 \r
10091 int\r
10092 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10093 {\r
10094   ChildProc *cp;\r
10095   int err;\r
10096   SOCKET s, s2, s3;\r
10097   struct sockaddr_in sa, mysa;\r
10098   struct hostent FAR *hp;\r
10099   unsigned short uport;\r
10100   WORD wVersionRequested;\r
10101   WSADATA wsaData;\r
10102   int fromPort;\r
10103   char stderrPortStr[MSG_SIZ];\r
10104 \r
10105   /* Initialize socket DLL */\r
10106   wVersionRequested = MAKEWORD(1, 1);\r
10107   err = WSAStartup(wVersionRequested, &wsaData);\r
10108   if (err != 0) return err;\r
10109 \r
10110   /* Resolve remote host name */\r
10111   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10112   if (!(hp = gethostbyname(host))) {\r
10113     unsigned int b0, b1, b2, b3;\r
10114 \r
10115     err = WSAGetLastError();\r
10116 \r
10117     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10118       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10119       hp->h_addrtype = AF_INET;\r
10120       hp->h_length = 4;\r
10121       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10122       hp->h_addr_list[0] = (char *) malloc(4);\r
10123       hp->h_addr_list[0][0] = (char) b0;\r
10124       hp->h_addr_list[0][1] = (char) b1;\r
10125       hp->h_addr_list[0][2] = (char) b2;\r
10126       hp->h_addr_list[0][3] = (char) b3;\r
10127     } else {\r
10128       WSACleanup();\r
10129       return err;\r
10130     }\r
10131   }\r
10132   sa.sin_family = hp->h_addrtype;\r
10133   uport = (unsigned short) 514;\r
10134   sa.sin_port = htons(uport);\r
10135   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10136 \r
10137   /* Bind local socket to unused "privileged" port address\r
10138    */\r
10139   s = INVALID_SOCKET;\r
10140   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10141   mysa.sin_family = AF_INET;\r
10142   mysa.sin_addr.s_addr = INADDR_ANY;\r
10143   for (fromPort = 1023;; fromPort--) {\r
10144     if (fromPort < 0) {\r
10145       WSACleanup();\r
10146       return WSAEADDRINUSE;\r
10147     }\r
10148     if (s == INVALID_SOCKET) {\r
10149       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10150         err = WSAGetLastError();\r
10151         WSACleanup();\r
10152         return err;\r
10153       }\r
10154     }\r
10155     uport = (unsigned short) fromPort;\r
10156     mysa.sin_port = htons(uport);\r
10157     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10158         == SOCKET_ERROR) {\r
10159       err = WSAGetLastError();\r
10160       if (err == WSAEADDRINUSE) continue;\r
10161       WSACleanup();\r
10162       return err;\r
10163     }\r
10164     if (connect(s, (struct sockaddr *) &sa,\r
10165       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10166       err = WSAGetLastError();\r
10167       if (err == WSAEADDRINUSE) {\r
10168         closesocket(s);\r
10169         s = -1;\r
10170         continue;\r
10171       }\r
10172       WSACleanup();\r
10173       return err;\r
10174     }\r
10175     break;\r
10176   }\r
10177 \r
10178   /* Bind stderr local socket to unused "privileged" port address\r
10179    */\r
10180   s2 = INVALID_SOCKET;\r
10181   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10182   mysa.sin_family = AF_INET;\r
10183   mysa.sin_addr.s_addr = INADDR_ANY;\r
10184   for (fromPort = 1023;; fromPort--) {\r
10185     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10186     if (fromPort < 0) {\r
10187       (void) closesocket(s);\r
10188       WSACleanup();\r
10189       return WSAEADDRINUSE;\r
10190     }\r
10191     if (s2 == INVALID_SOCKET) {\r
10192       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10193         err = WSAGetLastError();\r
10194         closesocket(s);\r
10195         WSACleanup();\r
10196         return err;\r
10197       }\r
10198     }\r
10199     uport = (unsigned short) fromPort;\r
10200     mysa.sin_port = htons(uport);\r
10201     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10202         == SOCKET_ERROR) {\r
10203       err = WSAGetLastError();\r
10204       if (err == WSAEADDRINUSE) continue;\r
10205       (void) closesocket(s);\r
10206       WSACleanup();\r
10207       return err;\r
10208     }\r
10209     if (listen(s2, 1) == SOCKET_ERROR) {\r
10210       err = WSAGetLastError();\r
10211       if (err == WSAEADDRINUSE) {\r
10212         closesocket(s2);\r
10213         s2 = INVALID_SOCKET;\r
10214         continue;\r
10215       }\r
10216       (void) closesocket(s);\r
10217       (void) closesocket(s2);\r
10218       WSACleanup();\r
10219       return err;\r
10220     }\r
10221     break;\r
10222   }\r
10223   prevStderrPort = fromPort; // remember port used\r
10224   sprintf(stderrPortStr, "%d", fromPort);\r
10225 \r
10226   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10227     err = WSAGetLastError();\r
10228     (void) closesocket(s);\r
10229     (void) closesocket(s2);\r
10230     WSACleanup();\r
10231     return err;\r
10232   }\r
10233 \r
10234   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10235     err = WSAGetLastError();\r
10236     (void) closesocket(s);\r
10237     (void) closesocket(s2);\r
10238     WSACleanup();\r
10239     return err;\r
10240   }\r
10241   if (*user == NULLCHAR) user = UserName();\r
10242   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10243     err = WSAGetLastError();\r
10244     (void) closesocket(s);\r
10245     (void) closesocket(s2);\r
10246     WSACleanup();\r
10247     return err;\r
10248   }\r
10249   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10250     err = WSAGetLastError();\r
10251     (void) closesocket(s);\r
10252     (void) closesocket(s2);\r
10253     WSACleanup();\r
10254     return err;\r
10255   }\r
10256 \r
10257   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10258     err = WSAGetLastError();\r
10259     (void) closesocket(s);\r
10260     (void) closesocket(s2);\r
10261     WSACleanup();\r
10262     return err;\r
10263   }\r
10264   (void) closesocket(s2);  /* Stop listening */\r
10265 \r
10266   /* Prepare return value */\r
10267   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10268   cp->kind = CPRcmd;\r
10269   cp->sock = s;\r
10270   cp->sock2 = s3;\r
10271   *pr = (ProcRef *) cp;\r
10272 \r
10273   return NO_ERROR;\r
10274 }\r
10275 \r
10276 \r
10277 InputSourceRef\r
10278 AddInputSource(ProcRef pr, int lineByLine,\r
10279                InputCallback func, VOIDSTAR closure)\r
10280 {\r
10281   InputSource *is, *is2 = NULL;\r
10282   ChildProc *cp = (ChildProc *) pr;\r
10283 \r
10284   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10285   is->lineByLine = lineByLine;\r
10286   is->func = func;\r
10287   is->closure = closure;\r
10288   is->second = NULL;\r
10289   is->next = is->buf;\r
10290   if (pr == NoProc) {\r
10291     is->kind = CPReal;\r
10292     consoleInputSource = is;\r
10293   } else {\r
10294     is->kind = cp->kind;\r
10295     /* \r
10296         [AS] Try to avoid a race condition if the thread is given control too early:\r
10297         we create all threads suspended so that the is->hThread variable can be\r
10298         safely assigned, then let the threads start with ResumeThread.\r
10299     */\r
10300     switch (cp->kind) {\r
10301     case CPReal:\r
10302       is->hFile = cp->hFrom;\r
10303       cp->hFrom = NULL; /* now owned by InputThread */\r
10304       is->hThread =\r
10305         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10306                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10307       break;\r
10308 \r
10309     case CPComm:\r
10310       is->hFile = cp->hFrom;\r
10311       cp->hFrom = NULL; /* now owned by InputThread */\r
10312       is->hThread =\r
10313         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10314                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10315       break;\r
10316 \r
10317     case CPSock:\r
10318       is->sock = cp->sock;\r
10319       is->hThread =\r
10320         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10321                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10322       break;\r
10323 \r
10324     case CPRcmd:\r
10325       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10326       *is2 = *is;\r
10327       is->sock = cp->sock;\r
10328       is->second = is2;\r
10329       is2->sock = cp->sock2;\r
10330       is2->second = is2;\r
10331       is->hThread =\r
10332         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10333                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10334       is2->hThread =\r
10335         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10336                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10337       break;\r
10338     }\r
10339 \r
10340     if( is->hThread != NULL ) {\r
10341         ResumeThread( is->hThread );\r
10342     }\r
10343 \r
10344     if( is2 != NULL && is2->hThread != NULL ) {\r
10345         ResumeThread( is2->hThread );\r
10346     }\r
10347   }\r
10348 \r
10349   return (InputSourceRef) is;\r
10350 }\r
10351 \r
10352 void\r
10353 RemoveInputSource(InputSourceRef isr)\r
10354 {\r
10355   InputSource *is;\r
10356 \r
10357   is = (InputSource *) isr;\r
10358   is->hThread = NULL;  /* tell thread to stop */\r
10359   CloseHandle(is->hThread);\r
10360   if (is->second != NULL) {\r
10361     is->second->hThread = NULL;\r
10362     CloseHandle(is->second->hThread);\r
10363   }\r
10364 }\r
10365 \r
10366 int no_wrap(char *message, int count)\r
10367 {\r
10368     ConsoleOutput(message, count, FALSE);\r
10369     return count;\r
10370 }\r
10371 \r
10372 int\r
10373 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10374 {\r
10375   DWORD dOutCount;\r
10376   int outCount = SOCKET_ERROR;\r
10377   ChildProc *cp = (ChildProc *) pr;\r
10378   static OVERLAPPED ovl;\r
10379   static int line = 0;\r
10380 \r
10381   if (pr == NoProc)\r
10382   {\r
10383     if (appData.noJoin || !appData.useInternalWrap)\r
10384       return no_wrap(message, count);\r
10385     else\r
10386     {\r
10387       int width = get_term_width();\r
10388       int len = wrap(NULL, message, count, width, &line);\r
10389       char *msg = malloc(len);\r
10390       int dbgchk;\r
10391 \r
10392       if (!msg)\r
10393         return no_wrap(message, count);\r
10394       else\r
10395       {\r
10396         dbgchk = wrap(msg, message, count, width, &line);\r
10397         if (dbgchk != len && appData.debugMode)\r
10398             fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);\r
10399         ConsoleOutput(msg, len, FALSE);\r
10400         free(msg);\r
10401         return len;\r
10402       }\r
10403     }\r
10404   }\r
10405 \r
10406   if (ovl.hEvent == NULL) {\r
10407     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10408   }\r
10409   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10410 \r
10411   switch (cp->kind) {\r
10412   case CPSock:\r
10413   case CPRcmd:\r
10414     outCount = send(cp->sock, message, count, 0);\r
10415     if (outCount == SOCKET_ERROR) {\r
10416       *outError = WSAGetLastError();\r
10417     } else {\r
10418       *outError = NO_ERROR;\r
10419     }\r
10420     break;\r
10421 \r
10422   case CPReal:\r
10423     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10424                   &dOutCount, NULL)) {\r
10425       *outError = NO_ERROR;\r
10426       outCount = (int) dOutCount;\r
10427     } else {\r
10428       *outError = GetLastError();\r
10429     }\r
10430     break;\r
10431 \r
10432   case CPComm:\r
10433     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10434                             &dOutCount, &ovl);\r
10435     if (*outError == NO_ERROR) {\r
10436       outCount = (int) dOutCount;\r
10437     }\r
10438     break;\r
10439   }\r
10440   return outCount;\r
10441 }\r
10442 \r
10443 int\r
10444 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10445                        long msdelay)\r
10446 {\r
10447   /* Ignore delay, not implemented for WinBoard */\r
10448   return OutputToProcess(pr, message, count, outError);\r
10449 }\r
10450 \r
10451 \r
10452 void\r
10453 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10454                         char *buf, int count, int error)\r
10455 {\r
10456   DisplayFatalError("Not implemented", 0, 1);\r
10457 }\r
10458 \r
10459 /* see wgamelist.c for Game List functions */\r
10460 /* see wedittags.c for Edit Tags functions */\r
10461 \r
10462 \r
10463 VOID\r
10464 ICSInitScript()\r
10465 {\r
10466   FILE *f;\r
10467   char buf[MSG_SIZ];\r
10468   char *dummy;\r
10469 \r
10470   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10471     f = fopen(buf, "r");\r
10472     if (f != NULL) {\r
10473       ProcessICSInitScript(f);\r
10474       fclose(f);\r
10475     }\r
10476   }\r
10477 }\r
10478 \r
10479 \r
10480 VOID\r
10481 StartAnalysisClock()\r
10482 {\r
10483   if (analysisTimerEvent) return;\r
10484   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10485                                         (UINT) 2000, NULL);\r
10486 }\r
10487 \r
10488 LRESULT CALLBACK\r
10489 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10490 {\r
10491   static HANDLE hwndText;\r
10492   RECT rect;\r
10493   static int sizeX, sizeY;\r
10494   int newSizeX, newSizeY, flags;\r
10495   MINMAXINFO *mmi;\r
10496 \r
10497   switch (message) {\r
10498   case WM_INITDIALOG: /* message: initialize dialog box */\r
10499     /* Initialize the dialog items */\r
10500     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10501     SetWindowText(hDlg, analysisTitle);\r
10502     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10503     /* Size and position the dialog */\r
10504     if (!analysisDialog) {\r
10505       analysisDialog = hDlg;\r
10506       flags = SWP_NOZORDER;\r
10507       GetClientRect(hDlg, &rect);\r
10508       sizeX = rect.right;\r
10509       sizeY = rect.bottom;\r
10510       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10511           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10512         WINDOWPLACEMENT wp;\r
10513         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10514         wp.length = sizeof(WINDOWPLACEMENT);\r
10515         wp.flags = 0;\r
10516         wp.showCmd = SW_SHOW;\r
10517         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10518         wp.rcNormalPosition.left = analysisX;\r
10519         wp.rcNormalPosition.right = analysisX + analysisW;\r
10520         wp.rcNormalPosition.top = analysisY;\r
10521         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10522         SetWindowPlacement(hDlg, &wp);\r
10523 \r
10524         GetClientRect(hDlg, &rect);\r
10525         newSizeX = rect.right;\r
10526         newSizeY = rect.bottom;\r
10527         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10528                               newSizeX, newSizeY);\r
10529         sizeX = newSizeX;\r
10530         sizeY = newSizeY;\r
10531       }\r
10532     }\r
10533     return FALSE;\r
10534 \r
10535   case WM_COMMAND: /* message: received a command */\r
10536     switch (LOWORD(wParam)) {\r
10537     case IDCANCEL:\r
10538       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10539           ExitAnalyzeMode();\r
10540           ModeHighlight();\r
10541           return TRUE;\r
10542       }\r
10543       EditGameEvent();\r
10544       return TRUE;\r
10545     default:\r
10546       break;\r
10547     }\r
10548     break;\r
10549 \r
10550   case WM_SIZE:\r
10551     newSizeX = LOWORD(lParam);\r
10552     newSizeY = HIWORD(lParam);\r
10553     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10554     sizeX = newSizeX;\r
10555     sizeY = newSizeY;\r
10556     break;\r
10557 \r
10558   case WM_GETMINMAXINFO:\r
10559     /* Prevent resizing window too small */\r
10560     mmi = (MINMAXINFO *) lParam;\r
10561     mmi->ptMinTrackSize.x = 100;\r
10562     mmi->ptMinTrackSize.y = 100;\r
10563     break;\r
10564   }\r
10565   return FALSE;\r
10566 }\r
10567 \r
10568 VOID\r
10569 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10570 {\r
10571   highlightInfo.sq[0].x = fromX;\r
10572   highlightInfo.sq[0].y = fromY;\r
10573   highlightInfo.sq[1].x = toX;\r
10574   highlightInfo.sq[1].y = toY;\r
10575 }\r
10576 \r
10577 VOID\r
10578 ClearHighlights()\r
10579 {\r
10580   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10581     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10582 }\r
10583 \r
10584 VOID\r
10585 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10586 {\r
10587   premoveHighlightInfo.sq[0].x = fromX;\r
10588   premoveHighlightInfo.sq[0].y = fromY;\r
10589   premoveHighlightInfo.sq[1].x = toX;\r
10590   premoveHighlightInfo.sq[1].y = toY;\r
10591 }\r
10592 \r
10593 VOID\r
10594 ClearPremoveHighlights()\r
10595 {\r
10596   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10597     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10598 }\r
10599 \r
10600 VOID\r
10601 ShutDownFrontEnd()\r
10602 {\r
10603   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10604   DeleteClipboardTempFiles();\r
10605 }\r
10606 \r
10607 void\r
10608 BoardToTop()\r
10609 {\r
10610     if (IsIconic(hwndMain))\r
10611       ShowWindow(hwndMain, SW_RESTORE);\r
10612 \r
10613     SetActiveWindow(hwndMain);\r
10614 }\r
10615 \r
10616 /*\r
10617  * Prototypes for animation support routines\r
10618  */\r
10619 static void ScreenSquare(int column, int row, POINT * pt);\r
10620 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10621      POINT frames[], int * nFrames);\r
10622 \r
10623 \r
10624 void\r
10625 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10626 {       // [HGM] atomic: animate blast wave\r
10627         int i;\r
10628 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10629         explodeInfo.fromX = fromX;\r
10630         explodeInfo.fromY = fromY;\r
10631         explodeInfo.toX = toX;\r
10632         explodeInfo.toY = toY;\r
10633         for(i=1; i<nFrames; i++) {\r
10634             explodeInfo.radius = (i*180)/(nFrames-1);\r
10635             DrawPosition(FALSE, NULL);\r
10636             Sleep(appData.animSpeed);\r
10637         }\r
10638         explodeInfo.radius = 0;\r
10639         DrawPosition(TRUE, NULL);\r
10640 }\r
10641 \r
10642 #define kFactor 4\r
10643 \r
10644 void\r
10645 AnimateMove(board, fromX, fromY, toX, toY)\r
10646      Board board;\r
10647      int fromX;\r
10648      int fromY;\r
10649      int toX;\r
10650      int toY;\r
10651 {\r
10652   ChessSquare piece;\r
10653   POINT start, finish, mid;\r
10654   POINT frames[kFactor * 2 + 1];\r
10655   int nFrames, n;\r
10656 \r
10657   if (!appData.animate) return;\r
10658   if (doingSizing) return;\r
10659   if (fromY < 0 || fromX < 0) return;\r
10660   piece = board[fromY][fromX];\r
10661   if (piece >= EmptySquare) return;\r
10662 \r
10663   ScreenSquare(fromX, fromY, &start);\r
10664   ScreenSquare(toX, toY, &finish);\r
10665 \r
10666   /* All pieces except knights move in straight line */\r
10667   if (piece != WhiteKnight && piece != BlackKnight) {\r
10668     mid.x = start.x + (finish.x - start.x) / 2;\r
10669     mid.y = start.y + (finish.y - start.y) / 2;\r
10670   } else {\r
10671     /* Knight: make diagonal movement then straight */\r
10672     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10673        mid.x = start.x + (finish.x - start.x) / 2;\r
10674        mid.y = finish.y;\r
10675      } else {\r
10676        mid.x = finish.x;\r
10677        mid.y = start.y + (finish.y - start.y) / 2;\r
10678      }\r
10679   }\r
10680   \r
10681   /* Don't use as many frames for very short moves */\r
10682   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10683     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10684   else\r
10685     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10686 \r
10687   animInfo.from.x = fromX;\r
10688   animInfo.from.y = fromY;\r
10689   animInfo.to.x = toX;\r
10690   animInfo.to.y = toY;\r
10691   animInfo.lastpos = start;\r
10692   animInfo.piece = piece;\r
10693   for (n = 0; n < nFrames; n++) {\r
10694     animInfo.pos = frames[n];\r
10695     DrawPosition(FALSE, NULL);\r
10696     animInfo.lastpos = animInfo.pos;\r
10697     Sleep(appData.animSpeed);\r
10698   }\r
10699   animInfo.pos = finish;\r
10700   DrawPosition(FALSE, NULL);\r
10701   animInfo.piece = EmptySquare;\r
10702   if(gameInfo.variant == VariantAtomic && \r
10703      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10704         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10705 }\r
10706 \r
10707 /*      Convert board position to corner of screen rect and color       */\r
10708 \r
10709 static void\r
10710 ScreenSquare(column, row, pt)\r
10711      int column; int row; POINT * pt;\r
10712 {\r
10713   if (flipView) {\r
10714     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10715     pt->y = lineGap + row * (squareSize + lineGap);\r
10716   } else {\r
10717     pt->x = lineGap + column * (squareSize + lineGap);\r
10718     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10719   }\r
10720 }\r
10721 \r
10722 /*      Generate a series of frame coords from start->mid->finish.\r
10723         The movement rate doubles until the half way point is\r
10724         reached, then halves back down to the final destination,\r
10725         which gives a nice slow in/out effect. The algorithmn\r
10726         may seem to generate too many intermediates for short\r
10727         moves, but remember that the purpose is to attract the\r
10728         viewers attention to the piece about to be moved and\r
10729         then to where it ends up. Too few frames would be less\r
10730         noticeable.                                             */\r
10731 \r
10732 static void\r
10733 Tween(start, mid, finish, factor, frames, nFrames)\r
10734      POINT * start; POINT * mid;\r
10735      POINT * finish; int factor;\r
10736      POINT frames[]; int * nFrames;\r
10737 {\r
10738   int n, fraction = 1, count = 0;\r
10739 \r
10740   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10741   for (n = 0; n < factor; n++)\r
10742     fraction *= 2;\r
10743   for (n = 0; n < factor; n++) {\r
10744     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10745     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10746     count ++;\r
10747     fraction = fraction / 2;\r
10748   }\r
10749   \r
10750   /* Midpoint */\r
10751   frames[count] = *mid;\r
10752   count ++;\r
10753   \r
10754   /* Slow out, stepping 1/2, then 1/4, ... */\r
10755   fraction = 2;\r
10756   for (n = 0; n < factor; n++) {\r
10757     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10758     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10759     count ++;\r
10760     fraction = fraction * 2;\r
10761   }\r
10762   *nFrames = count;\r
10763 }\r
10764 \r
10765 void\r
10766 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10767 {\r
10768     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10769 \r
10770     EvalGraphSet( first, last, current, pvInfoList );\r
10771 }\r
10772 \r
10773 void SetProgramStats( FrontEndProgramStats * stats )\r
10774 {\r
10775     EngineOutputUpdate( stats );\r
10776 }\r