added a "Mute All Sounds" item in the WinBoard Options menu, on Eric's request
[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 #define oldDialog (_winmajor < 4)\r
229 #endif\r
230 \r
231 char *defaultTextAttribs[] = \r
232 {\r
233   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
234   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
235   COLOR_NONE\r
236 };\r
237 \r
238 typedef struct {\r
239   char *name;\r
240   int squareSize;\r
241   int lineGap;\r
242   int smallLayout;\r
243   int tinyLayout;\r
244   int cliWidth, cliHeight;\r
245 } SizeInfo;\r
246 \r
247 SizeInfo sizeInfo[] = \r
248 {\r
249   { "tiny",     21, 0, 1, 1, 0, 0 },\r
250   { "teeny",    25, 1, 1, 1, 0, 0 },\r
251   { "dinky",    29, 1, 1, 1, 0, 0 },\r
252   { "petite",   33, 1, 1, 1, 0, 0 },\r
253   { "slim",     37, 2, 1, 0, 0, 0 },\r
254   { "small",    40, 2, 1, 0, 0, 0 },\r
255   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
256   { "middling", 49, 2, 0, 0, 0, 0 },\r
257   { "average",  54, 2, 0, 0, 0, 0 },\r
258   { "moderate", 58, 3, 0, 0, 0, 0 },\r
259   { "medium",   64, 3, 0, 0, 0, 0 },\r
260   { "bulky",    72, 3, 0, 0, 0, 0 },\r
261   { "large",    80, 3, 0, 0, 0, 0 },\r
262   { "big",      87, 3, 0, 0, 0, 0 },\r
263   { "huge",     95, 3, 0, 0, 0, 0 },\r
264   { "giant",    108, 3, 0, 0, 0, 0 },\r
265   { "colossal", 116, 4, 0, 0, 0, 0 },\r
266   { "titanic",  129, 4, 0, 0, 0, 0 },\r
267   { NULL, 0, 0, 0, 0, 0, 0 }\r
268 };\r
269 \r
270 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
271 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
272 {\r
273   { 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
274   { 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
275   { 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
276   { 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
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283   { 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
284   { 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
285   { 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
286   { 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
287   { 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
288   { 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
289   { 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
290   { 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
291 };\r
292 \r
293 MyFont *font[NUM_SIZES][NUM_FONTS];\r
294 \r
295 typedef struct {\r
296   char *label;\r
297   int id;\r
298   HWND hwnd;\r
299   WNDPROC wndproc;\r
300 } MyButtonDesc;\r
301 \r
302 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
303 #define N_BUTTONS 5\r
304 \r
305 MyButtonDesc buttonDesc[N_BUTTONS] =\r
306 {\r
307   {"<<", IDM_ToStart, NULL, NULL},\r
308   {"<", IDM_Backward, NULL, NULL},\r
309   {"P", IDM_Pause, NULL, NULL},\r
310   {">", IDM_Forward, NULL, NULL},\r
311   {">>", IDM_ToEnd, NULL, NULL},\r
312 };\r
313 \r
314 int tinyLayout = 0, smallLayout = 0;\r
315 #define MENU_BAR_ITEMS 7\r
316 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
317   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
318   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
319 };\r
320 \r
321 \r
322 MySound sounds[(int)NSoundClasses];\r
323 MyTextAttribs textAttribs[(int)NColorClasses];\r
324 \r
325 MyColorizeAttribs colorizeAttribs[] = {\r
326   { (COLORREF)0, 0, "Shout Text" },\r
327   { (COLORREF)0, 0, "SShout/CShout" },\r
328   { (COLORREF)0, 0, "Channel 1 Text" },\r
329   { (COLORREF)0, 0, "Channel Text" },\r
330   { (COLORREF)0, 0, "Kibitz Text" },\r
331   { (COLORREF)0, 0, "Tell Text" },\r
332   { (COLORREF)0, 0, "Challenge Text" },\r
333   { (COLORREF)0, 0, "Request Text" },\r
334   { (COLORREF)0, 0, "Seek Text" },\r
335   { (COLORREF)0, 0, "Normal Text" },\r
336   { (COLORREF)0, 0, "None" }\r
337 };\r
338 \r
339 \r
340 \r
341 static char *commentTitle;\r
342 static char *commentText;\r
343 static int commentIndex;\r
344 static Boolean editComment = FALSE;\r
345 HWND commentDialog = NULL;\r
346 BOOLEAN commentDialogUp = FALSE;\r
347 static int commentX, commentY, commentH, commentW;\r
348 \r
349 static char *analysisTitle;\r
350 static char *analysisText;\r
351 HWND analysisDialog = NULL;\r
352 BOOLEAN analysisDialogUp = FALSE;\r
353 static int analysisX, analysisY, analysisH, analysisW;\r
354 \r
355 char errorTitle[MSG_SIZ];\r
356 char errorMessage[2*MSG_SIZ];\r
357 HWND errorDialog = NULL;\r
358 BOOLEAN moveErrorMessageUp = FALSE;\r
359 BOOLEAN consoleEcho = TRUE;\r
360 CHARFORMAT consoleCF;\r
361 COLORREF consoleBackgroundColor;\r
362 \r
363 char *programVersion;\r
364 \r
365 #define CPReal 1\r
366 #define CPComm 2\r
367 #define CPSock 3\r
368 #define CPRcmd 4\r
369 typedef int CPKind;\r
370 \r
371 typedef struct {\r
372   CPKind kind;\r
373   HANDLE hProcess;\r
374   DWORD pid;\r
375   HANDLE hTo;\r
376   HANDLE hFrom;\r
377   SOCKET sock;\r
378   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
379 } ChildProc;\r
380 \r
381 #define INPUT_SOURCE_BUF_SIZE 4096\r
382 \r
383 typedef struct _InputSource {\r
384   CPKind kind;\r
385   HANDLE hFile;\r
386   SOCKET sock;\r
387   int lineByLine;\r
388   HANDLE hThread;\r
389   DWORD id;\r
390   char buf[INPUT_SOURCE_BUF_SIZE];\r
391   char *next;\r
392   DWORD count;\r
393   int error;\r
394   InputCallback func;\r
395   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
396   VOIDSTAR closure;\r
397 } InputSource;\r
398 \r
399 InputSource *consoleInputSource;\r
400 \r
401 DCB dcb;\r
402 \r
403 /* forward */\r
404 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
405 VOID ConsoleCreate();\r
406 LRESULT CALLBACK\r
407   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
408 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
409 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
410 VOID ParseCommSettings(char *arg, DCB *dcb);\r
411 LRESULT CALLBACK\r
412   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
413 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
414 void ParseIcsTextMenu(char *icsTextMenuString);\r
415 VOID PopUpMoveDialog(char firstchar);\r
416 VOID PopUpNameDialog(char firstchar);\r
417 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
418 \r
419 /* [AS] */\r
420 int NewGameFRC();\r
421 int GameListOptions();\r
422 \r
423 HWND moveHistoryDialog = NULL;\r
424 BOOLEAN moveHistoryDialogUp = FALSE;\r
425 \r
426 WindowPlacement wpMoveHistory;\r
427 \r
428 HWND evalGraphDialog = NULL;\r
429 BOOLEAN evalGraphDialogUp = FALSE;\r
430 \r
431 WindowPlacement wpEvalGraph;\r
432 \r
433 HWND engineOutputDialog = NULL;\r
434 BOOLEAN engineOutputDialogUp = FALSE;\r
435 \r
436 WindowPlacement wpEngineOutput;\r
437 WindowPlacement wpGameList;\r
438 WindowPlacement wpConsole;\r
439 \r
440 VOID MoveHistoryPopUp();\r
441 VOID MoveHistoryPopDown();\r
442 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
443 BOOL MoveHistoryIsUp();\r
444 \r
445 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
446 VOID EvalGraphPopUp();\r
447 VOID EvalGraphPopDown();\r
448 BOOL EvalGraphIsUp();\r
449 \r
450 VOID EngineOutputPopUp();\r
451 VOID EngineOutputPopDown();\r
452 BOOL EngineOutputIsUp();\r
453 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
454 \r
455 VOID EngineOptionsPopup(); // [HGM] settings\r
456 \r
457 VOID GothicPopUp(char *title, VariantClass variant);\r
458 /*\r
459  * Setting "frozen" should disable all user input other than deleting\r
460  * the window.  We do this while engines are initializing themselves.\r
461  */\r
462 static int frozen = 0;\r
463 static int oldMenuItemState[MENU_BAR_ITEMS];\r
464 void FreezeUI()\r
465 {\r
466   HMENU hmenu;\r
467   int i;\r
468 \r
469   if (frozen) return;\r
470   frozen = 1;\r
471   hmenu = GetMenu(hwndMain);\r
472   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
473     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
474   }\r
475   DrawMenuBar(hwndMain);\r
476 }\r
477 \r
478 /* Undo a FreezeUI */\r
479 void ThawUI()\r
480 {\r
481   HMENU hmenu;\r
482   int i;\r
483 \r
484   if (!frozen) return;\r
485   frozen = 0;\r
486   hmenu = GetMenu(hwndMain);\r
487   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
488     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
489   }\r
490   DrawMenuBar(hwndMain);\r
491 }\r
492 \r
493 static int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
494 \r
495 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
496 #ifdef JAWS\r
497 #include "jaws.c"\r
498 #else\r
499 #define JAWS_INIT\r
500 #define JAWS_ARGS\r
501 #define JAWS_ALT_INTERCEPT\r
502 #define JAWS_KB_NAVIGATION\r
503 #define JAWS_MENU_ITEMS\r
504 #define JAWS_SILENCE\r
505 #define JAWS_REPLAY\r
506 #define JAWS_ACCEL\r
507 #define JAWS_COPYRIGHT\r
508 #define JAWS_DELETE(X) X\r
509 #define SAYMACHINEMOVE()\r
510 #define SAY(X)\r
511 #endif\r
512 \r
513 /*---------------------------------------------------------------------------*\\r
514  *\r
515  * WinMain\r
516  *\r
517 \*---------------------------------------------------------------------------*/\r
518 \r
519 int APIENTRY\r
520 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
521         LPSTR lpCmdLine, int nCmdShow)\r
522 {\r
523   MSG msg;\r
524   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
525 //  INITCOMMONCONTROLSEX ex;\r
526 \r
527   debugFP = stderr;\r
528 \r
529   LoadLibrary("RICHED32.DLL");\r
530   consoleCF.cbSize = sizeof(CHARFORMAT);\r
531 \r
532   if (!InitApplication(hInstance)) {\r
533     return (FALSE);\r
534   }\r
535   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
536     return (FALSE);\r
537   }\r
538 \r
539   JAWS_INIT\r
540 \r
541 //  InitCommonControlsEx(&ex);\r
542   InitCommonControls();\r
543 \r
544   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
545   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
546   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
547 \r
548   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
549 \r
550   while (GetMessage(&msg, /* message structure */\r
551                     NULL, /* handle of window receiving the message */\r
552                     0,    /* lowest message to examine */\r
553                     0))   /* highest message to examine */\r
554     {\r
555 \r
556       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
557         // [HGM] navigate: switch between all windows with tab\r
558         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
559         int i, currentElement = 0;\r
560 \r
561         // first determine what element of the chain we come from (if any)\r
562         if(appData.icsActive) {\r
563             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
564             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
565         }\r
566         if(engineOutputDialog && EngineOutputIsUp()) {\r
567             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
568             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
569         }\r
570         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
571             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
572         }\r
573         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
574         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
575         if(msg.hwnd == e1)                 currentElement = 2; else\r
576         if(msg.hwnd == e2)                 currentElement = 3; else\r
577         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
578         if(msg.hwnd == mh)                currentElement = 4; else\r
579         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
580         if(msg.hwnd == hText)  currentElement = 5; else\r
581         if(msg.hwnd == hInput) currentElement = 6; else\r
582         for (i = 0; i < N_BUTTONS; i++) {\r
583             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
584         }\r
585 \r
586         // determine where to go to\r
587         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
588           do {\r
589             currentElement = (currentElement + direction) % 7;\r
590             switch(currentElement) {\r
591                 case 0:\r
592                   h = hwndMain; break; // passing this case always makes the loop exit\r
593                 case 1:\r
594                   h = buttonDesc[0].hwnd; break; // could be NULL\r
595                 case 2:\r
596                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
597                   h = e1; break;\r
598                 case 3:\r
599                   if(!EngineOutputIsUp()) continue;\r
600                   h = e2; break;\r
601                 case 4:\r
602                   if(!MoveHistoryIsUp()) continue;\r
603                   h = mh; break;\r
604 //              case 6: // input to eval graph does not seem to get here!\r
605 //                if(!EvalGraphIsUp()) continue;\r
606 //                h = evalGraphDialog; break;\r
607                 case 5:\r
608                   if(!appData.icsActive) continue;\r
609                   SAY("display");\r
610                   h = hText; break;\r
611                 case 6:\r
612                   if(!appData.icsActive) continue;\r
613                   SAY("input");\r
614                   h = hInput; break;\r
615             }\r
616           } while(h == 0);\r
617 \r
618           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
619           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
620           SetFocus(h);\r
621 \r
622           continue; // this message now has been processed\r
623         }\r
624       }\r
625 \r
626       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
627           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
628           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
629           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
630           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
631           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
632           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
633           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
634           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
635           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
636         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
637         for(i=0; i<MAX_CHAT; i++) \r
638             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
639                 done = 1; break;\r
640         }\r
641         if(done) continue; // [HGM] chat: end patch\r
642         TranslateMessage(&msg); /* Translates virtual key codes */\r
643         DispatchMessage(&msg);  /* Dispatches message to window */\r
644       }\r
645     }\r
646 \r
647 \r
648   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
649 }\r
650 \r
651 /*---------------------------------------------------------------------------*\\r
652  *\r
653  * Initialization functions\r
654  *\r
655 \*---------------------------------------------------------------------------*/\r
656 \r
657 void\r
658 SetUserLogo()\r
659 {   // update user logo if necessary\r
660     static char oldUserName[MSG_SIZ], *curName;\r
661 \r
662     if(appData.autoLogo) {\r
663           curName = UserName();\r
664           if(strcmp(curName, oldUserName)) {\r
665                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
666                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
667                 strcpy(oldUserName, curName);\r
668           }\r
669     }\r
670 }\r
671 \r
672 BOOL\r
673 InitApplication(HINSTANCE hInstance)\r
674 {\r
675   WNDCLASS wc;\r
676 \r
677   /* Fill in window class structure with parameters that describe the */\r
678   /* main window. */\r
679 \r
680   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
681   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
682   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
683   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
684   wc.hInstance     = hInstance;         /* Owner of this class */\r
685   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
686   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
687   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
688   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
689   wc.lpszClassName = szAppName;                 /* Name to register as */\r
690 \r
691   /* Register the window class and return success/failure code. */\r
692   if (!RegisterClass(&wc)) return FALSE;\r
693 \r
694   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
695   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
696   wc.cbClsExtra    = 0;\r
697   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
698   wc.hInstance     = hInstance;\r
699   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
700   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
701   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
702   wc.lpszMenuName  = NULL;\r
703   wc.lpszClassName = szConsoleName;\r
704 \r
705   if (!RegisterClass(&wc)) return FALSE;\r
706   return TRUE;\r
707 }\r
708 \r
709 \r
710 /* Set by InitInstance, used by EnsureOnScreen */\r
711 int screenHeight, screenWidth;\r
712 \r
713 void\r
714 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
715 {\r
716 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
717   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
718   if (*x > screenWidth - 32) *x = 0;\r
719   if (*y > screenHeight - 32) *y = 0;\r
720   if (*x < minX) *x = minX;\r
721   if (*y < minY) *y = minY;\r
722 }\r
723 \r
724 BOOL\r
725 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
726 {\r
727   HWND hwnd; /* Main window handle. */\r
728   int ibs;\r
729   WINDOWPLACEMENT wp;\r
730   char *filepart;\r
731 \r
732   hInst = hInstance;    /* Store instance handle in our global variable */\r
733 \r
734   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
735     *filepart = NULLCHAR;\r
736   } else {\r
737     GetCurrentDirectory(MSG_SIZ, installDir);\r
738   }\r
739   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
740   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
741   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
742   if (appData.debugMode) {\r
743     debugFP = fopen(appData.nameOfDebugFile, "w");\r
744     setbuf(debugFP, NULL);\r
745   }\r
746 \r
747   InitBackEnd1();\r
748 \r
749 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
750 //  InitEngineUCI( installDir, &second );\r
751 \r
752   /* Create a main window for this application instance. */\r
753   hwnd = CreateWindow(szAppName, szTitle,\r
754                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
755                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
756                       NULL, NULL, hInstance, NULL);\r
757   hwndMain = hwnd;\r
758 \r
759   /* If window could not be created, return "failure" */\r
760   if (!hwnd) {\r
761     return (FALSE);\r
762   }\r
763 \r
764   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
765   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
766       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
767 \r
768       if (first.programLogo == NULL && appData.debugMode) {\r
769           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
770       }\r
771   } else if(appData.autoLogo) {\r
772       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
773         char buf[MSG_SIZ];\r
774         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
775         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
776       }\r
777   }\r
778 \r
779   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
780       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
781 \r
782       if (second.programLogo == NULL && appData.debugMode) {\r
783           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
784       }\r
785   } else if(appData.autoLogo) {\r
786       char buf[MSG_SIZ];\r
787       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
788         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
789         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
790       } else\r
791       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
792         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
793         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
794       }\r
795   }\r
796 \r
797   SetUserLogo();\r
798 \r
799   iconWhite = LoadIcon(hInstance, "icon_white");\r
800   iconBlack = LoadIcon(hInstance, "icon_black");\r
801   iconCurrent = iconWhite;\r
802   InitDrawingColors();\r
803   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
804   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
805   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
806     /* Compute window size for each board size, and use the largest\r
807        size that fits on this screen as the default. */\r
808     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
809     if (boardSize == (BoardSize)-1 &&\r
810         winH <= screenHeight\r
811            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
812         && winW <= screenWidth) {\r
813       boardSize = (BoardSize)ibs;\r
814     }\r
815   }\r
816 \r
817   InitDrawingSizes(boardSize, 0);\r
818   InitMenuChecks();\r
819   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
820 \r
821   /* [AS] Load textures if specified */\r
822   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
823   \r
824   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
825       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
826       liteBackTextureMode = appData.liteBackTextureMode;\r
827 \r
828       if (liteBackTexture == NULL && appData.debugMode) {\r
829           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
830       }\r
831   }\r
832   \r
833   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
834       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
835       darkBackTextureMode = appData.darkBackTextureMode;\r
836 \r
837       if (darkBackTexture == NULL && appData.debugMode) {\r
838           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
839       }\r
840   }\r
841 \r
842   mysrandom( (unsigned) time(NULL) );\r
843 \r
844   /* [AS] Restore layout */\r
845   if( wpMoveHistory.visible ) {\r
846       MoveHistoryPopUp();\r
847   }\r
848 \r
849   if( wpEvalGraph.visible ) {\r
850       EvalGraphPopUp();\r
851   }\r
852 \r
853   if( wpEngineOutput.visible ) {\r
854       EngineOutputPopUp();\r
855   }\r
856 \r
857   InitBackEnd2();\r
858 \r
859   /* Make the window visible; update its client area; and return "success" */\r
860   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
861   wp.length = sizeof(WINDOWPLACEMENT);\r
862   wp.flags = 0;\r
863   wp.showCmd = nCmdShow;\r
864   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
865   wp.rcNormalPosition.left = boardX;\r
866   wp.rcNormalPosition.right = boardX + winWidth;\r
867   wp.rcNormalPosition.top = boardY;\r
868   wp.rcNormalPosition.bottom = boardY + winHeight;\r
869   SetWindowPlacement(hwndMain, &wp);\r
870 \r
871   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
872                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
873 \r
874   if (hwndConsole) {\r
875 #if AOT_CONSOLE\r
876     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
877                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
878 #endif\r
879     ShowWindow(hwndConsole, nCmdShow);\r
880   }\r
881   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
882   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
883 \r
884   return TRUE;\r
885 \r
886 }\r
887 \r
888 \r
889 typedef enum {\r
890   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
891   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
892   ArgSettingsFilename,\r
893   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
894 } ArgType;\r
895 \r
896 typedef struct {\r
897   char *argName;\r
898   ArgType argType;\r
899   /***\r
900   union {\r
901     String *pString;       // ArgString\r
902     int *pInt;             // ArgInt\r
903     float *pFloat;         // ArgFloat\r
904     Boolean *pBoolean;     // ArgBoolean\r
905     COLORREF *pColor;      // ArgColor\r
906     ColorClass cc;         // ArgAttribs\r
907     String *pFilename;     // ArgFilename\r
908     BoardSize *pBoardSize; // ArgBoardSize\r
909     int whichFont;         // ArgFont\r
910     DCB *pDCB;             // ArgCommSettings\r
911     String *pFilename;     // ArgSettingsFilename\r
912   } argLoc;\r
913   ***/\r
914   LPVOID argLoc;\r
915   BOOL save;\r
916 } ArgDescriptor;\r
917 \r
918 int junk;\r
919 ArgDescriptor argDescriptors[] = {\r
920   /* positional arguments */\r
921   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
922   { "", ArgNone, NULL },\r
923   /* keyword arguments */\r
924   JAWS_ARGS\r
925   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
926   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
927   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
928   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
929   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
930   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
931   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
932   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
933   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
934   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
935   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
936   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
937   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
938   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
939   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
940   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
941   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
942   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
943     FALSE },\r
944   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
945     FALSE },\r
946   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
947     FALSE },\r
948   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
949   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
950     FALSE },\r
951   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
952   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
953   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
954   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
955   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
956   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
957   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
958   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
959   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
960   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
961   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
962   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
963   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
964   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
965   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
966   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
967   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
968   /*!!bitmapDirectory?*/\r
969   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
970   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
971   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
972   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
973   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
974   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
975   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
976   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
977   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
978   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
979   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
980   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
981   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
982   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
983   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
984   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
985   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
986   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
987   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
988   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
989   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
990   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
991   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
992   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
993   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
994   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
995   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
996   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
997   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
998   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
999   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
1000   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
1001   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
1002   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1003   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1004   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
1005   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
1006   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
1007   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
1008   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1009   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1010   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1011   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1012   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1013   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1014   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1015   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1016   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
1017   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1018   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1019   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1020   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1021   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1022   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1023   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1024   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1025   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1026   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1027   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1028   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1029   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1030   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1031   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1032   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1033   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1034   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1035   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1036   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1037   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1038   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1039   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1040   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1041   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1042   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1043   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1044   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1045   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1046   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1047   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1048   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1049   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1050   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1051   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1052   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1053   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1054   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1055   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1056   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1057   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1058   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1059   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1060   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1061   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1062   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1063     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1064   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1065   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1066   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1067   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1068   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1069   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1070   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1071   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1072     TRUE }, /* must come after all fonts */\r
1073   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1074   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1075     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1076   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1077   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1078   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1079   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1080   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1081   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1082   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1083   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1084   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1085   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1086   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1087   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1088   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1089   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1090   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1091   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1092   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1093   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1094   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1095   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1096   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1097   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1098   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1099   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1100   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1101   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1102   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1103   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1104 #if 0\r
1105   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1106   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1107 #endif\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 \r
1339 #ifdef ZIPPY\r
1340   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1341   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1342   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1343   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1344   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1345   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1346   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1347   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1348   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1349   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1350   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1351   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1352   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1353     FALSE },\r
1354   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1355   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1356   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1357   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1358   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1359   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1360   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1361     FALSE },\r
1362   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1363   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1364   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1365   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1366   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1367   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1368   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1369   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1370   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1371   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1372   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1373   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1374   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1375   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1376   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1377   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1378   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1379   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1380   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1381 #endif\r
1382   /* [HGM] options for broadcasting and time odds */\r
1383   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1384   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1385   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1386   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1387   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1388   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1389   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1390   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1391   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1392   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1393   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1394 \r
1395   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1396   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1397   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1398   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1399   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1400   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1401   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1402   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1403   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1404   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1405   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1406   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1407   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1408   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1409   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1410   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1411   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1412   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1413   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1414   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1415   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1416   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1417   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1418   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1419   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1420   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1421   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1422   /* [AS] Layout stuff */\r
1423   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1424   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1425   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1426   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1427   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1428 \r
1429   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1430   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1431   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1432   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1433   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1434 \r
1435   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1436   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1437   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1438   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1439   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1440 \r
1441   { NULL, ArgNone, NULL, FALSE }\r
1442 };\r
1443 \r
1444 \r
1445 /* Kludge for indirection files on command line */\r
1446 char* lastIndirectionFilename;\r
1447 ArgDescriptor argDescriptorIndirection =\r
1448 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1449 \r
1450 \r
1451 VOID\r
1452 ExitArgError(char *msg, char *badArg)\r
1453 {\r
1454   char buf[MSG_SIZ];\r
1455 \r
1456   sprintf(buf, "%s %s", msg, badArg);\r
1457   DisplayFatalError(buf, 0, 2);\r
1458   exit(2);\r
1459 }\r
1460 \r
1461 /* Command line font name parser.  NULL name means do nothing.\r
1462    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1463    For backward compatibility, syntax without the colon is also\r
1464    accepted, but font names with digits in them won't work in that case.\r
1465 */\r
1466 VOID\r
1467 ParseFontName(char *name, MyFontParams *mfp)\r
1468 {\r
1469   char *p, *q;\r
1470   if (name == NULL) return;\r
1471   p = name;\r
1472   q = strchr(p, ':');\r
1473   if (q) {\r
1474     if (q - p >= sizeof(mfp->faceName))\r
1475       ExitArgError("Font name too long:", name);\r
1476     memcpy(mfp->faceName, p, q - p);\r
1477     mfp->faceName[q - p] = NULLCHAR;\r
1478     p = q + 1;\r
1479   } else {\r
1480     q = mfp->faceName;\r
1481     while (*p && !isdigit(*p)) {\r
1482       *q++ = *p++;\r
1483       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1484         ExitArgError("Font name too long:", name);\r
1485     }\r
1486     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1487     *q = NULLCHAR;\r
1488   }\r
1489   if (!*p) ExitArgError("Font point size missing:", name);\r
1490   mfp->pointSize = (float) atof(p);\r
1491   mfp->bold = (strchr(p, 'b') != NULL);\r
1492   mfp->italic = (strchr(p, 'i') != NULL);\r
1493   mfp->underline = (strchr(p, 'u') != NULL);\r
1494   mfp->strikeout = (strchr(p, 's') != NULL);\r
1495 }\r
1496 \r
1497 /* Color name parser.\r
1498    X version accepts X color names, but this one\r
1499    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1500 COLORREF\r
1501 ParseColorName(char *name)\r
1502 {\r
1503   int red, green, blue, count;\r
1504   char buf[MSG_SIZ];\r
1505 \r
1506   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1507   if (count != 3) {\r
1508     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1509       &red, &green, &blue);\r
1510   }\r
1511   if (count != 3) {\r
1512     sprintf(buf, "Can't parse color name %s", name);\r
1513     DisplayError(buf, 0);\r
1514     return RGB(0, 0, 0);\r
1515   }\r
1516   return PALETTERGB(red, green, blue);\r
1517 }\r
1518 \r
1519 \r
1520 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1521 {\r
1522   char *e = argValue;\r
1523   int eff = 0;\r
1524 \r
1525   while (*e) {\r
1526     if (*e == 'b')      eff |= CFE_BOLD;\r
1527     else if (*e == 'i') eff |= CFE_ITALIC;\r
1528     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1529     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1530     else if (*e == '#' || isdigit(*e)) break;\r
1531     e++;\r
1532   }\r
1533   *effects = eff;\r
1534   *color   = ParseColorName(e);\r
1535 }\r
1536 \r
1537 \r
1538 BoardSize\r
1539 ParseBoardSize(char *name)\r
1540 {\r
1541   BoardSize bs = SizeTiny;\r
1542   while (sizeInfo[bs].name != NULL) {\r
1543     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1544     bs++;\r
1545   }\r
1546   ExitArgError("Unrecognized board size value", name);\r
1547   return bs; /* not reached */\r
1548 }\r
1549 \r
1550 \r
1551 char\r
1552 StringGet(void *getClosure)\r
1553 {\r
1554   char **p = (char **) getClosure;\r
1555   return *((*p)++);\r
1556 }\r
1557 \r
1558 char\r
1559 FileGet(void *getClosure)\r
1560 {\r
1561   int c;\r
1562   FILE* f = (FILE*) getClosure;\r
1563 \r
1564   c = getc(f);\r
1565   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1566   if (c == EOF)\r
1567     return NULLCHAR;\r
1568   else\r
1569     return (char) c;\r
1570 }\r
1571 \r
1572 /* Parse settings file named "name". If file found, return the\r
1573    full name in fullname and return TRUE; else return FALSE */\r
1574 BOOLEAN\r
1575 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1576 {\r
1577   char *dummy;\r
1578   FILE *f;\r
1579   int ok; char buf[MSG_SIZ];\r
1580 \r
1581   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1582   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1583     sprintf(buf, "%s.ini", name);\r
1584     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1585   }\r
1586   if (ok) {\r
1587     f = fopen(fullname, "r");\r
1588     if (f != NULL) {\r
1589       ParseArgs(FileGet, f);\r
1590       fclose(f);\r
1591       return TRUE;\r
1592     }\r
1593   }\r
1594   return FALSE;\r
1595 }\r
1596 \r
1597 VOID\r
1598 ParseArgs(GetFunc get, void *cl)\r
1599 {\r
1600   char argName[ARG_MAX];\r
1601   char argValue[ARG_MAX];\r
1602   ArgDescriptor *ad;\r
1603   char start;\r
1604   char *q;\r
1605   int i, octval;\r
1606   char ch;\r
1607   int posarg = 0;\r
1608 \r
1609   ch = get(cl);\r
1610   for (;;) {\r
1611     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1612     if (ch == NULLCHAR) break;\r
1613     if (ch == ';') {\r
1614       /* Comment to end of line */\r
1615       ch = get(cl);\r
1616       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1617       continue;\r
1618     } else if (ch == '/' || ch == '-') {\r
1619       /* Switch */\r
1620       q = argName;\r
1621       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1622              ch != '\n' && ch != '\t') {\r
1623         *q++ = ch;\r
1624         ch = get(cl);\r
1625       }\r
1626       *q = NULLCHAR;\r
1627 \r
1628       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1629         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1630 \r
1631       if (ad->argName == NULL)\r
1632         ExitArgError("Unrecognized argument", argName);\r
1633 \r
1634     } else if (ch == '@') {\r
1635       /* Indirection file */\r
1636       ad = &argDescriptorIndirection;\r
1637       ch = get(cl);\r
1638     } else {\r
1639       /* Positional argument */\r
1640       ad = &argDescriptors[posarg++];\r
1641       strcpy(argName, ad->argName);\r
1642     }\r
1643 \r
1644     if (ad->argType == ArgTrue) {\r
1645       *(Boolean *) ad->argLoc = TRUE;\r
1646       continue;\r
1647     }\r
1648     if (ad->argType == ArgFalse) {\r
1649       *(Boolean *) ad->argLoc = FALSE;\r
1650       continue;\r
1651     }\r
1652 \r
1653     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1654     if (ch == NULLCHAR || ch == '\n') {\r
1655       ExitArgError("No value provided for argument", argName);\r
1656     }\r
1657     q = argValue;\r
1658     if (ch == '{') {\r
1659       // Quoting with { }.  No characters have to (or can) be escaped.\r
1660       // Thus the string cannot contain a '}' character.\r
1661       start = ch;\r
1662       ch = get(cl);\r
1663       while (start) {\r
1664         switch (ch) {\r
1665         case NULLCHAR:\r
1666           start = NULLCHAR;\r
1667           break;\r
1668           \r
1669         case '}':\r
1670           ch = get(cl);\r
1671           start = NULLCHAR;\r
1672           break;\r
1673 \r
1674         default:\r
1675           *q++ = ch;\r
1676           ch = get(cl);\r
1677           break;\r
1678         }\r
1679       }   \r
1680     } else if (ch == '\'' || ch == '"') {\r
1681       // Quoting with ' ' or " ", with \ as escape character.\r
1682       // Inconvenient for long strings that may contain Windows filenames.\r
1683       start = ch;\r
1684       ch = get(cl);\r
1685       while (start) {\r
1686         switch (ch) {\r
1687         case NULLCHAR:\r
1688           start = NULLCHAR;\r
1689           break;\r
1690 \r
1691         default:\r
1692         not_special:\r
1693           *q++ = ch;\r
1694           ch = get(cl);\r
1695           break;\r
1696 \r
1697         case '\'':\r
1698         case '\"':\r
1699           if (ch == start) {\r
1700             ch = get(cl);\r
1701             start = NULLCHAR;\r
1702             break;\r
1703           } else {\r
1704             goto not_special;\r
1705           }\r
1706 \r
1707         case '\\':\r
1708           if (ad->argType == ArgFilename\r
1709               || ad->argType == ArgSettingsFilename) {\r
1710               goto not_special;\r
1711           }\r
1712           ch = get(cl);\r
1713           switch (ch) {\r
1714           case NULLCHAR:\r
1715             ExitArgError("Incomplete \\ escape in value for", argName);\r
1716             break;\r
1717           case 'n':\r
1718             *q++ = '\n';\r
1719             ch = get(cl);\r
1720             break;\r
1721           case 'r':\r
1722             *q++ = '\r';\r
1723             ch = get(cl);\r
1724             break;\r
1725           case 't':\r
1726             *q++ = '\t';\r
1727             ch = get(cl);\r
1728             break;\r
1729           case 'b':\r
1730             *q++ = '\b';\r
1731             ch = get(cl);\r
1732             break;\r
1733           case 'f':\r
1734             *q++ = '\f';\r
1735             ch = get(cl);\r
1736             break;\r
1737           default:\r
1738             octval = 0;\r
1739             for (i = 0; i < 3; i++) {\r
1740               if (ch >= '0' && ch <= '7') {\r
1741                 octval = octval*8 + (ch - '0');\r
1742                 ch = get(cl);\r
1743               } else {\r
1744                 break;\r
1745               }\r
1746             }\r
1747             if (i > 0) {\r
1748               *q++ = (char) octval;\r
1749             } else {\r
1750               *q++ = ch;\r
1751               ch = get(cl);\r
1752             }\r
1753             break;\r
1754           }\r
1755           break;\r
1756         }\r
1757       }\r
1758     } else {\r
1759       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1760         *q++ = ch;\r
1761         ch = get(cl);\r
1762       }\r
1763     }\r
1764     *q = NULLCHAR;\r
1765 \r
1766     switch (ad->argType) {\r
1767     case ArgInt:\r
1768       *(int *) ad->argLoc = atoi(argValue);\r
1769       break;\r
1770 \r
1771     case ArgX:\r
1772       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1773       break;\r
1774 \r
1775     case ArgY:\r
1776       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1777       break;\r
1778 \r
1779     case ArgZ:\r
1780       *(int *) ad->argLoc = atoi(argValue);\r
1781       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1782       break;\r
1783 \r
1784     case ArgFloat:\r
1785       *(float *) ad->argLoc = (float) atof(argValue);\r
1786       break;\r
1787 \r
1788     case ArgString:\r
1789     case ArgFilename:\r
1790       *(char **) ad->argLoc = strdup(argValue);\r
1791       break;\r
1792 \r
1793     case ArgSettingsFilename:\r
1794       {\r
1795         char fullname[MSG_SIZ];\r
1796         if (ParseSettingsFile(argValue, fullname)) {\r
1797           if (ad->argLoc != NULL) {\r
1798             *(char **) ad->argLoc = strdup(fullname);\r
1799           }\r
1800         } else {\r
1801           if (ad->argLoc != NULL) {\r
1802           } else {\r
1803             ExitArgError("Failed to open indirection file", argValue);\r
1804           }\r
1805         }\r
1806       }\r
1807       break;\r
1808 \r
1809     case ArgBoolean:\r
1810       switch (argValue[0]) {\r
1811       case 't':\r
1812       case 'T':\r
1813         *(Boolean *) ad->argLoc = TRUE;\r
1814         break;\r
1815       case 'f':\r
1816       case 'F':\r
1817         *(Boolean *) ad->argLoc = FALSE;\r
1818         break;\r
1819       default:\r
1820         ExitArgError("Unrecognized boolean argument value", argValue);\r
1821         break;\r
1822       }\r
1823       break;\r
1824 \r
1825     case ArgColor:\r
1826       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1827       break;\r
1828 \r
1829     case ArgAttribs: {\r
1830       ColorClass cc = (ColorClass)ad->argLoc;\r
1831       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1832       }\r
1833       break;\r
1834       \r
1835     case ArgBoardSize:\r
1836       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1837       break;\r
1838 \r
1839     case ArgFont:\r
1840       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1841       break;\r
1842 \r
1843     case ArgCommSettings:\r
1844       ParseCommSettings(argValue, &dcb);\r
1845       break;\r
1846 \r
1847     case ArgNone:\r
1848       ExitArgError("Unrecognized argument", argValue);\r
1849       break;\r
1850     case ArgTrue:\r
1851     case ArgFalse: ;\r
1852     }\r
1853   }\r
1854 }\r
1855 \r
1856 VOID\r
1857 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1858 {\r
1859   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1860   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1861   DeleteDC(hdc);\r
1862   lf->lfWidth = 0;\r
1863   lf->lfEscapement = 0;\r
1864   lf->lfOrientation = 0;\r
1865   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1866   lf->lfItalic = mfp->italic;\r
1867   lf->lfUnderline = mfp->underline;\r
1868   lf->lfStrikeOut = mfp->strikeout;\r
1869   lf->lfCharSet = DEFAULT_CHARSET;\r
1870   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1871   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1872   lf->lfQuality = DEFAULT_QUALITY;\r
1873   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1874   strcpy(lf->lfFaceName, mfp->faceName);\r
1875 }\r
1876 \r
1877 VOID\r
1878 CreateFontInMF(MyFont *mf)\r
1879 {\r
1880   LFfromMFP(&mf->lf, &mf->mfp);\r
1881   if (mf->hf) DeleteObject(mf->hf);\r
1882   mf->hf = CreateFontIndirect(&mf->lf);\r
1883 }\r
1884 \r
1885 VOID\r
1886 SetDefaultTextAttribs()\r
1887 {\r
1888   ColorClass cc;\r
1889   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1890     ParseAttribs(&textAttribs[cc].color, \r
1891                  &textAttribs[cc].effects, \r
1892                  defaultTextAttribs[cc]);\r
1893   }\r
1894 }\r
1895 \r
1896 VOID\r
1897 SetDefaultSounds()\r
1898 {\r
1899   ColorClass cc;\r
1900   SoundClass sc;\r
1901   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1902     textAttribs[cc].sound.name = strdup("");\r
1903     textAttribs[cc].sound.data = NULL;\r
1904   }\r
1905   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1906     sounds[sc].name = strdup("");\r
1907     sounds[sc].data = NULL;\r
1908   }\r
1909   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1910 }\r
1911 \r
1912 VOID\r
1913 LoadAllSounds()\r
1914 {\r
1915   ColorClass cc;\r
1916   SoundClass sc;\r
1917   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1918     MyLoadSound(&textAttribs[cc].sound);\r
1919   }\r
1920   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1921     MyLoadSound(&sounds[sc]);\r
1922   }\r
1923 }\r
1924 \r
1925 VOID\r
1926 InitAppData(LPSTR lpCmdLine)\r
1927 {\r
1928   int i, j;\r
1929   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1930   char *dummy, *p;\r
1931 \r
1932   programName = szAppName;\r
1933 \r
1934   /* Initialize to defaults */\r
1935   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1936   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1937   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1938   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1939   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1940   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1941   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1942   SetDefaultTextAttribs();\r
1943   SetDefaultSounds();\r
1944   appData.movesPerSession = MOVES_PER_SESSION;\r
1945   appData.initString = INIT_STRING;\r
1946   appData.secondInitString = INIT_STRING;\r
1947   appData.firstComputerString = COMPUTER_STRING;\r
1948   appData.secondComputerString = COMPUTER_STRING;\r
1949   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1950   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1951   appData.firstPlaysBlack = FALSE;\r
1952   appData.noChessProgram = FALSE;\r
1953   chessProgram = FALSE;\r
1954   appData.firstHost = FIRST_HOST;\r
1955   appData.secondHost = SECOND_HOST;\r
1956   appData.firstDirectory = FIRST_DIRECTORY;\r
1957   appData.secondDirectory = SECOND_DIRECTORY;\r
1958   appData.bitmapDirectory = "";\r
1959   appData.remoteShell = REMOTE_SHELL;\r
1960   appData.remoteUser = "";\r
1961   appData.timeDelay = TIME_DELAY;\r
1962   appData.timeControl = TIME_CONTROL;\r
1963   appData.timeIncrement = TIME_INCREMENT;\r
1964   appData.icsActive = FALSE;\r
1965   appData.icsHost = "";\r
1966   appData.icsPort = ICS_PORT;\r
1967   appData.icsCommPort = ICS_COMM_PORT;\r
1968   appData.icsLogon = ICS_LOGON;\r
1969   appData.icsHelper = "";\r
1970   appData.useTelnet = FALSE;\r
1971   appData.telnetProgram = TELNET_PROGRAM;\r
1972   appData.gateway = "";\r
1973   appData.loadGameFile = "";\r
1974   appData.loadGameIndex = 0;\r
1975   appData.saveGameFile = "";\r
1976   appData.autoSaveGames = FALSE;\r
1977   appData.loadPositionFile = "";\r
1978   appData.loadPositionIndex = 1;\r
1979   appData.savePositionFile = "";\r
1980   appData.matchMode = FALSE;\r
1981   appData.matchGames = 0;\r
1982   appData.monoMode = FALSE;\r
1983   appData.debugMode = FALSE;\r
1984   appData.clockMode = TRUE;\r
1985   boardSize = (BoardSize) -1; /* determine by screen size */\r
1986   appData.Iconic = FALSE; /*unused*/\r
1987   appData.searchTime = "";\r
1988   appData.searchDepth = 0;\r
1989   appData.showCoords = FALSE;\r
1990   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1991   appData.autoCallFlag = FALSE;\r
1992   appData.flipView = FALSE;\r
1993   appData.autoFlipView = TRUE;\r
1994   appData.cmailGameName = "";\r
1995   appData.alwaysPromoteToQueen = FALSE;\r
1996   appData.oldSaveStyle = FALSE;\r
1997   appData.quietPlay = FALSE;\r
1998   appData.showThinking = FALSE;\r
1999   appData.ponderNextMove = TRUE;\r
2000   appData.periodicUpdates = TRUE;\r
2001   appData.popupExitMessage = TRUE;\r
2002   appData.popupMoveErrors = FALSE;\r
2003   appData.autoObserve = FALSE;\r
2004   appData.autoComment = FALSE;\r
2005   appData.animate = TRUE;\r
2006   appData.animSpeed = 10;\r
2007   appData.animateDragging = TRUE;\r
2008   appData.highlightLastMove = TRUE;\r
2009   appData.getMoveList = TRUE;\r
2010   appData.testLegality = TRUE;\r
2011   appData.premove = TRUE;\r
2012   appData.premoveWhite = FALSE;\r
2013   appData.premoveWhiteText = "";\r
2014   appData.premoveBlack = FALSE;\r
2015   appData.premoveBlackText = "";\r
2016   appData.icsAlarm = TRUE;\r
2017   appData.icsAlarmTime = 5000;\r
2018   appData.autoRaiseBoard = TRUE;\r
2019   appData.localLineEditing = TRUE;\r
2020   appData.colorize = TRUE;\r
2021   appData.reuseFirst = TRUE;\r
2022   appData.reuseSecond = TRUE;\r
2023   appData.blindfold = FALSE;\r
2024   appData.icsEngineAnalyze = FALSE;\r
2025   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2026   dcb.DCBlength = sizeof(DCB);\r
2027   dcb.BaudRate = 9600;\r
2028   dcb.fBinary = TRUE;\r
2029   dcb.fParity = FALSE;\r
2030   dcb.fOutxCtsFlow = FALSE;\r
2031   dcb.fOutxDsrFlow = FALSE;\r
2032   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2033   dcb.fDsrSensitivity = FALSE;\r
2034   dcb.fTXContinueOnXoff = TRUE;\r
2035   dcb.fOutX = FALSE;\r
2036   dcb.fInX = FALSE;\r
2037   dcb.fNull = FALSE;\r
2038   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2039   dcb.fAbortOnError = FALSE;\r
2040   dcb.ByteSize = 7;\r
2041   dcb.Parity = SPACEPARITY;\r
2042   dcb.StopBits = ONESTOPBIT;\r
2043   settingsFileName = SETTINGS_FILE;\r
2044   saveSettingsOnExit = TRUE;\r
2045   boardX = CW_USEDEFAULT;\r
2046   boardY = CW_USEDEFAULT;\r
2047   analysisX = CW_USEDEFAULT; \r
2048   analysisY = CW_USEDEFAULT; \r
2049   analysisW = CW_USEDEFAULT;\r
2050   analysisH = CW_USEDEFAULT;\r
2051   commentX = CW_USEDEFAULT; \r
2052   commentY = CW_USEDEFAULT; \r
2053   commentW = CW_USEDEFAULT;\r
2054   commentH = CW_USEDEFAULT;\r
2055   editTagsX = CW_USEDEFAULT; \r
2056   editTagsY = CW_USEDEFAULT; \r
2057   editTagsW = CW_USEDEFAULT;\r
2058   editTagsH = CW_USEDEFAULT;\r
2059   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2060   icsNames = ICS_NAMES;\r
2061   firstChessProgramNames = FCP_NAMES;\r
2062   secondChessProgramNames = SCP_NAMES;\r
2063   appData.initialMode = "";\r
2064   appData.variant = "normal";\r
2065   appData.firstProtocolVersion = PROTOVER;\r
2066   appData.secondProtocolVersion = PROTOVER;\r
2067   appData.showButtonBar = TRUE;\r
2068 \r
2069    /* [AS] New properties (see comments in header file) */\r
2070   appData.firstScoreIsAbsolute = FALSE;\r
2071   appData.secondScoreIsAbsolute = FALSE;\r
2072   appData.saveExtendedInfoInPGN = FALSE;\r
2073   appData.hideThinkingFromHuman = FALSE;\r
2074   appData.liteBackTextureFile = "";\r
2075   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2076   appData.darkBackTextureFile = "";\r
2077   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2078   appData.renderPiecesWithFont = "";\r
2079   appData.fontToPieceTable = "";\r
2080   appData.fontBackColorWhite = 0;\r
2081   appData.fontForeColorWhite = 0;\r
2082   appData.fontBackColorBlack = 0;\r
2083   appData.fontForeColorBlack = 0;\r
2084   appData.fontPieceSize = 80;\r
2085   appData.overrideLineGap = 1;\r
2086   appData.adjudicateLossThreshold = 0;\r
2087   appData.delayBeforeQuit = 0;\r
2088   appData.delayAfterQuit = 0;\r
2089   appData.nameOfDebugFile = "winboard.debug";\r
2090   appData.pgnEventHeader = "Computer Chess Game";\r
2091   appData.defaultFrcPosition = -1;\r
2092   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2093   appData.saveOutOfBookInfo = TRUE;\r
2094   appData.showEvalInMoveHistory = TRUE;\r
2095   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2096   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2097   appData.highlightMoveWithArrow = FALSE;\r
2098   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2099   appData.useStickyWindows = TRUE;\r
2100   appData.adjudicateDrawMoves = 0;\r
2101   appData.autoDisplayComment = TRUE;\r
2102   appData.autoDisplayTags = TRUE;\r
2103   appData.firstIsUCI = FALSE;\r
2104   appData.secondIsUCI = FALSE;\r
2105   appData.firstHasOwnBookUCI = TRUE;\r
2106   appData.secondHasOwnBookUCI = TRUE;\r
2107   appData.polyglotDir = "";\r
2108   appData.usePolyglotBook = FALSE;\r
2109   appData.polyglotBook = "";\r
2110   appData.defaultHashSize = 64;\r
2111   appData.defaultCacheSizeEGTB = 4;\r
2112   appData.defaultPathEGTB = "c:\\egtb";\r
2113   appData.firstOptions = "";\r
2114   appData.secondOptions = "";\r
2115 \r
2116   InitWindowPlacement( &wpGameList );\r
2117   InitWindowPlacement( &wpMoveHistory );\r
2118   InitWindowPlacement( &wpEvalGraph );\r
2119   InitWindowPlacement( &wpEngineOutput );\r
2120   InitWindowPlacement( &wpConsole );\r
2121 \r
2122   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2123   appData.NrFiles      = -1;\r
2124   appData.NrRanks      = -1;\r
2125   appData.holdingsSize = -1;\r
2126   appData.testClaims   = FALSE;\r
2127   appData.checkMates   = FALSE;\r
2128   appData.materialDraws= FALSE;\r
2129   appData.trivialDraws = FALSE;\r
2130   appData.ruleMoves    = 51;\r
2131   appData.drawRepeats  = 6;\r
2132   appData.matchPause   = 10000;\r
2133   appData.alphaRank    = FALSE;\r
2134   appData.allWhite     = FALSE;\r
2135   appData.upsideDown   = FALSE;\r
2136   appData.serverPause  = 15;\r
2137   appData.serverMovesName   = NULL;\r
2138   appData.suppressLoadMoves = FALSE;\r
2139   appData.firstTimeOdds  = 1;\r
2140   appData.secondTimeOdds = 1;\r
2141   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2142   appData.secondAccumulateTC = 1;\r
2143   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2144   appData.secondNPS = -1;\r
2145   appData.engineComments = 1;\r
2146   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2147   appData.egtFormats = "";\r
2148 \r
2149 #ifdef ZIPPY\r
2150   appData.zippyTalk = ZIPPY_TALK;\r
2151   appData.zippyPlay = ZIPPY_PLAY;\r
2152   appData.zippyLines = ZIPPY_LINES;\r
2153   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2154   appData.zippyPassword = ZIPPY_PASSWORD;\r
2155   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2156   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2157   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2158   appData.zippyUseI = ZIPPY_USE_I;\r
2159   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2160   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2161   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2162   appData.zippyGameStart = ZIPPY_GAME_START;\r
2163   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2164   appData.zippyAbort = ZIPPY_ABORT;\r
2165   appData.zippyVariants = ZIPPY_VARIANTS;\r
2166   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2167   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2168 #endif\r
2169 \r
2170   /* Point font array elements to structures and\r
2171      parse default font names */\r
2172   for (i=0; i<NUM_FONTS; i++) {\r
2173     for (j=0; j<NUM_SIZES; j++) {\r
2174       font[j][i] = &fontRec[j][i];\r
2175       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2176     }\r
2177   }\r
2178   \r
2179   /* Parse default settings file if any */\r
2180   if (ParseSettingsFile(settingsFileName, buf)) {\r
2181     settingsFileName = strdup(buf);\r
2182   }\r
2183 \r
2184   /* Parse command line */\r
2185   ParseArgs(StringGet, &lpCmdLine);\r
2186 \r
2187   /* [HGM] make sure board size is acceptable */\r
2188   if(appData.NrFiles > BOARD_SIZE ||\r
2189      appData.NrRanks > BOARD_SIZE   )\r
2190       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2191 \r
2192   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2193    * with options from the command line, we now make an even higher priority\r
2194    * overrule by WB options attached to the engine command line. This so that\r
2195    * tournament managers can use WB options (such as /timeOdds) that follow\r
2196    * the engines.\r
2197    */\r
2198   if(appData.firstChessProgram != NULL) {\r
2199       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2200       static char *f = "first";\r
2201       char buf[MSG_SIZ], *q = buf;\r
2202       if(p != NULL) { // engine command line contains WinBoard options\r
2203           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2204           ParseArgs(StringGet, &q);\r
2205           p[-1] = 0; // cut them offengine command line\r
2206       }\r
2207   }\r
2208   // now do same for second chess program\r
2209   if(appData.secondChessProgram != NULL) {\r
2210       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2211       static char *s = "second";\r
2212       char buf[MSG_SIZ], *q = buf;\r
2213       if(p != NULL) { // engine command line contains WinBoard options\r
2214           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2215           ParseArgs(StringGet, &q);\r
2216           p[-1] = 0; // cut them offengine command line\r
2217       }\r
2218   }\r
2219 \r
2220 \r
2221   /* Propagate options that affect others */\r
2222   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2223   if (appData.icsActive || appData.noChessProgram) {\r
2224      chessProgram = FALSE;  /* not local chess program mode */\r
2225   }\r
2226 \r
2227   /* Open startup dialog if needed */\r
2228   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2229       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2230       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2231                         *appData.secondChessProgram == NULLCHAR))) {\r
2232     FARPROC lpProc;\r
2233     \r
2234     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2235     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2236     FreeProcInstance(lpProc);\r
2237   }\r
2238 \r
2239   /* Make sure save files land in the right (?) directory */\r
2240   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2241     appData.saveGameFile = strdup(buf);\r
2242   }\r
2243   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2244     appData.savePositionFile = strdup(buf);\r
2245   }\r
2246 \r
2247   /* Finish initialization for fonts and sounds */\r
2248   for (i=0; i<NUM_FONTS; i++) {\r
2249     for (j=0; j<NUM_SIZES; j++) {\r
2250       CreateFontInMF(font[j][i]);\r
2251     }\r
2252   }\r
2253   /* xboard, and older WinBoards, controlled the move sound with the\r
2254      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2255      always turn the option on (so that the backend will call us),\r
2256      then let the user turn the sound off by setting it to silence if\r
2257      desired.  To accommodate old winboard.ini files saved by old\r
2258      versions of WinBoard, we also turn off the sound if the option\r
2259      was initially set to false. */\r
2260   if (!appData.ringBellAfterMoves) {\r
2261     sounds[(int)SoundMove].name = strdup("");\r
2262     appData.ringBellAfterMoves = TRUE;\r
2263   }\r
2264   GetCurrentDirectory(MSG_SIZ, currDir);\r
2265   SetCurrentDirectory(installDir);\r
2266   LoadAllSounds();\r
2267   SetCurrentDirectory(currDir);\r
2268 \r
2269   p = icsTextMenuString;\r
2270   if (p[0] == '@') {\r
2271     FILE* f = fopen(p + 1, "r");\r
2272     if (f == NULL) {\r
2273       DisplayFatalError(p + 1, errno, 2);\r
2274       return;\r
2275     }\r
2276     i = fread(buf, 1, sizeof(buf)-1, f);\r
2277     fclose(f);\r
2278     buf[i] = NULLCHAR;\r
2279     p = buf;\r
2280   }\r
2281   ParseIcsTextMenu(strdup(p));\r
2282 }\r
2283 \r
2284 \r
2285 VOID\r
2286 InitMenuChecks()\r
2287 {\r
2288   HMENU hmenu = GetMenu(hwndMain);\r
2289 \r
2290   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2291                         MF_BYCOMMAND|((appData.icsActive &&\r
2292                                        *appData.icsCommPort != NULLCHAR) ?\r
2293                                       MF_ENABLED : MF_GRAYED));\r
2294   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2295                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2296                                      MF_CHECKED : MF_UNCHECKED));\r
2297 }\r
2298 \r
2299 \r
2300 VOID\r
2301 SaveSettings(char* name)\r
2302 {\r
2303   FILE *f;\r
2304   ArgDescriptor *ad;\r
2305   WINDOWPLACEMENT wp;\r
2306   char dir[MSG_SIZ];\r
2307 \r
2308   if (!hwndMain) return;\r
2309 \r
2310   GetCurrentDirectory(MSG_SIZ, dir);\r
2311   SetCurrentDirectory(installDir);\r
2312   f = fopen(name, "w");\r
2313   SetCurrentDirectory(dir);\r
2314   if (f == NULL) {\r
2315     DisplayError(name, errno);\r
2316     return;\r
2317   }\r
2318   fprintf(f, ";\n");\r
2319   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2320   fprintf(f, ";\n");\r
2321   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2322   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2323   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2324   fprintf(f, ";\n");\r
2325 \r
2326   wp.length = sizeof(WINDOWPLACEMENT);\r
2327   GetWindowPlacement(hwndMain, &wp);\r
2328   boardX = wp.rcNormalPosition.left;\r
2329   boardY = wp.rcNormalPosition.top;\r
2330 \r
2331   if (hwndConsole) {\r
2332     GetWindowPlacement(hwndConsole, &wp);\r
2333     wpConsole.x = wp.rcNormalPosition.left;\r
2334     wpConsole.y = wp.rcNormalPosition.top;\r
2335     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2336     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2337   }\r
2338 \r
2339   if (analysisDialog) {\r
2340     GetWindowPlacement(analysisDialog, &wp);\r
2341     analysisX = wp.rcNormalPosition.left;\r
2342     analysisY = wp.rcNormalPosition.top;\r
2343     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2344     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2345   }\r
2346 \r
2347   if (commentDialog) {\r
2348     GetWindowPlacement(commentDialog, &wp);\r
2349     commentX = wp.rcNormalPosition.left;\r
2350     commentY = wp.rcNormalPosition.top;\r
2351     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2352     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2353   }\r
2354 \r
2355   if (editTagsDialog) {\r
2356     GetWindowPlacement(editTagsDialog, &wp);\r
2357     editTagsX = wp.rcNormalPosition.left;\r
2358     editTagsY = wp.rcNormalPosition.top;\r
2359     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2360     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2361   }\r
2362 \r
2363   if (gameListDialog) {\r
2364     GetWindowPlacement(gameListDialog, &wp);\r
2365     wpGameList.x = wp.rcNormalPosition.left;\r
2366     wpGameList.y = wp.rcNormalPosition.top;\r
2367     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2368     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2369   }\r
2370 \r
2371   /* [AS] Move history */\r
2372   wpMoveHistory.visible = MoveHistoryIsUp();\r
2373   \r
2374   if( moveHistoryDialog ) {\r
2375     GetWindowPlacement(moveHistoryDialog, &wp);\r
2376     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2377     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2378     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2379     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2380   }\r
2381 \r
2382   /* [AS] Eval graph */\r
2383   wpEvalGraph.visible = EvalGraphIsUp();\r
2384 \r
2385   if( evalGraphDialog ) {\r
2386     GetWindowPlacement(evalGraphDialog, &wp);\r
2387     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2388     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2389     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2390     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2391   }\r
2392 \r
2393   /* [AS] Engine output */\r
2394   wpEngineOutput.visible = EngineOutputIsUp();\r
2395 \r
2396   if( engineOutputDialog ) {\r
2397     GetWindowPlacement(engineOutputDialog, &wp);\r
2398     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2399     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2400     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2401     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2402   }\r
2403 \r
2404   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2405     if (!ad->save) continue;\r
2406     switch (ad->argType) {\r
2407     case ArgString:\r
2408       {\r
2409         char *p = *(char **)ad->argLoc;\r
2410         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2411           /* Quote multiline values or \-containing values\r
2412              with { } if possible */\r
2413           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2414         } else {\r
2415           /* Else quote with " " */\r
2416           fprintf(f, "/%s=\"", ad->argName);\r
2417           while (*p) {\r
2418             if (*p == '\n') fprintf(f, "\n");\r
2419             else if (*p == '\r') fprintf(f, "\\r");\r
2420             else if (*p == '\t') fprintf(f, "\\t");\r
2421             else if (*p == '\b') fprintf(f, "\\b");\r
2422             else if (*p == '\f') fprintf(f, "\\f");\r
2423             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2424             else if (*p == '\"') fprintf(f, "\\\"");\r
2425             else if (*p == '\\') fprintf(f, "\\\\");\r
2426             else putc(*p, f);\r
2427             p++;\r
2428           }\r
2429           fprintf(f, "\"\n");\r
2430         }\r
2431       }\r
2432       break;\r
2433     case ArgInt:\r
2434     case ArgZ:\r
2435       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2436       break;\r
2437     case ArgX:\r
2438       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2439       break;\r
2440     case ArgY:\r
2441       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2442       break;\r
2443     case ArgFloat:\r
2444       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2445       break;\r
2446     case ArgBoolean:\r
2447       fprintf(f, "/%s=%s\n", ad->argName, \r
2448         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2449       break;\r
2450     case ArgTrue:\r
2451       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2452       break;\r
2453     case ArgFalse:\r
2454       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2455       break;\r
2456     case ArgColor:\r
2457       {\r
2458         COLORREF color = *(COLORREF *)ad->argLoc;\r
2459         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2460           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2461       }\r
2462       break;\r
2463     case ArgAttribs:\r
2464       {\r
2465         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2466         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2467           (ta->effects & CFE_BOLD) ? "b" : "",\r
2468           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2469           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2470           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2471           (ta->effects) ? " " : "",\r
2472           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2473       }\r
2474       break;\r
2475     case ArgFilename:\r
2476       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2477         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2478       } else {\r
2479         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2480       }\r
2481       break;\r
2482     case ArgBoardSize:\r
2483       fprintf(f, "/%s=%s\n", ad->argName,\r
2484               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2485       break;\r
2486     case ArgFont:\r
2487       {\r
2488         int bs;\r
2489         for (bs=0; bs<NUM_SIZES; bs++) {\r
2490           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2491           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2492           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2493             ad->argName, mfp->faceName, mfp->pointSize,\r
2494             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2495             mfp->bold ? "b" : "",\r
2496             mfp->italic ? "i" : "",\r
2497             mfp->underline ? "u" : "",\r
2498             mfp->strikeout ? "s" : "");\r
2499         }\r
2500       }\r
2501       break;\r
2502     case ArgCommSettings:\r
2503       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2504     case ArgNone:\r
2505     case ArgSettingsFilename: ;\r
2506     }\r
2507   }\r
2508   fclose(f);\r
2509 }\r
2510 \r
2511 \r
2512 \r
2513 /*---------------------------------------------------------------------------*\\r
2514  *\r
2515  * GDI board drawing routines\r
2516  *\r
2517 \*---------------------------------------------------------------------------*/\r
2518 \r
2519 /* [AS] Draw square using background texture */\r
2520 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2521 {\r
2522     XFORM   x;\r
2523 \r
2524     if( mode == 0 ) {\r
2525         return; /* Should never happen! */\r
2526     }\r
2527 \r
2528     SetGraphicsMode( dst, GM_ADVANCED );\r
2529 \r
2530     switch( mode ) {\r
2531     case 1:\r
2532         /* Identity */\r
2533         break;\r
2534     case 2:\r
2535         /* X reflection */\r
2536         x.eM11 = -1.0;\r
2537         x.eM12 = 0;\r
2538         x.eM21 = 0;\r
2539         x.eM22 = 1.0;\r
2540         x.eDx = (FLOAT) dw + dx - 1;\r
2541         x.eDy = 0;\r
2542         dx = 0;\r
2543         SetWorldTransform( dst, &x );\r
2544         break;\r
2545     case 3:\r
2546         /* Y reflection */\r
2547         x.eM11 = 1.0;\r
2548         x.eM12 = 0;\r
2549         x.eM21 = 0;\r
2550         x.eM22 = -1.0;\r
2551         x.eDx = 0;\r
2552         x.eDy = (FLOAT) dh + dy - 1;\r
2553         dy = 0;\r
2554         SetWorldTransform( dst, &x );\r
2555         break;\r
2556     case 4:\r
2557         /* X/Y flip */\r
2558         x.eM11 = 0;\r
2559         x.eM12 = 1.0;\r
2560         x.eM21 = 1.0;\r
2561         x.eM22 = 0;\r
2562         x.eDx = (FLOAT) dx;\r
2563         x.eDy = (FLOAT) dy;\r
2564         dx = 0;\r
2565         dy = 0;\r
2566         SetWorldTransform( dst, &x );\r
2567         break;\r
2568     }\r
2569 \r
2570     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2571 \r
2572     x.eM11 = 1.0;\r
2573     x.eM12 = 0;\r
2574     x.eM21 = 0;\r
2575     x.eM22 = 1.0;\r
2576     x.eDx = 0;\r
2577     x.eDy = 0;\r
2578     SetWorldTransform( dst, &x );\r
2579 \r
2580     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2581 }\r
2582 \r
2583 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2584 enum {\r
2585     PM_WP = (int) WhitePawn, \r
2586     PM_WN = (int) WhiteKnight, \r
2587     PM_WB = (int) WhiteBishop, \r
2588     PM_WR = (int) WhiteRook, \r
2589     PM_WQ = (int) WhiteQueen, \r
2590     PM_WF = (int) WhiteFerz, \r
2591     PM_WW = (int) WhiteWazir, \r
2592     PM_WE = (int) WhiteAlfil, \r
2593     PM_WM = (int) WhiteMan, \r
2594     PM_WO = (int) WhiteCannon, \r
2595     PM_WU = (int) WhiteUnicorn, \r
2596     PM_WH = (int) WhiteNightrider, \r
2597     PM_WA = (int) WhiteAngel, \r
2598     PM_WC = (int) WhiteMarshall, \r
2599     PM_WAB = (int) WhiteCardinal, \r
2600     PM_WD = (int) WhiteDragon, \r
2601     PM_WL = (int) WhiteLance, \r
2602     PM_WS = (int) WhiteCobra, \r
2603     PM_WV = (int) WhiteFalcon, \r
2604     PM_WSG = (int) WhiteSilver, \r
2605     PM_WG = (int) WhiteGrasshopper, \r
2606     PM_WK = (int) WhiteKing,\r
2607     PM_BP = (int) BlackPawn, \r
2608     PM_BN = (int) BlackKnight, \r
2609     PM_BB = (int) BlackBishop, \r
2610     PM_BR = (int) BlackRook, \r
2611     PM_BQ = (int) BlackQueen, \r
2612     PM_BF = (int) BlackFerz, \r
2613     PM_BW = (int) BlackWazir, \r
2614     PM_BE = (int) BlackAlfil, \r
2615     PM_BM = (int) BlackMan,\r
2616     PM_BO = (int) BlackCannon, \r
2617     PM_BU = (int) BlackUnicorn, \r
2618     PM_BH = (int) BlackNightrider, \r
2619     PM_BA = (int) BlackAngel, \r
2620     PM_BC = (int) BlackMarshall, \r
2621     PM_BG = (int) BlackGrasshopper, \r
2622     PM_BAB = (int) BlackCardinal,\r
2623     PM_BD = (int) BlackDragon,\r
2624     PM_BL = (int) BlackLance,\r
2625     PM_BS = (int) BlackCobra,\r
2626     PM_BV = (int) BlackFalcon,\r
2627     PM_BSG = (int) BlackSilver,\r
2628     PM_BK = (int) BlackKing\r
2629 };\r
2630 \r
2631 static HFONT hPieceFont = NULL;\r
2632 static HBITMAP hPieceMask[(int) EmptySquare];\r
2633 static HBITMAP hPieceFace[(int) EmptySquare];\r
2634 static int fontBitmapSquareSize = 0;\r
2635 static char pieceToFontChar[(int) EmptySquare] =\r
2636                               { 'p', 'n', 'b', 'r', 'q', \r
2637                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2638                       'k', 'o', 'm', 'v', 't', 'w', \r
2639                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2640                                                               'l' };\r
2641 \r
2642 extern BOOL SetCharTable( char *table, const char * map );\r
2643 /* [HGM] moved to backend.c */\r
2644 \r
2645 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2646 {\r
2647     HBRUSH hbrush;\r
2648     BYTE r1 = GetRValue( color );\r
2649     BYTE g1 = GetGValue( color );\r
2650     BYTE b1 = GetBValue( color );\r
2651     BYTE r2 = r1 / 2;\r
2652     BYTE g2 = g1 / 2;\r
2653     BYTE b2 = b1 / 2;\r
2654     RECT rc;\r
2655 \r
2656     /* Create a uniform background first */\r
2657     hbrush = CreateSolidBrush( color );\r
2658     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2659     FillRect( hdc, &rc, hbrush );\r
2660     DeleteObject( hbrush );\r
2661     \r
2662     if( mode == 1 ) {\r
2663         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2664         int steps = squareSize / 2;\r
2665         int i;\r
2666 \r
2667         for( i=0; i<steps; i++ ) {\r
2668             BYTE r = r1 - (r1-r2) * i / steps;\r
2669             BYTE g = g1 - (g1-g2) * i / steps;\r
2670             BYTE b = b1 - (b1-b2) * i / steps;\r
2671 \r
2672             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2673             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2674             FillRect( hdc, &rc, hbrush );\r
2675             DeleteObject(hbrush);\r
2676         }\r
2677     }\r
2678     else if( mode == 2 ) {\r
2679         /* Diagonal gradient, good more or less for every piece */\r
2680         POINT triangle[3];\r
2681         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2682         HBRUSH hbrush_old;\r
2683         int steps = squareSize;\r
2684         int i;\r
2685 \r
2686         triangle[0].x = squareSize - steps;\r
2687         triangle[0].y = squareSize;\r
2688         triangle[1].x = squareSize;\r
2689         triangle[1].y = squareSize;\r
2690         triangle[2].x = squareSize;\r
2691         triangle[2].y = squareSize - steps;\r
2692 \r
2693         for( i=0; i<steps; i++ ) {\r
2694             BYTE r = r1 - (r1-r2) * i / steps;\r
2695             BYTE g = g1 - (g1-g2) * i / steps;\r
2696             BYTE b = b1 - (b1-b2) * i / steps;\r
2697 \r
2698             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2699             hbrush_old = SelectObject( hdc, hbrush );\r
2700             Polygon( hdc, triangle, 3 );\r
2701             SelectObject( hdc, hbrush_old );\r
2702             DeleteObject(hbrush);\r
2703             triangle[0].x++;\r
2704             triangle[2].y++;\r
2705         }\r
2706 \r
2707         SelectObject( hdc, hpen );\r
2708     }\r
2709 }\r
2710 \r
2711 /*\r
2712     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2713     seems to work ok. The main problem here is to find the "inside" of a chess\r
2714     piece: follow the steps as explained below.\r
2715 */\r
2716 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2717 {\r
2718     HBITMAP hbm;\r
2719     HBITMAP hbm_old;\r
2720     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2721     RECT rc;\r
2722     SIZE sz;\r
2723     POINT pt;\r
2724     int backColor = whitePieceColor; \r
2725     int foreColor = blackPieceColor;\r
2726     \r
2727     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2728         backColor = appData.fontBackColorWhite;\r
2729         foreColor = appData.fontForeColorWhite;\r
2730     }\r
2731     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2732         backColor = appData.fontBackColorBlack;\r
2733         foreColor = appData.fontForeColorBlack;\r
2734     }\r
2735 \r
2736     /* Mask */\r
2737     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2738 \r
2739     hbm_old = SelectObject( hdc, hbm );\r
2740 \r
2741     rc.left = 0;\r
2742     rc.top = 0;\r
2743     rc.right = squareSize;\r
2744     rc.bottom = squareSize;\r
2745 \r
2746     /* Step 1: background is now black */\r
2747     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2748 \r
2749     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2750 \r
2751     pt.x = (squareSize - sz.cx) / 2;\r
2752     pt.y = (squareSize - sz.cy) / 2;\r
2753 \r
2754     SetBkMode( hdc, TRANSPARENT );\r
2755     SetTextColor( hdc, chroma );\r
2756     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2757     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2758 \r
2759     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2760     /* Step 3: the area outside the piece is filled with white */\r
2761 //    FloodFill( hdc, 0, 0, chroma );\r
2762     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2763     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2764     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2765     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2766     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2767     /* \r
2768         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2769         but if the start point is not inside the piece we're lost!\r
2770         There should be a better way to do this... if we could create a region or path\r
2771         from the fill operation we would be fine for example.\r
2772     */\r
2773 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2774     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2775 \r
2776     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2777         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2778         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2779 \r
2780         SelectObject( dc2, bm2 );\r
2781         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2782         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2783         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2784         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2785         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2786 \r
2787         DeleteDC( dc2 );\r
2788         DeleteObject( bm2 );\r
2789     }\r
2790 \r
2791     SetTextColor( hdc, 0 );\r
2792     /* \r
2793         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2794         draw the piece again in black for safety.\r
2795     */\r
2796     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2797 \r
2798     SelectObject( hdc, hbm_old );\r
2799 \r
2800     if( hPieceMask[index] != NULL ) {\r
2801         DeleteObject( hPieceMask[index] );\r
2802     }\r
2803 \r
2804     hPieceMask[index] = hbm;\r
2805 \r
2806     /* Face */\r
2807     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2808 \r
2809     SelectObject( hdc, hbm );\r
2810 \r
2811     {\r
2812         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2813         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2814         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2815 \r
2816         SelectObject( dc1, hPieceMask[index] );\r
2817         SelectObject( dc2, bm2 );\r
2818         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2819         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2820         \r
2821         /* \r
2822             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2823             the piece background and deletes (makes transparent) the rest.\r
2824             Thanks to that mask, we are free to paint the background with the greates\r
2825             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2826             We use this, to make gradients and give the pieces a "roundish" look.\r
2827         */\r
2828         SetPieceBackground( hdc, backColor, 2 );\r
2829         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2830 \r
2831         DeleteDC( dc2 );\r
2832         DeleteDC( dc1 );\r
2833         DeleteObject( bm2 );\r
2834     }\r
2835 \r
2836     SetTextColor( hdc, foreColor );\r
2837     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2838 \r
2839     SelectObject( hdc, hbm_old );\r
2840 \r
2841     if( hPieceFace[index] != NULL ) {\r
2842         DeleteObject( hPieceFace[index] );\r
2843     }\r
2844 \r
2845     hPieceFace[index] = hbm;\r
2846 }\r
2847 \r
2848 static int TranslatePieceToFontPiece( int piece )\r
2849 {\r
2850     switch( piece ) {\r
2851     case BlackPawn:\r
2852         return PM_BP;\r
2853     case BlackKnight:\r
2854         return PM_BN;\r
2855     case BlackBishop:\r
2856         return PM_BB;\r
2857     case BlackRook:\r
2858         return PM_BR;\r
2859     case BlackQueen:\r
2860         return PM_BQ;\r
2861     case BlackKing:\r
2862         return PM_BK;\r
2863     case WhitePawn:\r
2864         return PM_WP;\r
2865     case WhiteKnight:\r
2866         return PM_WN;\r
2867     case WhiteBishop:\r
2868         return PM_WB;\r
2869     case WhiteRook:\r
2870         return PM_WR;\r
2871     case WhiteQueen:\r
2872         return PM_WQ;\r
2873     case WhiteKing:\r
2874         return PM_WK;\r
2875 \r
2876     case BlackAngel:\r
2877         return PM_BA;\r
2878     case BlackMarshall:\r
2879         return PM_BC;\r
2880     case BlackFerz:\r
2881         return PM_BF;\r
2882     case BlackNightrider:\r
2883         return PM_BH;\r
2884     case BlackAlfil:\r
2885         return PM_BE;\r
2886     case BlackWazir:\r
2887         return PM_BW;\r
2888     case BlackUnicorn:\r
2889         return PM_BU;\r
2890     case BlackCannon:\r
2891         return PM_BO;\r
2892     case BlackGrasshopper:\r
2893         return PM_BG;\r
2894     case BlackMan:\r
2895         return PM_BM;\r
2896     case BlackSilver:\r
2897         return PM_BSG;\r
2898     case BlackLance:\r
2899         return PM_BL;\r
2900     case BlackFalcon:\r
2901         return PM_BV;\r
2902     case BlackCobra:\r
2903         return PM_BS;\r
2904     case BlackCardinal:\r
2905         return PM_BAB;\r
2906     case BlackDragon:\r
2907         return PM_BD;\r
2908 \r
2909     case WhiteAngel:\r
2910         return PM_WA;\r
2911     case WhiteMarshall:\r
2912         return PM_WC;\r
2913     case WhiteFerz:\r
2914         return PM_WF;\r
2915     case WhiteNightrider:\r
2916         return PM_WH;\r
2917     case WhiteAlfil:\r
2918         return PM_WE;\r
2919     case WhiteWazir:\r
2920         return PM_WW;\r
2921     case WhiteUnicorn:\r
2922         return PM_WU;\r
2923     case WhiteCannon:\r
2924         return PM_WO;\r
2925     case WhiteGrasshopper:\r
2926         return PM_WG;\r
2927     case WhiteMan:\r
2928         return PM_WM;\r
2929     case WhiteSilver:\r
2930         return PM_WSG;\r
2931     case WhiteLance:\r
2932         return PM_WL;\r
2933     case WhiteFalcon:\r
2934         return PM_WV;\r
2935     case WhiteCobra:\r
2936         return PM_WS;\r
2937     case WhiteCardinal:\r
2938         return PM_WAB;\r
2939     case WhiteDragon:\r
2940         return PM_WD;\r
2941     }\r
2942 \r
2943     return 0;\r
2944 }\r
2945 \r
2946 void CreatePiecesFromFont()\r
2947 {\r
2948     LOGFONT lf;\r
2949     HDC hdc_window = NULL;\r
2950     HDC hdc = NULL;\r
2951     HFONT hfont_old;\r
2952     int fontHeight;\r
2953     int i;\r
2954 \r
2955     if( fontBitmapSquareSize < 0 ) {\r
2956         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2957         return;\r
2958     }\r
2959 \r
2960     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2961         fontBitmapSquareSize = -1;\r
2962         return;\r
2963     }\r
2964 \r
2965     if( fontBitmapSquareSize != squareSize ) {\r
2966         hdc_window = GetDC( hwndMain );\r
2967         hdc = CreateCompatibleDC( hdc_window );\r
2968 \r
2969         if( hPieceFont != NULL ) {\r
2970             DeleteObject( hPieceFont );\r
2971         }\r
2972         else {\r
2973             for( i=0; i<=(int)BlackKing; i++ ) {\r
2974                 hPieceMask[i] = NULL;\r
2975                 hPieceFace[i] = NULL;\r
2976             }\r
2977         }\r
2978 \r
2979         fontHeight = 75;\r
2980 \r
2981         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2982             fontHeight = appData.fontPieceSize;\r
2983         }\r
2984 \r
2985         fontHeight = (fontHeight * squareSize) / 100;\r
2986 \r
2987         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2988         lf.lfWidth = 0;\r
2989         lf.lfEscapement = 0;\r
2990         lf.lfOrientation = 0;\r
2991         lf.lfWeight = FW_NORMAL;\r
2992         lf.lfItalic = 0;\r
2993         lf.lfUnderline = 0;\r
2994         lf.lfStrikeOut = 0;\r
2995         lf.lfCharSet = DEFAULT_CHARSET;\r
2996         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2997         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2998         lf.lfQuality = PROOF_QUALITY;\r
2999         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
3000         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
3001         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
3002 \r
3003         hPieceFont = CreateFontIndirect( &lf );\r
3004 \r
3005         if( hPieceFont == NULL ) {\r
3006             fontBitmapSquareSize = -2;\r
3007         }\r
3008         else {\r
3009             /* Setup font-to-piece character table */\r
3010             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
3011                 /* No (or wrong) global settings, try to detect the font */\r
3012                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
3013                     /* Alpha */\r
3014                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
3015                 }\r
3016                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3017                     /* DiagramTT* family */\r
3018                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3019                 }\r
3020                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3021                     /* Fairy symbols */\r
3022                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3023                 }\r
3024                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3025                     /* Good Companion (Some characters get warped as literal :-( */\r
3026                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3027                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3028                     SetCharTable(pieceToFontChar, s);\r
3029                 }\r
3030                 else {\r
3031                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3032                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3033                 }\r
3034             }\r
3035 \r
3036             /* Create bitmaps */\r
3037             hfont_old = SelectObject( hdc, hPieceFont );\r
3038 #if 0\r
3039             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
3040             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
3041             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
3042             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
3043             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
3044             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
3045             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
3046             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
3047             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
3048             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
3049             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
3050             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
3051 \r
3052             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
3053             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
3054             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
3055             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
3056             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
3057             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
3058             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
3059             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
3060             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
3061             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
3062             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
3063             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
3064             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
3065             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
3066             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
3067             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
3068             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
3069             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
3070             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
3071             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
3072             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
3073             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
3074             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
3075             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
3076             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
3077             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
3078             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
3079             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
3080             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
3081             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
3082             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
3083             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
3084 #else\r
3085             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3086                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3087                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3088 #endif\r
3089             SelectObject( hdc, hfont_old );\r
3090 \r
3091             fontBitmapSquareSize = squareSize;\r
3092         }\r
3093     }\r
3094 \r
3095     if( hdc != NULL ) {\r
3096         DeleteDC( hdc );\r
3097     }\r
3098 \r
3099     if( hdc_window != NULL ) {\r
3100         ReleaseDC( hwndMain, hdc_window );\r
3101     }\r
3102 }\r
3103 \r
3104 HBITMAP\r
3105 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3106 {\r
3107   char name[128];\r
3108 \r
3109   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3110   if (gameInfo.event &&\r
3111       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3112       strcmp(name, "k80s") == 0) {\r
3113     strcpy(name, "tim");\r
3114   }\r
3115   return LoadBitmap(hinst, name);\r
3116 }\r
3117 \r
3118 \r
3119 /* Insert a color into the program's logical palette\r
3120    structure.  This code assumes the given color is\r
3121    the result of the RGB or PALETTERGB macro, and it\r
3122    knows how those macros work (which is documented).\r
3123 */\r
3124 VOID\r
3125 InsertInPalette(COLORREF color)\r
3126 {\r
3127   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3128 \r
3129   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3130     DisplayFatalError("Too many colors", 0, 1);\r
3131     pLogPal->palNumEntries--;\r
3132     return;\r
3133   }\r
3134 \r
3135   pe->peFlags = (char) 0;\r
3136   pe->peRed = (char) (0xFF & color);\r
3137   pe->peGreen = (char) (0xFF & (color >> 8));\r
3138   pe->peBlue = (char) (0xFF & (color >> 16));\r
3139   return;\r
3140 }\r
3141 \r
3142 \r
3143 VOID\r
3144 InitDrawingColors()\r
3145 {\r
3146   if (pLogPal == NULL) {\r
3147     /* Allocate enough memory for a logical palette with\r
3148      * PALETTESIZE entries and set the size and version fields\r
3149      * of the logical palette structure.\r
3150      */\r
3151     pLogPal = (NPLOGPALETTE)\r
3152       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3153                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3154     pLogPal->palVersion    = 0x300;\r
3155   }\r
3156   pLogPal->palNumEntries = 0;\r
3157 \r
3158   InsertInPalette(lightSquareColor);\r
3159   InsertInPalette(darkSquareColor);\r
3160   InsertInPalette(whitePieceColor);\r
3161   InsertInPalette(blackPieceColor);\r
3162   InsertInPalette(highlightSquareColor);\r
3163   InsertInPalette(premoveHighlightColor);\r
3164 \r
3165   /*  create a logical color palette according the information\r
3166    *  in the LOGPALETTE structure.\r
3167    */\r
3168   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3169 \r
3170   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3171   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3172   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3173   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3174   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3175   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3176   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3177   /* [AS] Force rendering of the font-based pieces */\r
3178   if( fontBitmapSquareSize > 0 ) {\r
3179     fontBitmapSquareSize = 0;\r
3180   }\r
3181 }\r
3182 \r
3183 \r
3184 int\r
3185 BoardWidth(int boardSize, int n)\r
3186 { /* [HGM] argument n added to allow different width and height */\r
3187   int lineGap = sizeInfo[boardSize].lineGap;\r
3188 \r
3189   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3190       lineGap = appData.overrideLineGap;\r
3191   }\r
3192 \r
3193   return (n + 1) * lineGap +\r
3194           n * sizeInfo[boardSize].squareSize;\r
3195 }\r
3196 \r
3197 /* Respond to board resize by dragging edge */\r
3198 VOID\r
3199 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3200 {\r
3201   BoardSize newSize = NUM_SIZES - 1;\r
3202   static int recurse = 0;\r
3203   if (IsIconic(hwndMain)) return;\r
3204   if (recurse > 0) return;\r
3205   recurse++;\r
3206   while (newSize > 0) {\r
3207         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3208         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3209            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3210     newSize--;\r
3211   } \r
3212   boardSize = newSize;\r
3213   InitDrawingSizes(boardSize, flags);\r
3214   recurse--;\r
3215 }\r
3216 \r
3217 \r
3218 \r
3219 VOID\r
3220 InitDrawingSizes(BoardSize boardSize, int flags)\r
3221 {\r
3222   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3223   ChessSquare piece;\r
3224   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3225   HDC hdc;\r
3226   SIZE clockSize, messageSize;\r
3227   HFONT oldFont;\r
3228   char buf[MSG_SIZ];\r
3229   char *str;\r
3230   HMENU hmenu = GetMenu(hwndMain);\r
3231   RECT crect, wrect, oldRect;\r
3232   int offby;\r
3233   LOGBRUSH logbrush;\r
3234 \r
3235   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3236   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3237 \r
3238   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3239   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3240 \r
3241   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3242   oldRect.top = boardY;\r
3243   oldRect.right = boardX + winWidth;\r
3244   oldRect.bottom = boardY + winHeight;\r
3245 \r
3246   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3247   smallLayout = sizeInfo[boardSize].smallLayout;\r
3248   squareSize = sizeInfo[boardSize].squareSize;\r
3249   lineGap = sizeInfo[boardSize].lineGap;\r
3250   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3251 \r
3252   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3253       lineGap = appData.overrideLineGap;\r
3254   }\r
3255 \r
3256   if (tinyLayout != oldTinyLayout) {\r
3257     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3258     if (tinyLayout) {\r
3259       style &= ~WS_SYSMENU;\r
3260       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3261                  "&Minimize\tCtrl+F4");\r
3262     } else {\r
3263       style |= WS_SYSMENU;\r
3264       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3265     }\r
3266     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3267 \r
3268     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3269       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3270         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3271     }\r
3272     DrawMenuBar(hwndMain);\r
3273   }\r
3274 \r
3275   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3276   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3277 \r
3278   /* Get text area sizes */\r
3279   hdc = GetDC(hwndMain);\r
3280   if (appData.clockMode) {\r
3281     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3282   } else {\r
3283     sprintf(buf, "White");\r
3284   }\r
3285   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3286   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3287   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3288   str = "We only care about the height here";\r
3289   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3290   SelectObject(hdc, oldFont);\r
3291   ReleaseDC(hwndMain, hdc);\r
3292 \r
3293   /* Compute where everything goes */\r
3294   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3295         /* [HGM] logo: if either logo is on, reserve space for it */\r
3296         logoHeight =  2*clockSize.cy;\r
3297         leftLogoRect.left   = OUTER_MARGIN;\r
3298         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3299         leftLogoRect.top    = OUTER_MARGIN;\r
3300         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3301 \r
3302         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3303         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3304         rightLogoRect.top    = OUTER_MARGIN;\r
3305         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3306 \r
3307 \r
3308     whiteRect.left = leftLogoRect.right;\r
3309     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3310     whiteRect.top = OUTER_MARGIN;\r
3311     whiteRect.bottom = whiteRect.top + logoHeight;\r
3312 \r
3313     blackRect.right = rightLogoRect.left;\r
3314     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3315     blackRect.top = whiteRect.top;\r
3316     blackRect.bottom = whiteRect.bottom;\r
3317   } else {\r
3318     whiteRect.left = OUTER_MARGIN;\r
3319     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3320     whiteRect.top = OUTER_MARGIN;\r
3321     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3322 \r
3323     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3324     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3325     blackRect.top = whiteRect.top;\r
3326     blackRect.bottom = whiteRect.bottom;\r
3327   }\r
3328 \r
3329   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3330   if (appData.showButtonBar) {\r
3331     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3332       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3333   } else {\r
3334     messageRect.right = OUTER_MARGIN + boardWidth;\r
3335   }\r
3336   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3337   messageRect.bottom = messageRect.top + messageSize.cy;\r
3338 \r
3339   boardRect.left = OUTER_MARGIN;\r
3340   boardRect.right = boardRect.left + boardWidth;\r
3341   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3342   boardRect.bottom = boardRect.top + boardHeight;\r
3343 \r
3344   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3345   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3346   oldBoardSize = boardSize;\r
3347   oldTinyLayout = tinyLayout;\r
3348   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3349   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3350     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3351   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3352   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3353   winHeight = winH; //       without disturbing window attachments\r
3354   GetWindowRect(hwndMain, &wrect);\r
3355   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3356                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3357 \r
3358   // [HGM] placement: let attached windows follow size change.\r
3359   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3360   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3361   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3362   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3363   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3364 \r
3365   /* compensate if menu bar wrapped */\r
3366   GetClientRect(hwndMain, &crect);\r
3367   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3368   winHeight += offby;\r
3369   switch (flags) {\r
3370   case WMSZ_TOPLEFT:\r
3371     SetWindowPos(hwndMain, NULL, \r
3372                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3373                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3374     break;\r
3375 \r
3376   case WMSZ_TOPRIGHT:\r
3377   case WMSZ_TOP:\r
3378     SetWindowPos(hwndMain, NULL, \r
3379                  wrect.left, wrect.bottom - winHeight, \r
3380                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3381     break;\r
3382 \r
3383   case WMSZ_BOTTOMLEFT:\r
3384   case WMSZ_LEFT:\r
3385     SetWindowPos(hwndMain, NULL, \r
3386                  wrect.right - winWidth, wrect.top, \r
3387                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3388     break;\r
3389 \r
3390   case WMSZ_BOTTOMRIGHT:\r
3391   case WMSZ_BOTTOM:\r
3392   case WMSZ_RIGHT:\r
3393   default:\r
3394     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3395                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3396     break;\r
3397   }\r
3398 \r
3399   hwndPause = NULL;\r
3400   for (i = 0; i < N_BUTTONS; i++) {\r
3401     if (buttonDesc[i].hwnd != NULL) {\r
3402       DestroyWindow(buttonDesc[i].hwnd);\r
3403       buttonDesc[i].hwnd = NULL;\r
3404     }\r
3405     if (appData.showButtonBar) {\r
3406       buttonDesc[i].hwnd =\r
3407         CreateWindow("BUTTON", buttonDesc[i].label,\r
3408                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3409                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3410                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3411                      (HMENU) buttonDesc[i].id,\r
3412                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3413       if (tinyLayout) {\r
3414         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3415                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3416                     MAKELPARAM(FALSE, 0));\r
3417       }\r
3418       if (buttonDesc[i].id == IDM_Pause)\r
3419         hwndPause = buttonDesc[i].hwnd;\r
3420       buttonDesc[i].wndproc = (WNDPROC)\r
3421         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3422     }\r
3423   }\r
3424   if (gridPen != NULL) DeleteObject(gridPen);\r
3425   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3426   if (premovePen != NULL) DeleteObject(premovePen);\r
3427   if (lineGap != 0) {\r
3428     logbrush.lbStyle = BS_SOLID;\r
3429     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3430     gridPen =\r
3431       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3432                    lineGap, &logbrush, 0, NULL);\r
3433     logbrush.lbColor = highlightSquareColor;\r
3434     highlightPen =\r
3435       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3436                    lineGap, &logbrush, 0, NULL);\r
3437 \r
3438     logbrush.lbColor = premoveHighlightColor; \r
3439     premovePen =\r
3440       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3441                    lineGap, &logbrush, 0, NULL);\r
3442 \r
3443     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3444     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3445       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3446       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3447         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3448       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3449         BOARD_WIDTH * (squareSize + lineGap);\r
3450       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3451     }\r
3452     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3453       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3454       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3455         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3456         lineGap / 2 + (i * (squareSize + lineGap));\r
3457       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3458         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3459       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3460     }\r
3461   }\r
3462 \r
3463   /* [HGM] Licensing requirement */\r
3464 #ifdef GOTHIC\r
3465   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3466 #endif\r
3467 #ifdef FALCON\r
3468   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3469 #endif\r
3470   GothicPopUp( "", VariantNormal);\r
3471 \r
3472 \r
3473 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3474 \r
3475   /* Load piece bitmaps for this board size */\r
3476   for (i=0; i<=2; i++) {\r
3477     for (piece = WhitePawn;\r
3478          (int) piece < (int) BlackPawn;\r
3479          piece = (ChessSquare) ((int) piece + 1)) {\r
3480       if (pieceBitmap[i][piece] != NULL)\r
3481         DeleteObject(pieceBitmap[i][piece]);\r
3482     }\r
3483   }\r
3484 \r
3485   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3486   // Orthodox Chess pieces\r
3487   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3488   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3489   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3490   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3491   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3492   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3493   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3494   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3495   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3496   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3497   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3498   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3499   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3500   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3501   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3502   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3503     // in Shogi, Hijack the unused Queen for Lance\r
3504     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3505     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3506     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3507   } else {\r
3508     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3509     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3510     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3511   }\r
3512 \r
3513   if(squareSize <= 72 && squareSize >= 33) { \r
3514     /* A & C are available in most sizes now */\r
3515     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3516       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3517       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3518       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3519       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3520       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3521       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3522       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3523       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3524       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3525       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3526       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3527       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3528     } else { // Smirf-like\r
3529       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3530       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3531       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3532     }\r
3533     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3534       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3535       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3536       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3537     } else { // WinBoard standard\r
3538       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3539       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3540       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3541     }\r
3542   }\r
3543 \r
3544 \r
3545   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3546     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3547     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3548     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3549     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3550     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3551     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3552     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3553     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3554     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3555     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3556     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3557     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3558     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3559     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3560     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3561     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3562     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3563     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3564     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3565     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3566     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3567     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3568     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3569     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3570     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3571     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3572     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3573     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3574     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3575     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3576 \r
3577     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3578       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3579       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3580       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3581       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3582       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3583       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3584       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3585       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3586       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3587       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3588       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3589       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3590     } else {\r
3591       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3592       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3593       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3594       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3595       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3596       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3597       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3598       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3599       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3600       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3601       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3602       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3603     }\r
3604 \r
3605   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3606     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3607     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3608     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3609     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3610     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3611     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3612     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3613     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3614     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3615     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3616     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3617     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3618     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3619     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3620   }\r
3621 \r
3622 \r
3623   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3624   /* special Shogi support in this size */\r
3625   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3626       for (piece = WhitePawn;\r
3627            (int) piece < (int) BlackPawn;\r
3628            piece = (ChessSquare) ((int) piece + 1)) {\r
3629         if (pieceBitmap[i][piece] != NULL)\r
3630           DeleteObject(pieceBitmap[i][piece]);\r
3631       }\r
3632     }\r
3633   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3634   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3635   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3636   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3637   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3638   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3639   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3640   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3641   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3642   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3643   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3644   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3645   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3646   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3647   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3648   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3649   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3650   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3651   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3652   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3653   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3654   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3655   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3656   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3657   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3658   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3659   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3660   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3661   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3662   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3663   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3664   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3665   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3666   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3667   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3668   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3669   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3670   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3671   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3672   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3673   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3674   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3675   minorSize = 0;\r
3676   }\r
3677 }\r
3678 \r
3679 HBITMAP\r
3680 PieceBitmap(ChessSquare p, int kind)\r
3681 {\r
3682   if ((int) p >= (int) BlackPawn)\r
3683     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3684 \r
3685   return pieceBitmap[kind][(int) p];\r
3686 }\r
3687 \r
3688 /***************************************************************/\r
3689 \r
3690 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3691 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3692 /*\r
3693 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3694 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3695 */\r
3696 \r
3697 VOID\r
3698 SquareToPos(int row, int column, int * x, int * y)\r
3699 {\r
3700   if (flipView) {\r
3701     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3702     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3703   } else {\r
3704     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3705     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3706   }\r
3707 }\r
3708 \r
3709 VOID\r
3710 DrawCoordsOnDC(HDC hdc)\r
3711 {\r
3712   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
3713   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
3714   char str[2] = { NULLCHAR, NULLCHAR };\r
3715   int oldMode, oldAlign, x, y, start, i;\r
3716   HFONT oldFont;\r
3717   HBRUSH oldBrush;\r
3718 \r
3719   if (!appData.showCoords)\r
3720     return;\r
3721 \r
3722   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3723 \r
3724   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3725   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3726   oldAlign = GetTextAlign(hdc);\r
3727   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3728 \r
3729   y = boardRect.top + lineGap;\r
3730   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3731 \r
3732   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3733   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3734     str[0] = files[start + i];\r
3735     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3736     y += squareSize + lineGap;\r
3737   }\r
3738 \r
3739   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3740 \r
3741   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3742   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3743     str[0] = ranks[start + i];\r
3744     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3745     x += squareSize + lineGap;\r
3746   }    \r
3747 \r
3748   SelectObject(hdc, oldBrush);\r
3749   SetBkMode(hdc, oldMode);\r
3750   SetTextAlign(hdc, oldAlign);\r
3751   SelectObject(hdc, oldFont);\r
3752 }\r
3753 \r
3754 VOID\r
3755 DrawGridOnDC(HDC hdc)\r
3756 {\r
3757   HPEN oldPen;\r
3758  \r
3759   if (lineGap != 0) {\r
3760     oldPen = SelectObject(hdc, gridPen);\r
3761     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3762     SelectObject(hdc, oldPen);\r
3763   }\r
3764 }\r
3765 \r
3766 #define HIGHLIGHT_PEN 0\r
3767 #define PREMOVE_PEN   1\r
3768 \r
3769 VOID\r
3770 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3771 {\r
3772   int x1, y1;\r
3773   HPEN oldPen, hPen;\r
3774   if (lineGap == 0) return;\r
3775   if (flipView) {\r
3776     x1 = boardRect.left +\r
3777       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3778     y1 = boardRect.top +\r
3779       lineGap/2 + y * (squareSize + lineGap);\r
3780   } else {\r
3781     x1 = boardRect.left +\r
3782       lineGap/2 + x * (squareSize + lineGap);\r
3783     y1 = boardRect.top +\r
3784       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3785   }\r
3786   hPen = pen ? premovePen : highlightPen;\r
3787   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3788   MoveToEx(hdc, x1, y1, NULL);\r
3789   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3790   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3791   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3792   LineTo(hdc, x1, y1);\r
3793   SelectObject(hdc, oldPen);\r
3794 }\r
3795 \r
3796 VOID\r
3797 DrawHighlightsOnDC(HDC hdc)\r
3798 {\r
3799   int i;\r
3800   for (i=0; i<2; i++) {\r
3801     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3802       DrawHighlightOnDC(hdc, TRUE,\r
3803                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3804                         HIGHLIGHT_PEN);\r
3805   }\r
3806   for (i=0; i<2; i++) {\r
3807     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3808         premoveHighlightInfo.sq[i].y >= 0) {\r
3809         DrawHighlightOnDC(hdc, TRUE,\r
3810                           premoveHighlightInfo.sq[i].x, \r
3811                           premoveHighlightInfo.sq[i].y,\r
3812                           PREMOVE_PEN);\r
3813     }\r
3814   }\r
3815 }\r
3816 \r
3817 /* Note: sqcolor is used only in monoMode */\r
3818 /* Note that this code is largely duplicated in woptions.c,\r
3819    function DrawSampleSquare, so that needs to be updated too */\r
3820 VOID\r
3821 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3822 {\r
3823   HBITMAP oldBitmap;\r
3824   HBRUSH oldBrush;\r
3825   int tmpSize;\r
3826 \r
3827   if (appData.blindfold) return;\r
3828 \r
3829   /* [AS] Use font-based pieces if needed */\r
3830   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3831     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3832     CreatePiecesFromFont();\r
3833 \r
3834     if( fontBitmapSquareSize == squareSize ) {\r
3835         int index = TranslatePieceToFontPiece(piece);\r
3836 \r
3837         SelectObject( tmphdc, hPieceMask[ index ] );\r
3838 \r
3839         BitBlt( hdc,\r
3840             x, y,\r
3841             squareSize, squareSize,\r
3842             tmphdc,\r
3843             0, 0,\r
3844             SRCAND );\r
3845 \r
3846         SelectObject( tmphdc, hPieceFace[ index ] );\r
3847 \r
3848         BitBlt( hdc,\r
3849             x, y,\r
3850             squareSize, squareSize,\r
3851             tmphdc,\r
3852             0, 0,\r
3853             SRCPAINT );\r
3854 \r
3855         return;\r
3856     }\r
3857   }\r
3858 \r
3859   if (appData.monoMode) {\r
3860     SelectObject(tmphdc, PieceBitmap(piece, \r
3861       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3862     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3863            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3864   } else {\r
3865     tmpSize = squareSize;\r
3866     if(minorSize &&\r
3867         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3868          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3869       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3870       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3871       x += (squareSize - minorSize)>>1;\r
3872       y += squareSize - minorSize - 2;\r
3873       tmpSize = minorSize;\r
3874     }\r
3875     if (color || appData.allWhite ) {\r
3876       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3877       if( color )\r
3878               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3879       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3880       if(appData.upsideDown && color==flipView)\r
3881         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3882       else\r
3883         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3884 #if 0\r
3885       /* Use black piece color for outline of white pieces */\r
3886       /* Not sure this looks really good (though xboard does it).\r
3887          Maybe better to have another selectable color, default black */\r
3888       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3889       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3890       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3891 #else\r
3892       /* Use black for outline of white pieces */\r
3893       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3894       if(appData.upsideDown && color==flipView)\r
3895         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3896       else\r
3897         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3898 #endif\r
3899     } else {\r
3900 #if 0\r
3901       /* Use white piece color for details of black pieces */\r
3902       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3903          WHITE_PIECE ones aren't always the right shape. */\r
3904       /* Not sure this looks really good (though xboard does it).\r
3905          Maybe better to have another selectable color, default medium gray? */\r
3906       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3907       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3908       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3909       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3910       SelectObject(hdc, blackPieceBrush);\r
3911       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3912 #else\r
3913       /* Use square color for details of black pieces */\r
3914       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3915       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3916       if(appData.upsideDown && !flipView)\r
3917         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3918       else\r
3919         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3920 #endif\r
3921     }\r
3922     SelectObject(hdc, oldBrush);\r
3923     SelectObject(tmphdc, oldBitmap);\r
3924   }\r
3925 }\r
3926 \r
3927 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3928 int GetBackTextureMode( int algo )\r
3929 {\r
3930     int result = BACK_TEXTURE_MODE_DISABLED;\r
3931 \r
3932     switch( algo ) \r
3933     {\r
3934         case BACK_TEXTURE_MODE_PLAIN:\r
3935             result = 1; /* Always use identity map */\r
3936             break;\r
3937         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3938             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3939             break;\r
3940     }\r
3941 \r
3942     return result;\r
3943 }\r
3944 \r
3945 /* \r
3946     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3947     to handle redraws cleanly (as random numbers would always be different).\r
3948 */\r
3949 VOID RebuildTextureSquareInfo()\r
3950 {\r
3951     BITMAP bi;\r
3952     int lite_w = 0;\r
3953     int lite_h = 0;\r
3954     int dark_w = 0;\r
3955     int dark_h = 0;\r
3956     int row;\r
3957     int col;\r
3958 \r
3959     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3960 \r
3961     if( liteBackTexture != NULL ) {\r
3962         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3963             lite_w = bi.bmWidth;\r
3964             lite_h = bi.bmHeight;\r
3965         }\r
3966     }\r
3967 \r
3968     if( darkBackTexture != NULL ) {\r
3969         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3970             dark_w = bi.bmWidth;\r
3971             dark_h = bi.bmHeight;\r
3972         }\r
3973     }\r
3974 \r
3975     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3976         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3977             if( (col + row) & 1 ) {\r
3978                 /* Lite square */\r
3979                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3980                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3981                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3982                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3983                 }\r
3984             }\r
3985             else {\r
3986                 /* Dark square */\r
3987                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3988                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3989                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3990                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3991                 }\r
3992             }\r
3993         }\r
3994     }\r
3995 }\r
3996 \r
3997 /* [AS] Arrow highlighting support */\r
3998 \r
3999 static int A_WIDTH = 5; /* Width of arrow body */\r
4000 \r
4001 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
4002 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
4003 \r
4004 static double Sqr( double x )\r
4005 {\r
4006     return x*x;\r
4007 }\r
4008 \r
4009 static int Round( double x )\r
4010 {\r
4011     return (int) (x + 0.5);\r
4012 }\r
4013 \r
4014 /* Draw an arrow between two points using current settings */\r
4015 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
4016 {\r
4017     POINT arrow[7];\r
4018     double dx, dy, j, k, x, y;\r
4019 \r
4020     if( d_x == s_x ) {\r
4021         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4022 \r
4023         arrow[0].x = s_x + A_WIDTH;\r
4024         arrow[0].y = s_y;\r
4025 \r
4026         arrow[1].x = s_x + A_WIDTH;\r
4027         arrow[1].y = d_y - h;\r
4028 \r
4029         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
4030         arrow[2].y = d_y - h;\r
4031 \r
4032         arrow[3].x = d_x;\r
4033         arrow[3].y = d_y;\r
4034 \r
4035         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
4036         arrow[4].y = d_y - h;\r
4037 \r
4038         arrow[5].x = s_x - A_WIDTH;\r
4039         arrow[5].y = d_y - h;\r
4040 \r
4041         arrow[6].x = s_x - A_WIDTH;\r
4042         arrow[6].y = s_y;\r
4043     }\r
4044     else if( d_y == s_y ) {\r
4045         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4046 \r
4047         arrow[0].x = s_x;\r
4048         arrow[0].y = s_y + A_WIDTH;\r
4049 \r
4050         arrow[1].x = d_x - w;\r
4051         arrow[1].y = s_y + A_WIDTH;\r
4052 \r
4053         arrow[2].x = d_x - w;\r
4054         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
4055 \r
4056         arrow[3].x = d_x;\r
4057         arrow[3].y = d_y;\r
4058 \r
4059         arrow[4].x = d_x - w;\r
4060         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
4061 \r
4062         arrow[5].x = d_x - w;\r
4063         arrow[5].y = s_y - A_WIDTH;\r
4064 \r
4065         arrow[6].x = s_x;\r
4066         arrow[6].y = s_y - A_WIDTH;\r
4067     }\r
4068     else {\r
4069         /* [AS] Needed a lot of paper for this! :-) */\r
4070         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4071         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4072   \r
4073         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4074 \r
4075         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4076 \r
4077         x = s_x;\r
4078         y = s_y;\r
4079 \r
4080         arrow[0].x = Round(x - j);\r
4081         arrow[0].y = Round(y + j*dx);\r
4082 \r
4083         arrow[1].x = Round(x + j);\r
4084         arrow[1].y = Round(y - j*dx);\r
4085 \r
4086         if( d_x > s_x ) {\r
4087             x = (double) d_x - k;\r
4088             y = (double) d_y - k*dy;\r
4089         }\r
4090         else {\r
4091             x = (double) d_x + k;\r
4092             y = (double) d_y + k*dy;\r
4093         }\r
4094 \r
4095         arrow[2].x = Round(x + j);\r
4096         arrow[2].y = Round(y - j*dx);\r
4097 \r
4098         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4099         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4100 \r
4101         arrow[4].x = d_x;\r
4102         arrow[4].y = d_y;\r
4103 \r
4104         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4105         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4106 \r
4107         arrow[6].x = Round(x - j);\r
4108         arrow[6].y = Round(y + j*dx);\r
4109     }\r
4110 \r
4111     Polygon( hdc, arrow, 7 );\r
4112 }\r
4113 \r
4114 /* [AS] Draw an arrow between two squares */\r
4115 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4116 {\r
4117     int s_x, s_y, d_x, d_y;\r
4118     HPEN hpen;\r
4119     HPEN holdpen;\r
4120     HBRUSH hbrush;\r
4121     HBRUSH holdbrush;\r
4122     LOGBRUSH stLB;\r
4123 \r
4124     if( s_col == d_col && s_row == d_row ) {\r
4125         return;\r
4126     }\r
4127 \r
4128     /* Get source and destination points */\r
4129     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4130     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4131 \r
4132     if( d_y > s_y ) {\r
4133         d_y += squareSize / 4;\r
4134     }\r
4135     else if( d_y < s_y ) {\r
4136         d_y += 3 * squareSize / 4;\r
4137     }\r
4138     else {\r
4139         d_y += squareSize / 2;\r
4140     }\r
4141 \r
4142     if( d_x > s_x ) {\r
4143         d_x += squareSize / 4;\r
4144     }\r
4145     else if( d_x < s_x ) {\r
4146         d_x += 3 * squareSize / 4;\r
4147     }\r
4148     else {\r
4149         d_x += squareSize / 2;\r
4150     }\r
4151 \r
4152     s_x += squareSize / 2;\r
4153     s_y += squareSize / 2;\r
4154 \r
4155     /* Adjust width */\r
4156     A_WIDTH = squareSize / 14;\r
4157 \r
4158     /* Draw */\r
4159     stLB.lbStyle = BS_SOLID;\r
4160     stLB.lbColor = appData.highlightArrowColor;\r
4161     stLB.lbHatch = 0;\r
4162 \r
4163     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4164     holdpen = SelectObject( hdc, hpen );\r
4165     hbrush = CreateBrushIndirect( &stLB );\r
4166     holdbrush = SelectObject( hdc, hbrush );\r
4167 \r
4168     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4169 \r
4170     SelectObject( hdc, holdpen );\r
4171     SelectObject( hdc, holdbrush );\r
4172     DeleteObject( hpen );\r
4173     DeleteObject( hbrush );\r
4174 }\r
4175 \r
4176 BOOL HasHighlightInfo()\r
4177 {\r
4178     BOOL result = FALSE;\r
4179 \r
4180     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4181         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4182     {\r
4183         result = TRUE;\r
4184     }\r
4185 \r
4186     return result;\r
4187 }\r
4188 \r
4189 BOOL IsDrawArrowEnabled()\r
4190 {\r
4191     BOOL result = FALSE;\r
4192 \r
4193     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4194         result = TRUE;\r
4195     }\r
4196 \r
4197     return result;\r
4198 }\r
4199 \r
4200 VOID DrawArrowHighlight( HDC hdc )\r
4201 {\r
4202     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4203         DrawArrowBetweenSquares( hdc,\r
4204             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4205             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4206     }\r
4207 }\r
4208 \r
4209 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4210 {\r
4211     HRGN result = NULL;\r
4212 \r
4213     if( HasHighlightInfo() ) {\r
4214         int x1, y1, x2, y2;\r
4215         int sx, sy, dx, dy;\r
4216 \r
4217         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4218         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4219 \r
4220         sx = MIN( x1, x2 );\r
4221         sy = MIN( y1, y2 );\r
4222         dx = MAX( x1, x2 ) + squareSize;\r
4223         dy = MAX( y1, y2 ) + squareSize;\r
4224 \r
4225         result = CreateRectRgn( sx, sy, dx, dy );\r
4226     }\r
4227 \r
4228     return result;\r
4229 }\r
4230 \r
4231 /*\r
4232     Warning: this function modifies the behavior of several other functions. \r
4233     \r
4234     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4235     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4236     repaint is scattered all over the place, which is not good for features such as\r
4237     "arrow highlighting" that require a full repaint of the board.\r
4238 \r
4239     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4240     user interaction, when speed is not so important) but especially to avoid errors\r
4241     in the displayed graphics.\r
4242 \r
4243     In such patched places, I always try refer to this function so there is a single\r
4244     place to maintain knowledge.\r
4245     \r
4246     To restore the original behavior, just return FALSE unconditionally.\r
4247 */\r
4248 BOOL IsFullRepaintPreferrable()\r
4249 {\r
4250     BOOL result = FALSE;\r
4251 \r
4252     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4253         /* Arrow may appear on the board */\r
4254         result = TRUE;\r
4255     }\r
4256 \r
4257     return result;\r
4258 }\r
4259 \r
4260 /* \r
4261     This function is called by DrawPosition to know whether a full repaint must\r
4262     be forced or not.\r
4263 \r
4264     Only DrawPosition may directly call this function, which makes use of \r
4265     some state information. Other function should call DrawPosition specifying \r
4266     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4267 */\r
4268 BOOL DrawPositionNeedsFullRepaint()\r
4269 {\r
4270     BOOL result = FALSE;\r
4271 \r
4272     /* \r
4273         Probably a slightly better policy would be to trigger a full repaint\r
4274         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4275         but animation is fast enough that it's difficult to notice.\r
4276     */\r
4277     if( animInfo.piece == EmptySquare ) {\r
4278         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4279             result = TRUE;\r
4280         }\r
4281     }\r
4282 \r
4283     return result;\r
4284 }\r
4285 \r
4286 VOID\r
4287 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4288 {\r
4289   int row, column, x, y, square_color, piece_color;\r
4290   ChessSquare piece;\r
4291   HBRUSH oldBrush;\r
4292   HDC texture_hdc = NULL;\r
4293 \r
4294   /* [AS] Initialize background textures if needed */\r
4295   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4296       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4297       if( backTextureSquareSize != squareSize \r
4298        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4299           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4300           backTextureSquareSize = squareSize;\r
4301           RebuildTextureSquareInfo();\r
4302       }\r
4303 \r
4304       texture_hdc = CreateCompatibleDC( hdc );\r
4305   }\r
4306 \r
4307   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4308     for (column = 0; column < BOARD_WIDTH; column++) {\r
4309   \r
4310       SquareToPos(row, column, &x, &y);\r
4311 \r
4312       piece = board[row][column];\r
4313 \r
4314       square_color = ((column + row) % 2) == 1;\r
4315       if( gameInfo.variant == VariantXiangqi ) {\r
4316           square_color = !InPalace(row, column);\r
4317           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4318           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4319       }\r
4320       piece_color = (int) piece < (int) BlackPawn;\r
4321 \r
4322 \r
4323       /* [HGM] holdings file: light square or black */\r
4324       if(column == BOARD_LEFT-2) {\r
4325             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4326                 square_color = 1;\r
4327             else {\r
4328                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4329                 continue;\r
4330             }\r
4331       } else\r
4332       if(column == BOARD_RGHT + 1 ) {\r
4333             if( row < gameInfo.holdingsSize )\r
4334                 square_color = 1;\r
4335             else {\r
4336                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4337                 continue;\r
4338             }\r
4339       }\r
4340       if(column == BOARD_LEFT-1 ) /* left align */\r
4341             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4342       else if( column == BOARD_RGHT) /* right align */\r
4343             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4344       else\r
4345       if (appData.monoMode) {\r
4346         if (piece == EmptySquare) {\r
4347           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4348                  square_color ? WHITENESS : BLACKNESS);\r
4349         } else {\r
4350           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4351         }\r
4352       } \r
4353       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4354           /* [AS] Draw the square using a texture bitmap */\r
4355           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4356           int r = row, c = column; // [HGM] do not flip board in flipView\r
4357           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4358 \r
4359           DrawTile( x, y, \r
4360               squareSize, squareSize, \r
4361               hdc, \r
4362               texture_hdc,\r
4363               backTextureSquareInfo[r][c].mode,\r
4364               backTextureSquareInfo[r][c].x,\r
4365               backTextureSquareInfo[r][c].y );\r
4366 \r
4367           SelectObject( texture_hdc, hbm );\r
4368 \r
4369           if (piece != EmptySquare) {\r
4370               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4371           }\r
4372       }\r
4373       else {\r
4374         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4375 \r
4376         oldBrush = SelectObject(hdc, brush );\r
4377         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4378         SelectObject(hdc, oldBrush);\r
4379         if (piece != EmptySquare)\r
4380           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4381       }\r
4382     }\r
4383   }\r
4384 \r
4385   if( texture_hdc != NULL ) {\r
4386     DeleteDC( texture_hdc );\r
4387   }\r
4388 }\r
4389 \r
4390 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4391 void fputDW(FILE *f, int x)\r
4392 {\r
4393         fputc(x     & 255, f);\r
4394         fputc(x>>8  & 255, f);\r
4395         fputc(x>>16 & 255, f);\r
4396         fputc(x>>24 & 255, f);\r
4397 }\r
4398 \r
4399 #define MAX_CLIPS 200   /* more than enough */\r
4400 \r
4401 VOID\r
4402 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4403 {\r
4404 //  HBITMAP bufferBitmap;\r
4405   BITMAP bi;\r
4406 //  RECT Rect;\r
4407   HDC tmphdc;\r
4408   HBITMAP hbm;\r
4409   int w = 100, h = 50;\r
4410 \r
4411   if(logo == NULL) return;\r
4412 //  GetClientRect(hwndMain, &Rect);\r
4413 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4414 //                                      Rect.bottom-Rect.top+1);\r
4415   tmphdc = CreateCompatibleDC(hdc);\r
4416   hbm = SelectObject(tmphdc, logo);\r
4417   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4418             w = bi.bmWidth;\r
4419             h = bi.bmHeight;\r
4420   }\r
4421   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4422                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4423   SelectObject(tmphdc, hbm);\r
4424   DeleteDC(tmphdc);\r
4425 }\r
4426 \r
4427 VOID\r
4428 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4429 {\r
4430   static Board lastReq, lastDrawn;\r
4431   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4432   static int lastDrawnFlipView = 0;\r
4433   static int lastReqValid = 0, lastDrawnValid = 0;\r
4434   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4435   HDC tmphdc;\r
4436   HDC hdcmem;\r
4437   HBITMAP bufferBitmap;\r
4438   HBITMAP oldBitmap;\r
4439   RECT Rect;\r
4440   HRGN clips[MAX_CLIPS];\r
4441   ChessSquare dragged_piece = EmptySquare;\r
4442 \r
4443   /* I'm undecided on this - this function figures out whether a full\r
4444    * repaint is necessary on its own, so there's no real reason to have the\r
4445    * caller tell it that.  I think this can safely be set to FALSE - but\r
4446    * if we trust the callers not to request full repaints unnessesarily, then\r
4447    * we could skip some clipping work.  In other words, only request a full\r
4448    * redraw when the majority of pieces have changed positions (ie. flip, \r
4449    * gamestart and similar)  --Hawk\r
4450    */\r
4451   Boolean fullrepaint = repaint;\r
4452 \r
4453   if( DrawPositionNeedsFullRepaint() ) {\r
4454       fullrepaint = TRUE;\r
4455   }\r
4456 \r
4457 #if 0\r
4458   if( fullrepaint ) {\r
4459       static int repaint_count = 0;\r
4460       char buf[128];\r
4461 \r
4462       repaint_count++;\r
4463       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4464       OutputDebugString( buf );\r
4465   }\r
4466 #endif\r
4467 \r
4468   if (board == NULL) {\r
4469     if (!lastReqValid) {\r
4470       return;\r
4471     }\r
4472     board = lastReq;\r
4473   } else {\r
4474     CopyBoard(lastReq, board);\r
4475     lastReqValid = 1;\r
4476   }\r
4477 \r
4478   if (doingSizing) {\r
4479     return;\r
4480   }\r
4481 \r
4482   if (IsIconic(hwndMain)) {\r
4483     return;\r
4484   }\r
4485 \r
4486   if (hdc == NULL) {\r
4487     hdc = GetDC(hwndMain);\r
4488     if (!appData.monoMode) {\r
4489       SelectPalette(hdc, hPal, FALSE);\r
4490       RealizePalette(hdc);\r
4491     }\r
4492     releaseDC = TRUE;\r
4493   } else {\r
4494     releaseDC = FALSE;\r
4495   }\r
4496 \r
4497 #if 0\r
4498   fprintf(debugFP, "*******************************\n"\r
4499                    "repaint = %s\n"\r
4500                    "dragInfo.from (%d,%d)\n"\r
4501                    "dragInfo.start (%d,%d)\n"\r
4502                    "dragInfo.pos (%d,%d)\n"\r
4503                    "dragInfo.lastpos (%d,%d)\n", \r
4504                     repaint ? "TRUE" : "FALSE",\r
4505                     dragInfo.from.x, dragInfo.from.y, \r
4506                     dragInfo.start.x, dragInfo.start.y,\r
4507                     dragInfo.pos.x, dragInfo.pos.y,\r
4508                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4509   fprintf(debugFP, "prev:  ");\r
4510   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4511     for (column = 0; column < BOARD_WIDTH; column++) {\r
4512       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4513     }\r
4514   }\r
4515   fprintf(debugFP, "\n");\r
4516   fprintf(debugFP, "board: ");\r
4517   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4518     for (column = 0; column < BOARD_WIDTH; column++) {\r
4519       fprintf(debugFP, "%d ", board[row][column]);\r
4520     }\r
4521   }\r
4522   fprintf(debugFP, "\n");\r
4523   fflush(debugFP);\r
4524 #endif\r
4525 \r
4526   /* Create some work-DCs */\r
4527   hdcmem = CreateCompatibleDC(hdc);\r
4528   tmphdc = CreateCompatibleDC(hdc);\r
4529 \r
4530   /* If dragging is in progress, we temporarely remove the piece */\r
4531   /* [HGM] or temporarily decrease count if stacked              */\r
4532   /*       !! Moved to before board compare !!                   */\r
4533   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4534     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4535     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4536             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4537         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4538     } else \r
4539     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4540             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4541         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4542     } else \r
4543         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4544   }\r
4545 \r
4546   /* Figure out which squares need updating by comparing the \r
4547    * newest board with the last drawn board and checking if\r
4548    * flipping has changed.\r
4549    */\r
4550   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4551     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4552       for (column = 0; column < BOARD_WIDTH; column++) {\r
4553         if (lastDrawn[row][column] != board[row][column]) {\r
4554           SquareToPos(row, column, &x, &y);\r
4555           clips[num_clips++] =\r
4556             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4557         }\r
4558       }\r
4559     }\r
4560     for (i=0; i<2; i++) {\r
4561       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4562           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4563         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4564             lastDrawnHighlight.sq[i].y >= 0) {\r
4565           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4566                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4567           clips[num_clips++] =\r
4568             CreateRectRgn(x - lineGap, y - lineGap, \r
4569                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4570         }\r
4571         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4572           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4573           clips[num_clips++] =\r
4574             CreateRectRgn(x - lineGap, y - lineGap, \r
4575                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4576         }\r
4577       }\r
4578     }\r
4579     for (i=0; i<2; i++) {\r
4580       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4581           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4582         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4583             lastDrawnPremove.sq[i].y >= 0) {\r
4584           SquareToPos(lastDrawnPremove.sq[i].y,\r
4585                       lastDrawnPremove.sq[i].x, &x, &y);\r
4586           clips[num_clips++] =\r
4587             CreateRectRgn(x - lineGap, y - lineGap, \r
4588                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4589         }\r
4590         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4591             premoveHighlightInfo.sq[i].y >= 0) {\r
4592           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4593                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4594           clips[num_clips++] =\r
4595             CreateRectRgn(x - lineGap, y - lineGap, \r
4596                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4597         }\r
4598       }\r
4599     }\r
4600   } else {\r
4601     fullrepaint = TRUE;\r
4602   }\r
4603 \r
4604   /* Create a buffer bitmap - this is the actual bitmap\r
4605    * being written to.  When all the work is done, we can\r
4606    * copy it to the real DC (the screen).  This avoids\r
4607    * the problems with flickering.\r
4608    */\r
4609   GetClientRect(hwndMain, &Rect);\r
4610   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4611                                         Rect.bottom-Rect.top+1);\r
4612   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4613   if (!appData.monoMode) {\r
4614     SelectPalette(hdcmem, hPal, FALSE);\r
4615   }\r
4616 \r
4617   /* Create clips for dragging */\r
4618   if (!fullrepaint) {\r
4619     if (dragInfo.from.x >= 0) {\r
4620       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4621       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4622     }\r
4623     if (dragInfo.start.x >= 0) {\r
4624       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4625       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4626     }\r
4627     if (dragInfo.pos.x >= 0) {\r
4628       x = dragInfo.pos.x - squareSize / 2;\r
4629       y = dragInfo.pos.y - squareSize / 2;\r
4630       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4631     }\r
4632     if (dragInfo.lastpos.x >= 0) {\r
4633       x = dragInfo.lastpos.x - squareSize / 2;\r
4634       y = dragInfo.lastpos.y - squareSize / 2;\r
4635       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4636     }\r
4637   }\r
4638 \r
4639   /* Are we animating a move?  \r
4640    * If so, \r
4641    *   - remove the piece from the board (temporarely)\r
4642    *   - calculate the clipping region\r
4643    */\r
4644   if (!fullrepaint) {\r
4645     if (animInfo.piece != EmptySquare) {\r
4646       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4647       x = boardRect.left + animInfo.lastpos.x;\r
4648       y = boardRect.top + animInfo.lastpos.y;\r
4649       x2 = boardRect.left + animInfo.pos.x;\r
4650       y2 = boardRect.top + animInfo.pos.y;\r
4651       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4652       /* Slight kludge.  The real problem is that after AnimateMove is\r
4653          done, the position on the screen does not match lastDrawn.\r
4654          This currently causes trouble only on e.p. captures in\r
4655          atomic, where the piece moves to an empty square and then\r
4656          explodes.  The old and new positions both had an empty square\r
4657          at the destination, but animation has drawn a piece there and\r
4658          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4659       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4660     }\r
4661   }\r
4662 \r
4663   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4664   if (num_clips == 0)\r
4665     fullrepaint = TRUE;\r
4666 \r
4667   /* Set clipping on the memory DC */\r
4668   if (!fullrepaint) {\r
4669     SelectClipRgn(hdcmem, clips[0]);\r
4670     for (x = 1; x < num_clips; x++) {\r
4671       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4672         abort();  // this should never ever happen!\r
4673     }\r
4674   }\r
4675 \r
4676   /* Do all the drawing to the memory DC */\r
4677   if(explodeInfo.radius) { // [HGM] atomic\r
4678         HBRUSH oldBrush;\r
4679         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4680         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4681         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4682         x += squareSize/2;\r
4683         y += squareSize/2;\r
4684         if(!fullrepaint) {\r
4685           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4686           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4687         }\r
4688         DrawGridOnDC(hdcmem);\r
4689         DrawHighlightsOnDC(hdcmem);\r
4690         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4691         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4692         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4693         SelectObject(hdcmem, oldBrush);\r
4694   } else {\r
4695     DrawGridOnDC(hdcmem);\r
4696     DrawHighlightsOnDC(hdcmem);\r
4697     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4698   }\r
4699   if(logoHeight) {\r
4700         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4701         if(appData.autoLogo) {\r
4702           \r
4703           switch(gameMode) { // pick logos based on game mode\r
4704             case IcsObserving:\r
4705                 whiteLogo = second.programLogo; // ICS logo\r
4706                 blackLogo = second.programLogo;\r
4707             default:\r
4708                 break;\r
4709             case IcsPlayingWhite:\r
4710                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4711                 blackLogo = second.programLogo; // ICS logo\r
4712                 break;\r
4713             case IcsPlayingBlack:\r
4714                 whiteLogo = second.programLogo; // ICS logo\r
4715                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4716                 break;\r
4717             case TwoMachinesPlay:\r
4718                 if(first.twoMachinesColor[0] == 'b') {\r
4719                     whiteLogo = second.programLogo;\r
4720                     blackLogo = first.programLogo;\r
4721                 }\r
4722                 break;\r
4723             case MachinePlaysWhite:\r
4724                 blackLogo = userLogo;\r
4725                 break;\r
4726             case MachinePlaysBlack:\r
4727                 whiteLogo = userLogo;\r
4728                 blackLogo = first.programLogo;\r
4729           }\r
4730         }\r
4731         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4732         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4733   }\r
4734 \r
4735   if( appData.highlightMoveWithArrow ) {\r
4736     DrawArrowHighlight(hdcmem);\r
4737   }\r
4738 \r
4739   DrawCoordsOnDC(hdcmem);\r
4740 \r
4741   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4742                  /* to make sure lastDrawn contains what is actually drawn */\r
4743 \r
4744   /* Put the dragged piece back into place and draw it (out of place!) */\r
4745     if (dragged_piece != EmptySquare) {\r
4746     /* [HGM] or restack */\r
4747     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4748                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4749     else\r
4750     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4751                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4752     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4753     x = dragInfo.pos.x - squareSize / 2;\r
4754     y = dragInfo.pos.y - squareSize / 2;\r
4755     DrawPieceOnDC(hdcmem, dragged_piece,\r
4756                   ((int) dragged_piece < (int) BlackPawn), \r
4757                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4758   }   \r
4759   \r
4760   /* Put the animated piece back into place and draw it */\r
4761   if (animInfo.piece != EmptySquare) {\r
4762     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4763     x = boardRect.left + animInfo.pos.x;\r
4764     y = boardRect.top + animInfo.pos.y;\r
4765     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4766                   ((int) animInfo.piece < (int) BlackPawn),\r
4767                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4768   }\r
4769 \r
4770   /* Release the bufferBitmap by selecting in the old bitmap \r
4771    * and delete the memory DC\r
4772    */\r
4773   SelectObject(hdcmem, oldBitmap);\r
4774   DeleteDC(hdcmem);\r
4775 \r
4776   /* Set clipping on the target DC */\r
4777   if (!fullrepaint) {\r
4778     SelectClipRgn(hdc, clips[0]);\r
4779     for (x = 1; x < num_clips; x++) {\r
4780       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4781         abort();   // this should never ever happen!\r
4782     } \r
4783   }\r
4784 \r
4785   /* Copy the new bitmap onto the screen in one go.\r
4786    * This way we avoid any flickering\r
4787    */\r
4788   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4789   BitBlt(hdc, boardRect.left, boardRect.top,\r
4790          boardRect.right - boardRect.left,\r
4791          boardRect.bottom - boardRect.top,\r
4792          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4793   if(saveDiagFlag) { \r
4794     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4795     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4796 \r
4797     GetObject(bufferBitmap, sizeof(b), &b);\r
4798     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4799         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4800         bih.biWidth = b.bmWidth;\r
4801         bih.biHeight = b.bmHeight;\r
4802         bih.biPlanes = 1;\r
4803         bih.biBitCount = b.bmBitsPixel;\r
4804         bih.biCompression = 0;\r
4805         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4806         bih.biXPelsPerMeter = 0;\r
4807         bih.biYPelsPerMeter = 0;\r
4808         bih.biClrUsed = 0;\r
4809         bih.biClrImportant = 0;\r
4810 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4811 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4812         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4813 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4814 \r
4815 #if 1\r
4816         wb = b.bmWidthBytes;\r
4817         // count colors\r
4818         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4819                 int k = ((int*) pData)[i];\r
4820                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4821                 if(j >= 16) break;\r
4822                 color[j] = k;\r
4823                 if(j >= nrColors) nrColors = j+1;\r
4824         }\r
4825         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4826                 INT p = 0;\r
4827                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4828                     for(w=0; w<(wb>>2); w+=2) {\r
4829                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4830                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4831                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4832                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4833                         pData[p++] = m | j<<4;\r
4834                     }\r
4835                     while(p&3) pData[p++] = 0;\r
4836                 }\r
4837                 fac = 3;\r
4838                 wb = ((wb+31)>>5)<<2;\r
4839         }\r
4840         // write BITMAPFILEHEADER\r
4841         fprintf(diagFile, "BM");\r
4842         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4843         fputDW(diagFile, 0);\r
4844         fputDW(diagFile, 0x36 + (fac?64:0));\r
4845         // write BITMAPINFOHEADER\r
4846         fputDW(diagFile, 40);\r
4847         fputDW(diagFile, b.bmWidth);\r
4848         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4849         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4850         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4851         fputDW(diagFile, 0);\r
4852         fputDW(diagFile, 0);\r
4853         fputDW(diagFile, 0);\r
4854         fputDW(diagFile, 0);\r
4855         fputDW(diagFile, 0);\r
4856         fputDW(diagFile, 0);\r
4857         // write color table\r
4858         if(fac)\r
4859         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4860         // write bitmap data\r
4861         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4862                 fputc(pData[i], diagFile);\r
4863 #endif\r
4864      }\r
4865   }\r
4866 \r
4867   SelectObject(tmphdc, oldBitmap);\r
4868 \r
4869   /* Massive cleanup */\r
4870   for (x = 0; x < num_clips; x++)\r
4871     DeleteObject(clips[x]);\r
4872 \r
4873   DeleteDC(tmphdc);\r
4874   DeleteObject(bufferBitmap);\r
4875 \r
4876   if (releaseDC) \r
4877     ReleaseDC(hwndMain, hdc);\r
4878   \r
4879   if (lastDrawnFlipView != flipView) {\r
4880     if (flipView)\r
4881       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4882     else\r
4883       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4884   }\r
4885 \r
4886 /*  CopyBoard(lastDrawn, board);*/\r
4887   lastDrawnHighlight = highlightInfo;\r
4888   lastDrawnPremove   = premoveHighlightInfo;\r
4889   lastDrawnFlipView = flipView;\r
4890   lastDrawnValid = 1;\r
4891 }\r
4892 \r
4893 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4894 int\r
4895 SaveDiagram(f)\r
4896      FILE *f;\r
4897 {\r
4898     saveDiagFlag = 1; diagFile = f;\r
4899     HDCDrawPosition(NULL, TRUE, NULL);\r
4900 \r
4901     saveDiagFlag = 0;\r
4902 \r
4903 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4904     \r
4905     fclose(f);\r
4906     return TRUE;\r
4907 }\r
4908 \r
4909 \r
4910 /*---------------------------------------------------------------------------*\\r
4911 | CLIENT PAINT PROCEDURE\r
4912 |   This is the main event-handler for the WM_PAINT message.\r
4913 |\r
4914 \*---------------------------------------------------------------------------*/\r
4915 VOID\r
4916 PaintProc(HWND hwnd)\r
4917 {\r
4918   HDC         hdc;\r
4919   PAINTSTRUCT ps;\r
4920   HFONT       oldFont;\r
4921 \r
4922   if((hdc = BeginPaint(hwnd, &ps))) {\r
4923     if (IsIconic(hwnd)) {\r
4924       DrawIcon(hdc, 2, 2, iconCurrent);\r
4925     } else {\r
4926       if (!appData.monoMode) {\r
4927         SelectPalette(hdc, hPal, FALSE);\r
4928         RealizePalette(hdc);\r
4929       }\r
4930       HDCDrawPosition(hdc, 1, NULL);\r
4931       oldFont =\r
4932         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4933       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4934                  ETO_CLIPPED|ETO_OPAQUE,\r
4935                  &messageRect, messageText, strlen(messageText), NULL);\r
4936       SelectObject(hdc, oldFont);\r
4937       DisplayBothClocks();\r
4938     }\r
4939     EndPaint(hwnd,&ps);\r
4940   }\r
4941 \r
4942   return;\r
4943 }\r
4944 \r
4945 \r
4946 /*\r
4947  * If the user selects on a border boundary, return -1; if off the board,\r
4948  *   return -2.  Otherwise map the event coordinate to the square.\r
4949  * The offset boardRect.left or boardRect.top must already have been\r
4950  *   subtracted from x.\r
4951  */\r
4952 int\r
4953 EventToSquare(int x)\r
4954 {\r
4955   if (x <= 0)\r
4956     return -2;\r
4957   if (x < lineGap)\r
4958     return -1;\r
4959   x -= lineGap;\r
4960   if ((x % (squareSize + lineGap)) >= squareSize)\r
4961     return -1;\r
4962   x /= (squareSize + lineGap);\r
4963   if (x >= BOARD_SIZE)\r
4964     return -2;\r
4965   return x;\r
4966 }\r
4967 \r
4968 typedef struct {\r
4969   char piece;\r
4970   int command;\r
4971   char* name;\r
4972 } DropEnable;\r
4973 \r
4974 DropEnable dropEnables[] = {\r
4975   { 'P', DP_Pawn, "Pawn" },\r
4976   { 'N', DP_Knight, "Knight" },\r
4977   { 'B', DP_Bishop, "Bishop" },\r
4978   { 'R', DP_Rook, "Rook" },\r
4979   { 'Q', DP_Queen, "Queen" },\r
4980 };\r
4981 \r
4982 VOID\r
4983 SetupDropMenu(HMENU hmenu)\r
4984 {\r
4985   int i, count, enable;\r
4986   char *p;\r
4987   extern char white_holding[], black_holding[];\r
4988   char item[MSG_SIZ];\r
4989 \r
4990   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4991     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4992                dropEnables[i].piece);\r
4993     count = 0;\r
4994     while (p && *p++ == dropEnables[i].piece) count++;\r
4995     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4996     enable = count > 0 || !appData.testLegality\r
4997       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4998                       && !appData.icsActive);\r
4999     ModifyMenu(hmenu, dropEnables[i].command,\r
5000                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
5001                dropEnables[i].command, item);\r
5002   }\r
5003 }\r
5004 \r
5005 /* Event handler for mouse messages */\r
5006 VOID\r
5007 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5008 {\r
5009   int x, y;\r
5010   POINT pt;\r
5011   static int recursive = 0;\r
5012   HMENU hmenu;\r
5013 //  BOOLEAN needsRedraw = FALSE;\r
5014   BOOLEAN saveAnimate;\r
5015   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
5016   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
5017   ChessMove moveType;\r
5018 \r
5019   if (recursive) {\r
5020     if (message == WM_MBUTTONUP) {\r
5021       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
5022          to the middle button: we simulate pressing the left button too!\r
5023          */\r
5024       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
5025       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
5026     }\r
5027     return;\r
5028   }\r
5029   recursive++;\r
5030   \r
5031   pt.x = LOWORD(lParam);\r
5032   pt.y = HIWORD(lParam);\r
5033   x = EventToSquare(pt.x - boardRect.left);\r
5034   y = EventToSquare(pt.y - boardRect.top);\r
5035   if (!flipView && y >= 0) {\r
5036     y = BOARD_HEIGHT - 1 - y;\r
5037   }\r
5038   if (flipView && x >= 0) {\r
5039     x = BOARD_WIDTH - 1 - x;\r
5040   }\r
5041 \r
5042   switch (message) {\r
5043   case WM_LBUTTONDOWN:\r
5044     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
5045         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
5046         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
5047         if(gameInfo.holdingsWidth && \r
5048                 (WhiteOnMove(currentMove) \r
5049                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
5050                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
5051             // click in right holdings, for determining promotion piece\r
5052             ChessSquare p = boards[currentMove][y][x];\r
5053             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
5054             if(p != EmptySquare) {\r
5055                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
5056                 fromX = fromY = -1;\r
5057                 break;\r
5058             }\r
5059         }\r
5060         DrawPosition(FALSE, boards[currentMove]);\r
5061         break;\r
5062     }\r
5063     ErrorPopDown();\r
5064     sameAgain = FALSE;\r
5065     if (y == -2) {\r
5066       /* Downclick vertically off board; check if on clock */\r
5067       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5068         if (gameMode == EditPosition) {\r
5069           SetWhiteToPlayEvent();\r
5070         } else if (gameMode == IcsPlayingBlack ||\r
5071                    gameMode == MachinePlaysWhite) {\r
5072           CallFlagEvent();\r
5073         } else if (gameMode == EditGame) {\r
5074           AdjustClock(flipClock, -1);\r
5075         }\r
5076       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5077         if (gameMode == EditPosition) {\r
5078           SetBlackToPlayEvent();\r
5079         } else if (gameMode == IcsPlayingWhite ||\r
5080                    gameMode == MachinePlaysBlack) {\r
5081           CallFlagEvent();\r
5082         } else if (gameMode == EditGame) {\r
5083           AdjustClock(!flipClock, -1);\r
5084         }\r
5085       }\r
5086       if (!appData.highlightLastMove) {\r
5087         ClearHighlights();\r
5088         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
5089       }\r
5090       fromX = fromY = -1;\r
5091       dragInfo.start.x = dragInfo.start.y = -1;\r
5092       dragInfo.from = dragInfo.start;\r
5093       break;\r
5094     } else if (x < 0 || y < 0\r
5095       /* [HGM] block clicks between board and holdings */\r
5096               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
5097               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
5098               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
5099         /* EditPosition, empty square, or different color piece;\r
5100            click-click move is possible */\r
5101                                ) {\r
5102       break;\r
5103     } else if (fromX == x && fromY == y) {\r
5104       /* Downclick on same square again */\r
5105       ClearHighlights();\r
5106       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5107       sameAgain = TRUE;  \r
5108     } else if (fromX != -1 &&\r
5109                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5110                                                                         ) {\r
5111       /* Downclick on different square. */\r
5112       /* [HGM] if on holdings file, should count as new first click ! */\r
5113       /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5114         toX = x;\r
5115         toY = y;\r
5116         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5117            to make sure move is legal before showing promotion popup */\r
5118         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5119         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5120                 fromX = fromY = -1; \r
5121                 ClearHighlights();\r
5122                 DrawPosition(FALSE, boards[currentMove]);\r
5123                 break; \r
5124         } else \r
5125         if(moveType != ImpossibleMove) {\r
5126           if(moveType == IllegalMove) {\r
5127                 ;\r
5128           } else\r
5129           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5130           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5131             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5132               appData.alwaysPromoteToQueen)) {\r
5133                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5134                   if (!appData.highlightLastMove) {\r
5135                       ClearHighlights();\r
5136                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5137                   }\r
5138           } else\r
5139           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5140                   SetHighlights(fromX, fromY, toX, toY);\r
5141                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5142                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5143                      If promotion to Q is legal, all are legal! */\r
5144                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5145                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5146                     // kludge to temporarily execute move on display, without promoting yet\r
5147                     promotionChoice = TRUE;\r
5148                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5149                     boards[currentMove][toY][toX] = p;\r
5150                     DrawPosition(FALSE, boards[currentMove]);\r
5151                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5152                     boards[currentMove][toY][toX] = q;\r
5153                   } else\r
5154                   PromotionPopup(hwnd);\r
5155           } else {       /* not a promotion */\r
5156              if (appData.animate || appData.highlightLastMove) {\r
5157                  SetHighlights(fromX, fromY, toX, toY);\r
5158              } else {\r
5159                  ClearHighlights();\r
5160              }\r
5161              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5162              if (appData.animate && !appData.highlightLastMove) {\r
5163                   ClearHighlights();\r
5164                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5165              }\r
5166           }\r
5167           fromX = fromY = -1;\r
5168           break;\r
5169         }\r
5170         if (gotPremove) {\r
5171             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5172             DrawPosition(forceFullRepaint || FALSE, NULL);\r
5173         } else ClearHighlights();\r
5174         fromX = fromY = -1;\r
5175         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5176     }\r
5177     /* First downclick, or restart on a square with same color piece */\r
5178     if (!frozen && OKToStartUserMove(x, y)) {\r
5179       fromX = x;\r
5180       fromY = y;\r
5181       dragInfo.lastpos = pt;\r
5182       dragInfo.from.x = fromX;\r
5183       dragInfo.from.y = fromY;\r
5184       dragInfo.start = dragInfo.from;\r
5185       SetCapture(hwndMain);\r
5186     } else {\r
5187       fromX = fromY = -1;\r
5188       dragInfo.start.x = dragInfo.start.y = -1;\r
5189       dragInfo.from = dragInfo.start;\r
5190       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5191     }\r
5192     break;\r
5193 \r
5194   case WM_LBUTTONUP:\r
5195     ReleaseCapture();\r
5196     if (fromX == -1) break;\r
5197     if (x == fromX && y == fromY) {\r
5198       dragInfo.from.x = dragInfo.from.y = -1;\r
5199       /* Upclick on same square */\r
5200       if (sameAgain) {\r
5201         /* Clicked same square twice: abort click-click move */\r
5202         fromX = fromY = -1;\r
5203         gotPremove = 0;\r
5204         ClearPremoveHighlights();\r
5205       } else {\r
5206         /* First square clicked: start click-click move */\r
5207         SetHighlights(fromX, fromY, -1, -1);\r
5208       }\r
5209       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5210     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5211       /* Errant click; ignore */\r
5212       break;\r
5213     } else {\r
5214       /* Finish drag move. */\r
5215     if (appData.debugMode) {\r
5216         fprintf(debugFP, "release\n");\r
5217     }\r
5218       dragInfo.from.x = dragInfo.from.y = -1;\r
5219       toX = x;\r
5220       toY = y;\r
5221       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5222       appData.animate = appData.animate && !appData.animateDragging;\r
5223       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5224       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5225                 fromX = fromY = -1; \r
5226                 ClearHighlights();\r
5227                 DrawPosition(FALSE, boards[currentMove]);\r
5228                 appData.animate = saveAnimate;\r
5229                 break; \r
5230       } else \r
5231       if(moveType != ImpossibleMove) {\r
5232           /* [HGM] use move type to determine if move is promotion.\r
5233              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5234           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5235             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5236               appData.alwaysPromoteToQueen)) \r
5237                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5238           else \r
5239           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5240                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5241                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5242                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5243                     // kludge to temporarily execute move on display, wthout promotng yet\r
5244                     promotionChoice = TRUE;\r
5245                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5246                     boards[currentMove][toY][toX] = p;\r
5247                     DrawPosition(FALSE, boards[currentMove]);\r
5248                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5249                     boards[currentMove][toY][toX] = q;\r
5250                     appData.animate = saveAnimate;\r
5251                     break;\r
5252                   } else\r
5253                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5254           } else {\r
5255             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5256                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5257                                         moveType == WhiteCapturesEnPassant || \r
5258                                         moveType == BlackCapturesEnPassant   ) )\r
5259                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5260             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5261           }\r
5262       }\r
5263       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5264       appData.animate = saveAnimate;\r
5265       fromX = fromY = -1;\r
5266       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5267         ClearHighlights();\r
5268       }\r
5269       if (appData.animate || appData.animateDragging ||\r
5270           appData.highlightDragging || gotPremove) {\r
5271         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5272       }\r
5273     }\r
5274     dragInfo.start.x = dragInfo.start.y = -1; \r
5275     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5276     break;\r
5277 \r
5278   case WM_MOUSEMOVE:\r
5279     if ((appData.animateDragging || appData.highlightDragging)\r
5280         && (wParam & MK_LBUTTON)\r
5281         && dragInfo.from.x >= 0) \r
5282     {\r
5283       BOOL full_repaint = FALSE;\r
5284 \r
5285       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5286       if (appData.animateDragging) {\r
5287         dragInfo.pos = pt;\r
5288       }\r
5289       if (appData.highlightDragging) {\r
5290         SetHighlights(fromX, fromY, x, y);\r
5291         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5292             full_repaint = TRUE;\r
5293         }\r
5294       }\r
5295       \r
5296       DrawPosition( full_repaint, NULL);\r
5297       \r
5298       dragInfo.lastpos = dragInfo.pos;\r
5299     }\r
5300     break;\r
5301 \r
5302   case WM_MOUSEWHEEL: // [DM]\r
5303     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5304        /* Mouse Wheel is being rolled forward\r
5305         * Play moves forward\r
5306         */\r
5307        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5308                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5309        /* Mouse Wheel is being rolled backward\r
5310         * Play moves backward\r
5311         */\r
5312        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5313                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5314     }\r
5315     break;\r
5316 \r
5317   case WM_MBUTTONDOWN:\r
5318   case WM_RBUTTONDOWN:\r
5319     ErrorPopDown();\r
5320     ReleaseCapture();\r
5321     fromX = fromY = -1;\r
5322     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5323     dragInfo.start.x = dragInfo.start.y = -1;\r
5324     dragInfo.from = dragInfo.start;\r
5325     dragInfo.lastpos = dragInfo.pos;\r
5326     if (appData.highlightDragging) {\r
5327       ClearHighlights();\r
5328     }\r
5329     if(y == -2) {\r
5330       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5331       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5332           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5333       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5334           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5335       }\r
5336     }\r
5337     DrawPosition(TRUE, NULL);\r
5338 \r
5339     switch (gameMode) {\r
5340     case EditPosition:\r
5341     case IcsExamining:\r
5342       if (x < 0 || y < 0) break;\r
5343       fromX = x;\r
5344       fromY = y;\r
5345       if (message == WM_MBUTTONDOWN) {\r
5346         buttonCount = 3;  /* even if system didn't think so */\r
5347         if (wParam & MK_SHIFT) \r
5348           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5349         else\r
5350           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5351       } else { /* message == WM_RBUTTONDOWN */\r
5352 #if 0\r
5353         if (buttonCount == 3) {\r
5354           if (wParam & MK_SHIFT) \r
5355             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5356           else\r
5357             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5358         } else {\r
5359           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5360         }\r
5361 #else\r
5362         /* Just have one menu, on the right button.  Windows users don't\r
5363            think to try the middle one, and sometimes other software steals\r
5364            it, or it doesn't really exist. */\r
5365         if(gameInfo.variant != VariantShogi)\r
5366             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5367         else\r
5368             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5369 #endif\r
5370       }\r
5371       break;\r
5372     case IcsPlayingWhite:\r
5373     case IcsPlayingBlack:\r
5374     case EditGame:\r
5375     case MachinePlaysWhite:\r
5376     case MachinePlaysBlack:\r
5377       if (appData.testLegality &&\r
5378           gameInfo.variant != VariantBughouse &&\r
5379           gameInfo.variant != VariantCrazyhouse) break;\r
5380       if (x < 0 || y < 0) break;\r
5381       fromX = x;\r
5382       fromY = y;\r
5383       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5384       SetupDropMenu(hmenu);\r
5385       MenuPopup(hwnd, pt, hmenu, -1);\r
5386       break;\r
5387     default:\r
5388       break;\r
5389     }\r
5390     break;\r
5391   }\r
5392 \r
5393   recursive--;\r
5394 }\r
5395 \r
5396 /* Preprocess messages for buttons in main window */\r
5397 LRESULT CALLBACK\r
5398 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5399 {\r
5400   int id = GetWindowLong(hwnd, GWL_ID);\r
5401   int i, dir;\r
5402 \r
5403   for (i=0; i<N_BUTTONS; i++) {\r
5404     if (buttonDesc[i].id == id) break;\r
5405   }\r
5406   if (i == N_BUTTONS) return 0;\r
5407   switch (message) {\r
5408   case WM_KEYDOWN:\r
5409     switch (wParam) {\r
5410     case VK_LEFT:\r
5411     case VK_RIGHT:\r
5412       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5413       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5414       return TRUE;\r
5415     }\r
5416     break;\r
5417   case WM_CHAR:\r
5418     switch (wParam) {\r
5419     case '\r':\r
5420       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5421       return TRUE;\r
5422     default:\r
5423       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5424         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5425         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5426         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5427         SetFocus(h);\r
5428         SendMessage(h, WM_CHAR, wParam, lParam);\r
5429         return TRUE;\r
5430       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5431         PopUpMoveDialog((char)wParam);\r
5432       }\r
5433       break;\r
5434     }\r
5435     break;\r
5436   }\r
5437   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5438 }\r
5439 \r
5440 /* Process messages for Promotion dialog box */\r
5441 LRESULT CALLBACK\r
5442 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5443 {\r
5444   char promoChar;\r
5445 \r
5446   switch (message) {\r
5447   case WM_INITDIALOG: /* message: initialize dialog box */\r
5448     /* Center the dialog over the application window */\r
5449     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5450     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5451       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5452        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5453                SW_SHOW : SW_HIDE);\r
5454     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5455     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5456        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5457          PieceToChar(WhiteAngel) != '~') ||\r
5458         (PieceToChar(BlackAngel) >= 'A' &&\r
5459          PieceToChar(BlackAngel) != '~')   ) ?\r
5460                SW_SHOW : SW_HIDE);\r
5461     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5462        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5463          PieceToChar(WhiteMarshall) != '~') ||\r
5464         (PieceToChar(BlackMarshall) >= 'A' &&\r
5465          PieceToChar(BlackMarshall) != '~')   ) ?\r
5466                SW_SHOW : SW_HIDE);\r
5467     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5468     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5469        gameInfo.variant != VariantShogi ?\r
5470                SW_SHOW : SW_HIDE);\r
5471     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5472        gameInfo.variant != VariantShogi ?\r
5473                SW_SHOW : SW_HIDE);\r
5474     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5475        gameInfo.variant == VariantShogi ?\r
5476                SW_SHOW : SW_HIDE);\r
5477     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5478        gameInfo.variant == VariantShogi ?\r
5479                SW_SHOW : SW_HIDE);\r
5480     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5481        gameInfo.variant == VariantSuper ?\r
5482                SW_SHOW : SW_HIDE);\r
5483     return TRUE;\r
5484 \r
5485   case WM_COMMAND: /* message: received a command */\r
5486     switch (LOWORD(wParam)) {\r
5487     case IDCANCEL:\r
5488       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5489       ClearHighlights();\r
5490       DrawPosition(FALSE, NULL);\r
5491       return TRUE;\r
5492     case PB_King:\r
5493       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5494       break;\r
5495     case PB_Queen:\r
5496       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5497       break;\r
5498     case PB_Rook:\r
5499       promoChar = PieceToChar(BlackRook);\r
5500       break;\r
5501     case PB_Bishop:\r
5502       promoChar = PieceToChar(BlackBishop);\r
5503       break;\r
5504     case PB_Chancellor:\r
5505       promoChar = PieceToChar(BlackMarshall);\r
5506       break;\r
5507     case PB_Archbishop:\r
5508       promoChar = PieceToChar(BlackAngel);\r
5509       break;\r
5510     case PB_Knight:\r
5511       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5512       break;\r
5513     default:\r
5514       return FALSE;\r
5515     }\r
5516     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5517     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5518        only show the popup when we are already sure the move is valid or\r
5519        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5520        will figure out it is a promotion from the promoChar. */\r
5521     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5522     if (!appData.highlightLastMove) {\r
5523       ClearHighlights();\r
5524       DrawPosition(FALSE, NULL);\r
5525     }\r
5526     return TRUE;\r
5527   }\r
5528   return FALSE;\r
5529 }\r
5530 \r
5531 /* Pop up promotion dialog */\r
5532 VOID\r
5533 PromotionPopup(HWND hwnd)\r
5534 {\r
5535   FARPROC lpProc;\r
5536 \r
5537   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5538   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5539     hwnd, (DLGPROC)lpProc);\r
5540   FreeProcInstance(lpProc);\r
5541 }\r
5542 \r
5543 /* Toggle ShowThinking */\r
5544 VOID\r
5545 ToggleShowThinking()\r
5546 {\r
5547   appData.showThinking = !appData.showThinking;\r
5548   ShowThinkingEvent();\r
5549 }\r
5550 \r
5551 VOID\r
5552 LoadGameDialog(HWND hwnd, char* title)\r
5553 {\r
5554   UINT number = 0;\r
5555   FILE *f;\r
5556   char fileTitle[MSG_SIZ];\r
5557   f = OpenFileDialog(hwnd, "rb", "",\r
5558                      appData.oldSaveStyle ? "gam" : "pgn",\r
5559                      GAME_FILT,\r
5560                      title, &number, fileTitle, NULL);\r
5561   if (f != NULL) {\r
5562     cmailMsgLoaded = FALSE;\r
5563     if (number == 0) {\r
5564       int error = GameListBuild(f);\r
5565       if (error) {\r
5566         DisplayError("Cannot build game list", error);\r
5567       } else if (!ListEmpty(&gameList) &&\r
5568                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5569         GameListPopUp(f, fileTitle);\r
5570         return;\r
5571       }\r
5572       GameListDestroy();\r
5573       number = 1;\r
5574     }\r
5575     LoadGame(f, number, fileTitle, FALSE);\r
5576   }\r
5577 }\r
5578 \r
5579 VOID\r
5580 ChangedConsoleFont()\r
5581 {\r
5582   CHARFORMAT cfmt;\r
5583   CHARRANGE tmpsel, sel;\r
5584   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5585   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5586   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5587   PARAFORMAT paraf;\r
5588 \r
5589   cfmt.cbSize = sizeof(CHARFORMAT);\r
5590   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5591   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5592   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5593    * size.  This was undocumented in the version of MSVC++ that I had\r
5594    * when I wrote the code, but is apparently documented now.\r
5595    */\r
5596   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5597   cfmt.bCharSet = f->lf.lfCharSet;\r
5598   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5599   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5600   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5601   /* Why are the following seemingly needed too? */\r
5602   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5603   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5604   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5605   tmpsel.cpMin = 0;\r
5606   tmpsel.cpMax = -1; /*999999?*/\r
5607   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5608   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5609   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5610    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5611    */\r
5612   paraf.cbSize = sizeof(paraf);\r
5613   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5614   paraf.dxStartIndent = 0;\r
5615   paraf.dxOffset = WRAP_INDENT;\r
5616   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5617   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5618 }\r
5619 \r
5620 /*---------------------------------------------------------------------------*\\r
5621  *\r
5622  * Window Proc for main window\r
5623  *\r
5624 \*---------------------------------------------------------------------------*/\r
5625 \r
5626 /* Process messages for main window, etc. */\r
5627 LRESULT CALLBACK\r
5628 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5629 {\r
5630   FARPROC lpProc;\r
5631   int wmId, wmEvent;\r
5632   char *defName;\r
5633   FILE *f;\r
5634   UINT number;\r
5635   char fileTitle[MSG_SIZ];\r
5636   char buf[MSG_SIZ];\r
5637   static SnapData sd;\r
5638 \r
5639   switch (message) {\r
5640 \r
5641   case WM_PAINT: /* message: repaint portion of window */\r
5642     PaintProc(hwnd);\r
5643     break;\r
5644 \r
5645   case WM_ERASEBKGND:\r
5646     if (IsIconic(hwnd)) {\r
5647       /* Cheat; change the message */\r
5648       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5649     } else {\r
5650       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5651     }\r
5652     break;\r
5653 \r
5654   case WM_LBUTTONDOWN:\r
5655   case WM_MBUTTONDOWN:\r
5656   case WM_RBUTTONDOWN:\r
5657   case WM_LBUTTONUP:\r
5658   case WM_MBUTTONUP:\r
5659   case WM_RBUTTONUP:\r
5660   case WM_MOUSEMOVE:\r
5661   case WM_MOUSEWHEEL:\r
5662     MouseEvent(hwnd, message, wParam, lParam);\r
5663     break;\r
5664 \r
5665   JAWS_KB_NAVIGATION\r
5666 \r
5667   case WM_CHAR:\r
5668     \r
5669     JAWS_ALT_INTERCEPT\r
5670 \r
5671     if (appData.icsActive && (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9')) { \r
5672         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5673         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5674         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5675         SetFocus(h);\r
5676         SendMessage(h, message, wParam, lParam);\r
5677     } else if(lParam != KF_REPEAT) {\r
5678         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5679                 PopUpMoveDialog((char)wParam);\r
5680         } else if((char)wParam == 003) CopyGameToClipboard();\r
5681          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5682     }\r
5683 \r
5684     break;\r
5685 \r
5686   case WM_PALETTECHANGED:\r
5687     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5688       int nnew;\r
5689       HDC hdc = GetDC(hwndMain);\r
5690       SelectPalette(hdc, hPal, TRUE);\r
5691       nnew = RealizePalette(hdc);\r
5692       if (nnew > 0) {\r
5693         paletteChanged = TRUE;\r
5694 #if 0\r
5695         UpdateColors(hdc);\r
5696 #else\r
5697         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5698 #endif\r
5699       }\r
5700       ReleaseDC(hwnd, hdc);\r
5701     }\r
5702     break;\r
5703 \r
5704   case WM_QUERYNEWPALETTE:\r
5705     if (!appData.monoMode /*&& paletteChanged*/) {\r
5706       int nnew;\r
5707       HDC hdc = GetDC(hwndMain);\r
5708       paletteChanged = FALSE;\r
5709       SelectPalette(hdc, hPal, FALSE);\r
5710       nnew = RealizePalette(hdc);\r
5711       if (nnew > 0) {\r
5712         InvalidateRect(hwnd, &boardRect, FALSE);\r
5713       }\r
5714       ReleaseDC(hwnd, hdc);\r
5715       return TRUE;\r
5716     }\r
5717     return FALSE;\r
5718 \r
5719   case WM_COMMAND: /* message: command from application menu */\r
5720     wmId    = LOWORD(wParam);\r
5721     wmEvent = HIWORD(wParam);\r
5722 \r
5723     switch (wmId) {\r
5724     case IDM_NewGame:\r
5725       ResetGameEvent();\r
5726       AnalysisPopDown();\r
5727       SAY("new game enter a move to play against the computer with white");\r
5728       break;\r
5729 \r
5730     case IDM_NewGameFRC:\r
5731       if( NewGameFRC() == 0 ) {\r
5732         ResetGameEvent();\r
5733         AnalysisPopDown();\r
5734       }\r
5735       break;\r
5736 \r
5737     case IDM_NewVariant:\r
5738       NewVariantPopup(hwnd);\r
5739       break;\r
5740 \r
5741     case IDM_LoadGame:\r
5742       LoadGameDialog(hwnd, "Load Game from File");\r
5743       break;\r
5744 \r
5745     case IDM_LoadNextGame:\r
5746       ReloadGame(1);\r
5747       break;\r
5748 \r
5749     case IDM_LoadPrevGame:\r
5750       ReloadGame(-1);\r
5751       break;\r
5752 \r
5753     case IDM_ReloadGame:\r
5754       ReloadGame(0);\r
5755       break;\r
5756 \r
5757     case IDM_LoadPosition:\r
5758       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5759         Reset(FALSE, TRUE);\r
5760       }\r
5761       number = 1;\r
5762       f = OpenFileDialog(hwnd, "rb", "",\r
5763                          appData.oldSaveStyle ? "pos" : "fen",\r
5764                          POSITION_FILT,\r
5765                          "Load Position from File", &number, fileTitle, NULL);\r
5766       if (f != NULL) {\r
5767         LoadPosition(f, number, fileTitle);\r
5768       }\r
5769       break;\r
5770 \r
5771     case IDM_LoadNextPosition:\r
5772       ReloadPosition(1);\r
5773       break;\r
5774 \r
5775     case IDM_LoadPrevPosition:\r
5776       ReloadPosition(-1);\r
5777       break;\r
5778 \r
5779     case IDM_ReloadPosition:\r
5780       ReloadPosition(0);\r
5781       break;\r
5782 \r
5783     case IDM_SaveGame:\r
5784       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5785       f = OpenFileDialog(hwnd, "a", defName,\r
5786                          appData.oldSaveStyle ? "gam" : "pgn",\r
5787                          GAME_FILT,\r
5788                          "Save Game to File", NULL, fileTitle, NULL);\r
5789       if (f != NULL) {\r
5790         SaveGame(f, 0, "");\r
5791       }\r
5792       break;\r
5793 \r
5794     case IDM_SavePosition:\r
5795       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5796       f = OpenFileDialog(hwnd, "a", defName,\r
5797                          appData.oldSaveStyle ? "pos" : "fen",\r
5798                          POSITION_FILT,\r
5799                          "Save Position to File", NULL, fileTitle, NULL);\r
5800       if (f != NULL) {\r
5801         SavePosition(f, 0, "");\r
5802       }\r
5803       break;\r
5804 \r
5805     case IDM_SaveDiagram:\r
5806       defName = "diagram";\r
5807       f = OpenFileDialog(hwnd, "wb", defName,\r
5808                          "bmp",\r
5809                          DIAGRAM_FILT,\r
5810                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5811       if (f != NULL) {\r
5812         SaveDiagram(f);\r
5813       }\r
5814       break;\r
5815 \r
5816     case IDM_CopyGame:\r
5817       CopyGameToClipboard();\r
5818       break;\r
5819 \r
5820     case IDM_PasteGame:\r
5821       PasteGameFromClipboard();\r
5822       break;\r
5823 \r
5824     case IDM_CopyGameListToClipboard:\r
5825       CopyGameListToClipboard();\r
5826       break;\r
5827 \r
5828     /* [AS] Autodetect FEN or PGN data */\r
5829     case IDM_PasteAny:\r
5830       PasteGameOrFENFromClipboard();\r
5831       break;\r
5832 \r
5833     /* [AS] Move history */\r
5834     case IDM_ShowMoveHistory:\r
5835         if( MoveHistoryIsUp() ) {\r
5836             MoveHistoryPopDown();\r
5837         }\r
5838         else {\r
5839             MoveHistoryPopUp();\r
5840         }\r
5841         break;\r
5842 \r
5843     /* [AS] Eval graph */\r
5844     case IDM_ShowEvalGraph:\r
5845         if( EvalGraphIsUp() ) {\r
5846             EvalGraphPopDown();\r
5847         }\r
5848         else {\r
5849             EvalGraphPopUp();\r
5850             SetFocus(hwndMain);\r
5851         }\r
5852         break;\r
5853 \r
5854     /* [AS] Engine output */\r
5855     case IDM_ShowEngineOutput:\r
5856         if( EngineOutputIsUp() ) {\r
5857             EngineOutputPopDown();\r
5858         }\r
5859         else {\r
5860             EngineOutputPopUp();\r
5861         }\r
5862         break;\r
5863 \r
5864     /* [AS] User adjudication */\r
5865     case IDM_UserAdjudication_White:\r
5866         UserAdjudicationEvent( +1 );\r
5867         break;\r
5868 \r
5869     case IDM_UserAdjudication_Black:\r
5870         UserAdjudicationEvent( -1 );\r
5871         break;\r
5872 \r
5873     case IDM_UserAdjudication_Draw:\r
5874         UserAdjudicationEvent( 0 );\r
5875         break;\r
5876 \r
5877     /* [AS] Game list options dialog */\r
5878     case IDM_GameListOptions:\r
5879       GameListOptions();\r
5880       break;\r
5881 \r
5882     case IDM_NewChat:\r
5883       ChatPopUp();\r
5884       break;\r
5885 \r
5886     case IDM_CopyPosition:\r
5887       CopyFENToClipboard();\r
5888       break;\r
5889 \r
5890     case IDM_PastePosition:\r
5891       PasteFENFromClipboard();\r
5892       break;\r
5893 \r
5894     case IDM_MailMove:\r
5895       MailMoveEvent();\r
5896       break;\r
5897 \r
5898     case IDM_ReloadCMailMsg:\r
5899       Reset(TRUE, TRUE);\r
5900       ReloadCmailMsgEvent(FALSE);\r
5901       break;\r
5902 \r
5903     case IDM_Minimize:\r
5904       ShowWindow(hwnd, SW_MINIMIZE);\r
5905       break;\r
5906 \r
5907     case IDM_Exit:\r
5908       ExitEvent(0);\r
5909       break;\r
5910 \r
5911     case IDM_MachineWhite:\r
5912       MachineWhiteEvent();\r
5913       /*\r
5914        * refresh the tags dialog only if it's visible\r
5915        */\r
5916       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5917           char *tags;\r
5918           tags = PGNTags(&gameInfo);\r
5919           TagsPopUp(tags, CmailMsg());\r
5920           free(tags);\r
5921       }\r
5922       SAY("computer starts playing white");\r
5923       break;\r
5924 \r
5925     case IDM_MachineBlack:\r
5926       MachineBlackEvent();\r
5927       /*\r
5928        * refresh the tags dialog only if it's visible\r
5929        */\r
5930       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5931           char *tags;\r
5932           tags = PGNTags(&gameInfo);\r
5933           TagsPopUp(tags, CmailMsg());\r
5934           free(tags);\r
5935       }\r
5936       SAY("computer starts playing black");\r
5937       break;\r
5938 \r
5939     case IDM_TwoMachines:\r
5940       TwoMachinesEvent();\r
5941       /*\r
5942        * refresh the tags dialog only if it's visible\r
5943        */\r
5944       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5945           char *tags;\r
5946           tags = PGNTags(&gameInfo);\r
5947           TagsPopUp(tags, CmailMsg());\r
5948           free(tags);\r
5949       }\r
5950       SAY("programs start playing each other");\r
5951       break;\r
5952 \r
5953     case IDM_AnalysisMode:\r
5954       if (!first.analysisSupport) {\r
5955         sprintf(buf, "%s does not support analysis", first.tidy);\r
5956         DisplayError(buf, 0);\r
5957       } else {\r
5958         SAY("analyzing current position");\r
5959         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5960         if (appData.icsActive) {\r
5961                if (gameMode != IcsObserving) {\r
5962                        sprintf(buf, "You are not observing a game");\r
5963                        DisplayError(buf, 0);\r
5964                        /* secure check */\r
5965                        if (appData.icsEngineAnalyze) {\r
5966                                if (appData.debugMode) \r
5967                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5968                                ExitAnalyzeMode();\r
5969                                ModeHighlight();\r
5970                                break;\r
5971                        }\r
5972                        break;\r
5973                } else {\r
5974                        /* if enable, user want disable icsEngineAnalyze */\r
5975                        if (appData.icsEngineAnalyze) {\r
5976                                ExitAnalyzeMode();\r
5977                                ModeHighlight();\r
5978                                break;\r
5979                        }\r
5980                        appData.icsEngineAnalyze = TRUE;\r
5981                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5982                }\r
5983         } \r
5984         if (!appData.showThinking) ToggleShowThinking();\r
5985         AnalyzeModeEvent();\r
5986       }\r
5987       break;\r
5988 \r
5989     case IDM_AnalyzeFile:\r
5990       if (!first.analysisSupport) {\r
5991         char buf[MSG_SIZ];\r
5992         sprintf(buf, "%s does not support analysis", first.tidy);\r
5993         DisplayError(buf, 0);\r
5994       } else {\r
5995         if (!appData.showThinking) ToggleShowThinking();\r
5996         AnalyzeFileEvent();\r
5997         LoadGameDialog(hwnd, "Analyze Game from File");\r
5998         AnalysisPeriodicEvent(1);\r
5999       }\r
6000       break;\r
6001 \r
6002     case IDM_IcsClient:\r
6003       IcsClientEvent();\r
6004       break;\r
6005 \r
6006     case IDM_EditGame:\r
6007       EditGameEvent();\r
6008       SAY("edit game");\r
6009       break;\r
6010 \r
6011     case IDM_EditPosition:\r
6012       EditPositionEvent();\r
6013       SAY("to set up a position type a FEN");\r
6014       break;\r
6015 \r
6016     case IDM_Training:\r
6017       TrainingEvent();\r
6018       break;\r
6019 \r
6020     case IDM_ShowGameList:\r
6021       ShowGameListProc();\r
6022       break;\r
6023 \r
6024     case IDM_EditTags:\r
6025       EditTagsProc();\r
6026       break;\r
6027 \r
6028     case IDM_EditComment:\r
6029       if (commentDialogUp && editComment) {\r
6030         CommentPopDown();\r
6031       } else {\r
6032         EditCommentEvent();\r
6033       }\r
6034       break;\r
6035 \r
6036     case IDM_Pause:\r
6037       PauseEvent();\r
6038       break;\r
6039 \r
6040     case IDM_Accept:\r
6041       AcceptEvent();\r
6042       break;\r
6043 \r
6044     case IDM_Decline:\r
6045       DeclineEvent();\r
6046       break;\r
6047 \r
6048     case IDM_Rematch:\r
6049       RematchEvent();\r
6050       break;\r
6051 \r
6052     case IDM_CallFlag:\r
6053       CallFlagEvent();\r
6054       break;\r
6055 \r
6056     case IDM_Draw:\r
6057       DrawEvent();\r
6058       break;\r
6059 \r
6060     case IDM_Adjourn:\r
6061       AdjournEvent();\r
6062       break;\r
6063 \r
6064     case IDM_Abort:\r
6065       AbortEvent();\r
6066       break;\r
6067 \r
6068     case IDM_Resign:\r
6069       ResignEvent();\r
6070       break;\r
6071 \r
6072     case IDM_StopObserving:\r
6073       StopObservingEvent();\r
6074       break;\r
6075 \r
6076     case IDM_StopExamining:\r
6077       StopExaminingEvent();\r
6078       break;\r
6079 \r
6080     case IDM_TypeInMove:\r
6081       PopUpMoveDialog('\000');\r
6082       break;\r
6083 \r
6084     case IDM_TypeInName:\r
6085       PopUpNameDialog('\000');\r
6086       break;\r
6087 \r
6088     case IDM_Backward:\r
6089       BackwardEvent();\r
6090       SetFocus(hwndMain);\r
6091       break;\r
6092 \r
6093     JAWS_MENU_ITEMS\r
6094 \r
6095     case IDM_Forward:\r
6096       ForwardEvent();\r
6097       SetFocus(hwndMain);\r
6098       break;\r
6099 \r
6100     case IDM_ToStart:\r
6101       ToStartEvent();\r
6102       SetFocus(hwndMain);\r
6103       break;\r
6104 \r
6105     case IDM_ToEnd:\r
6106       ToEndEvent();\r
6107       SetFocus(hwndMain);\r
6108       break;\r
6109 \r
6110     case IDM_Revert:\r
6111       RevertEvent();\r
6112       break;\r
6113 \r
6114     case IDM_TruncateGame:\r
6115       TruncateGameEvent();\r
6116       break;\r
6117 \r
6118     case IDM_MoveNow:\r
6119       MoveNowEvent();\r
6120       break;\r
6121 \r
6122     case IDM_RetractMove:\r
6123       RetractMoveEvent();\r
6124       break;\r
6125 \r
6126     case IDM_FlipView:\r
6127       flipView = !flipView;\r
6128       DrawPosition(FALSE, NULL);\r
6129       break;\r
6130 \r
6131     case IDM_FlipClock:\r
6132       flipClock = !flipClock;\r
6133       DisplayBothClocks();\r
6134       DrawPosition(FALSE, NULL);\r
6135       break;\r
6136 \r
6137     case IDM_MuteSounds:\r
6138       mute = !mute; // [HGM] mute: keep track of global muting variable\r
6139       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
6140                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
6141       break;\r
6142 \r
6143     case IDM_GeneralOptions:\r
6144       GeneralOptionsPopup(hwnd);\r
6145       DrawPosition(TRUE, NULL);\r
6146       break;\r
6147 \r
6148     case IDM_BoardOptions:\r
6149       BoardOptionsPopup(hwnd);\r
6150       break;\r
6151 \r
6152     case IDM_EnginePlayOptions:\r
6153       EnginePlayOptionsPopup(hwnd);\r
6154       break;\r
6155 \r
6156     case IDM_Engine1Options:\r
6157       EngineOptionsPopup(hwnd, &first);\r
6158       break;\r
6159 \r
6160     case IDM_Engine2Options:\r
6161       EngineOptionsPopup(hwnd, &second);\r
6162       break;\r
6163 \r
6164     case IDM_OptionsUCI:\r
6165       UciOptionsPopup(hwnd);\r
6166       break;\r
6167 \r
6168     case IDM_IcsOptions:\r
6169       IcsOptionsPopup(hwnd);\r
6170       break;\r
6171 \r
6172     case IDM_Fonts:\r
6173       FontsOptionsPopup(hwnd);\r
6174       break;\r
6175 \r
6176     case IDM_Sounds:\r
6177       SoundOptionsPopup(hwnd);\r
6178       break;\r
6179 \r
6180     case IDM_CommPort:\r
6181       CommPortOptionsPopup(hwnd);\r
6182       break;\r
6183 \r
6184     case IDM_LoadOptions:\r
6185       LoadOptionsPopup(hwnd);\r
6186       break;\r
6187 \r
6188     case IDM_SaveOptions:\r
6189       SaveOptionsPopup(hwnd);\r
6190       break;\r
6191 \r
6192     case IDM_TimeControl:\r
6193       TimeControlOptionsPopup(hwnd);\r
6194       break;\r
6195 \r
6196     case IDM_SaveSettings:\r
6197       SaveSettings(settingsFileName);\r
6198       break;\r
6199 \r
6200     case IDM_SaveSettingsOnExit:\r
6201       saveSettingsOnExit = !saveSettingsOnExit;\r
6202       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6203                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6204                                          MF_CHECKED : MF_UNCHECKED));\r
6205       break;\r
6206 \r
6207     case IDM_Hint:\r
6208       HintEvent();\r
6209       break;\r
6210 \r
6211     case IDM_Book:\r
6212       BookEvent();\r
6213       break;\r
6214 \r
6215     case IDM_AboutGame:\r
6216       AboutGameEvent();\r
6217       break;\r
6218 \r
6219     case IDM_Debug:\r
6220       appData.debugMode = !appData.debugMode;\r
6221       if (appData.debugMode) {\r
6222         char dir[MSG_SIZ];\r
6223         GetCurrentDirectory(MSG_SIZ, dir);\r
6224         SetCurrentDirectory(installDir);\r
6225         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6226         SetCurrentDirectory(dir);\r
6227         setbuf(debugFP, NULL);\r
6228       } else {\r
6229         fclose(debugFP);\r
6230         debugFP = NULL;\r
6231       }\r
6232       break;\r
6233 \r
6234     case IDM_HELPCONTENTS:\r
6235       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6236           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6237           MessageBox (GetFocus(),\r
6238                     "Unable to activate help",\r
6239                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6240       }\r
6241       break;\r
6242 \r
6243     case IDM_HELPSEARCH:\r
6244         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6245             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6246         MessageBox (GetFocus(),\r
6247                     "Unable to activate help",\r
6248                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6249       }\r
6250       break;\r
6251 \r
6252     case IDM_HELPHELP:\r
6253       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6254         MessageBox (GetFocus(),\r
6255                     "Unable to activate help",\r
6256                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6257       }\r
6258       break;\r
6259 \r
6260     case IDM_ABOUT:\r
6261       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6262       DialogBox(hInst, \r
6263         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6264         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6265       FreeProcInstance(lpProc);\r
6266       break;\r
6267 \r
6268     case IDM_DirectCommand1:\r
6269       AskQuestionEvent("Direct Command",\r
6270                        "Send to chess program:", "", "1");\r
6271       break;\r
6272     case IDM_DirectCommand2:\r
6273       AskQuestionEvent("Direct Command",\r
6274                        "Send to second chess program:", "", "2");\r
6275       break;\r
6276 \r
6277     case EP_WhitePawn:\r
6278       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6279       fromX = fromY = -1;\r
6280       break;\r
6281 \r
6282     case EP_WhiteKnight:\r
6283       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6284       fromX = fromY = -1;\r
6285       break;\r
6286 \r
6287     case EP_WhiteBishop:\r
6288       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6289       fromX = fromY = -1;\r
6290       break;\r
6291 \r
6292     case EP_WhiteRook:\r
6293       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6294       fromX = fromY = -1;\r
6295       break;\r
6296 \r
6297     case EP_WhiteQueen:\r
6298       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6299       fromX = fromY = -1;\r
6300       break;\r
6301 \r
6302     case EP_WhiteFerz:\r
6303       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6304       fromX = fromY = -1;\r
6305       break;\r
6306 \r
6307     case EP_WhiteWazir:\r
6308       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6309       fromX = fromY = -1;\r
6310       break;\r
6311 \r
6312     case EP_WhiteAlfil:\r
6313       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6314       fromX = fromY = -1;\r
6315       break;\r
6316 \r
6317     case EP_WhiteCannon:\r
6318       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6319       fromX = fromY = -1;\r
6320       break;\r
6321 \r
6322     case EP_WhiteCardinal:\r
6323       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6324       fromX = fromY = -1;\r
6325       break;\r
6326 \r
6327     case EP_WhiteMarshall:\r
6328       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6329       fromX = fromY = -1;\r
6330       break;\r
6331 \r
6332     case EP_WhiteKing:\r
6333       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6334       fromX = fromY = -1;\r
6335       break;\r
6336 \r
6337     case EP_BlackPawn:\r
6338       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6339       fromX = fromY = -1;\r
6340       break;\r
6341 \r
6342     case EP_BlackKnight:\r
6343       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6344       fromX = fromY = -1;\r
6345       break;\r
6346 \r
6347     case EP_BlackBishop:\r
6348       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6349       fromX = fromY = -1;\r
6350       break;\r
6351 \r
6352     case EP_BlackRook:\r
6353       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6354       fromX = fromY = -1;\r
6355       break;\r
6356 \r
6357     case EP_BlackQueen:\r
6358       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6359       fromX = fromY = -1;\r
6360       break;\r
6361 \r
6362     case EP_BlackFerz:\r
6363       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6364       fromX = fromY = -1;\r
6365       break;\r
6366 \r
6367     case EP_BlackWazir:\r
6368       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6369       fromX = fromY = -1;\r
6370       break;\r
6371 \r
6372     case EP_BlackAlfil:\r
6373       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6374       fromX = fromY = -1;\r
6375       break;\r
6376 \r
6377     case EP_BlackCannon:\r
6378       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6379       fromX = fromY = -1;\r
6380       break;\r
6381 \r
6382     case EP_BlackCardinal:\r
6383       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6384       fromX = fromY = -1;\r
6385       break;\r
6386 \r
6387     case EP_BlackMarshall:\r
6388       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6389       fromX = fromY = -1;\r
6390       break;\r
6391 \r
6392     case EP_BlackKing:\r
6393       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6394       fromX = fromY = -1;\r
6395       break;\r
6396 \r
6397     case EP_EmptySquare:\r
6398       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6399       fromX = fromY = -1;\r
6400       break;\r
6401 \r
6402     case EP_ClearBoard:\r
6403       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6404       fromX = fromY = -1;\r
6405       break;\r
6406 \r
6407     case EP_White:\r
6408       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6409       fromX = fromY = -1;\r
6410       break;\r
6411 \r
6412     case EP_Black:\r
6413       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6414       fromX = fromY = -1;\r
6415       break;\r
6416 \r
6417     case EP_Promote:\r
6418       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6419       fromX = fromY = -1;\r
6420       break;\r
6421 \r
6422     case EP_Demote:\r
6423       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6424       fromX = fromY = -1;\r
6425       break;\r
6426 \r
6427     case DP_Pawn:\r
6428       DropMenuEvent(WhitePawn, fromX, fromY);\r
6429       fromX = fromY = -1;\r
6430       break;\r
6431 \r
6432     case DP_Knight:\r
6433       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6434       fromX = fromY = -1;\r
6435       break;\r
6436 \r
6437     case DP_Bishop:\r
6438       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6439       fromX = fromY = -1;\r
6440       break;\r
6441 \r
6442     case DP_Rook:\r
6443       DropMenuEvent(WhiteRook, fromX, fromY);\r
6444       fromX = fromY = -1;\r
6445       break;\r
6446 \r
6447     case DP_Queen:\r
6448       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6449       fromX = fromY = -1;\r
6450       break;\r
6451 \r
6452     default:\r
6453       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6454     }\r
6455     break;\r
6456 \r
6457   case WM_TIMER:\r
6458     switch (wParam) {\r
6459     case CLOCK_TIMER_ID:\r
6460       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6461       clockTimerEvent = 0;\r
6462       DecrementClocks(); /* call into back end */\r
6463       break;\r
6464     case LOAD_GAME_TIMER_ID:\r
6465       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6466       loadGameTimerEvent = 0;\r
6467       AutoPlayGameLoop(); /* call into back end */\r
6468       break;\r
6469     case ANALYSIS_TIMER_ID:\r
6470       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6471                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6472         AnalysisPeriodicEvent(0);\r
6473       } else {\r
6474         KillTimer(hwnd, analysisTimerEvent);\r
6475         analysisTimerEvent = 0;\r
6476       }\r
6477       break;\r
6478     case DELAYED_TIMER_ID:\r
6479       KillTimer(hwnd, delayedTimerEvent);\r
6480       delayedTimerEvent = 0;\r
6481       delayedTimerCallback();\r
6482       break;\r
6483     }\r
6484     break;\r
6485 \r
6486   case WM_USER_Input:\r
6487     InputEvent(hwnd, message, wParam, lParam);\r
6488     break;\r
6489 \r
6490   /* [AS] Also move "attached" child windows */\r
6491   case WM_WINDOWPOSCHANGING:\r
6492 \r
6493     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6494         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6495 \r
6496         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6497             /* Window is moving */\r
6498             RECT rcMain;\r
6499 \r
6500 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6501             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6502             rcMain.right  = boardX + winWidth;\r
6503             rcMain.top    = boardY;\r
6504             rcMain.bottom = boardY + winHeight;\r
6505             \r
6506             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6507             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6508             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6509             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6510             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6511             boardX = lpwp->x;\r
6512             boardY = lpwp->y;\r
6513         }\r
6514     }\r
6515     break;\r
6516 \r
6517   /* [AS] Snapping */\r
6518   case WM_ENTERSIZEMOVE:\r
6519     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6520     if (hwnd == hwndMain) {\r
6521       doingSizing = TRUE;\r
6522       lastSizing = 0;\r
6523     }\r
6524     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6525     break;\r
6526 \r
6527   case WM_SIZING:\r
6528     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6529     if (hwnd == hwndMain) {\r
6530       lastSizing = wParam;\r
6531     }\r
6532     break;\r
6533 \r
6534   case WM_MOVING:\r
6535     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6536       return OnMoving( &sd, hwnd, wParam, lParam );\r
6537 \r
6538   case WM_EXITSIZEMOVE:\r
6539     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6540     if (hwnd == hwndMain) {\r
6541       RECT client;\r
6542       doingSizing = FALSE;\r
6543       InvalidateRect(hwnd, &boardRect, FALSE);\r
6544       GetClientRect(hwnd, &client);\r
6545       ResizeBoard(client.right, client.bottom, lastSizing);\r
6546       lastSizing = 0;\r
6547       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6548     }\r
6549     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6550     break;\r
6551 \r
6552   case WM_DESTROY: /* message: window being destroyed */\r
6553     PostQuitMessage(0);\r
6554     break;\r
6555 \r
6556   case WM_CLOSE:\r
6557     if (hwnd == hwndMain) {\r
6558       ExitEvent(0);\r
6559     }\r
6560     break;\r
6561 \r
6562   default:      /* Passes it on if unprocessed */\r
6563     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6564   }\r
6565   return 0;\r
6566 }\r
6567 \r
6568 /*---------------------------------------------------------------------------*\\r
6569  *\r
6570  * Misc utility routines\r
6571  *\r
6572 \*---------------------------------------------------------------------------*/\r
6573 \r
6574 /*\r
6575  * Decent random number generator, at least not as bad as Windows\r
6576  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6577  */\r
6578 unsigned int randstate;\r
6579 \r
6580 int\r
6581 myrandom(void)\r
6582 {\r
6583   randstate = randstate * 1664525 + 1013904223;\r
6584   return (int) randstate & 0x7fffffff;\r
6585 }\r
6586 \r
6587 void\r
6588 mysrandom(unsigned int seed)\r
6589 {\r
6590   randstate = seed;\r
6591 }\r
6592 \r
6593 \r
6594 /* \r
6595  * returns TRUE if user selects a different color, FALSE otherwise \r
6596  */\r
6597 \r
6598 BOOL\r
6599 ChangeColor(HWND hwnd, COLORREF *which)\r
6600 {\r
6601   static BOOL firstTime = TRUE;\r
6602   static DWORD customColors[16];\r
6603   CHOOSECOLOR cc;\r
6604   COLORREF newcolor;\r
6605   int i;\r
6606   ColorClass ccl;\r
6607 \r
6608   if (firstTime) {\r
6609     /* Make initial colors in use available as custom colors */\r
6610     /* Should we put the compiled-in defaults here instead? */\r
6611     i = 0;\r
6612     customColors[i++] = lightSquareColor & 0xffffff;\r
6613     customColors[i++] = darkSquareColor & 0xffffff;\r
6614     customColors[i++] = whitePieceColor & 0xffffff;\r
6615     customColors[i++] = blackPieceColor & 0xffffff;\r
6616     customColors[i++] = highlightSquareColor & 0xffffff;\r
6617     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6618 \r
6619     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6620       customColors[i++] = textAttribs[ccl].color;\r
6621     }\r
6622     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6623     firstTime = FALSE;\r
6624   }\r
6625 \r
6626   cc.lStructSize = sizeof(cc);\r
6627   cc.hwndOwner = hwnd;\r
6628   cc.hInstance = NULL;\r
6629   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6630   cc.lpCustColors = (LPDWORD) customColors;\r
6631   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6632 \r
6633   if (!ChooseColor(&cc)) return FALSE;\r
6634 \r
6635   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6636   if (newcolor == *which) return FALSE;\r
6637   *which = newcolor;\r
6638   return TRUE;\r
6639 \r
6640   /*\r
6641   InitDrawingColors();\r
6642   InvalidateRect(hwnd, &boardRect, FALSE);\r
6643   */\r
6644 }\r
6645 \r
6646 BOOLEAN\r
6647 MyLoadSound(MySound *ms)\r
6648 {\r
6649   BOOL ok = FALSE;\r
6650   struct stat st;\r
6651   FILE *f;\r
6652 \r
6653   if (ms->data) free(ms->data);\r
6654   ms->data = NULL;\r
6655 \r
6656   switch (ms->name[0]) {\r
6657   case NULLCHAR:\r
6658     /* Silence */\r
6659     ok = TRUE;\r
6660     break;\r
6661   case '$':\r
6662     /* System sound from Control Panel.  Don't preload here. */\r
6663     ok = TRUE;\r
6664     break;\r
6665   case '!':\r
6666     if (ms->name[1] == NULLCHAR) {\r
6667       /* "!" alone = silence */\r
6668       ok = TRUE;\r
6669     } else {\r
6670       /* Builtin wave resource.  Error if not found. */\r
6671       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6672       if (h == NULL) break;\r
6673       ms->data = (void *)LoadResource(hInst, h);\r
6674       if (h == NULL) break;\r
6675       ok = TRUE;\r
6676     }\r
6677     break;\r
6678   default:\r
6679     /* .wav file.  Error if not found. */\r
6680     f = fopen(ms->name, "rb");\r
6681     if (f == NULL) break;\r
6682     if (fstat(fileno(f), &st) < 0) break;\r
6683     ms->data = malloc(st.st_size);\r
6684     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6685     fclose(f);\r
6686     ok = TRUE;\r
6687     break;\r
6688   }\r
6689   if (!ok) {\r
6690     char buf[MSG_SIZ];\r
6691     sprintf(buf, "Error loading sound %s", ms->name);\r
6692     DisplayError(buf, GetLastError());\r
6693   }\r
6694   return ok;\r
6695 }\r
6696 \r
6697 BOOLEAN\r
6698 MyPlaySound(MySound *ms)\r
6699 {\r
6700   BOOLEAN ok = FALSE;\r
6701 \r
6702   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6703   switch (ms->name[0]) {\r
6704   case NULLCHAR:\r
6705         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6706     /* Silence */\r
6707     ok = TRUE;\r
6708     break;\r
6709   case '$':\r
6710     /* System sound from Control Panel (deprecated feature).\r
6711        "$" alone or an unset sound name gets default beep (still in use). */\r
6712     if (ms->name[1]) {\r
6713       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6714     }\r
6715     if (!ok) ok = MessageBeep(MB_OK);\r
6716     break; \r
6717   case '!':\r
6718     /* Builtin wave resource, or "!" alone for silence */\r
6719     if (ms->name[1]) {\r
6720       if (ms->data == NULL) return FALSE;\r
6721       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6722     } else {\r
6723       ok = TRUE;\r
6724     }\r
6725     break;\r
6726   default:\r
6727     /* .wav file.  Error if not found. */\r
6728     if (ms->data == NULL) return FALSE;\r
6729     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6730     break;\r
6731   }\r
6732   /* Don't print an error: this can happen innocently if the sound driver\r
6733      is busy; for instance, if another instance of WinBoard is playing\r
6734      a sound at about the same time. */\r
6735 #if 0\r
6736   if (!ok) {\r
6737     char buf[MSG_SIZ];\r
6738     sprintf(buf, "Error playing sound %s", ms->name);\r
6739     DisplayError(buf, GetLastError());\r
6740   }\r
6741 #endif\r
6742   return ok;\r
6743 }\r
6744 \r
6745 \r
6746 LRESULT CALLBACK\r
6747 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6748 {\r
6749   BOOL ok;\r
6750   OPENFILENAME *ofn;\r
6751   static UINT *number; /* gross that this is static */\r
6752 \r
6753   switch (message) {\r
6754   case WM_INITDIALOG: /* message: initialize dialog box */\r
6755     /* Center the dialog over the application window */\r
6756     ofn = (OPENFILENAME *) lParam;\r
6757     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6758       number = (UINT *) ofn->lCustData;\r
6759       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6760     } else {\r
6761       number = NULL;\r
6762     }\r
6763     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6764     return FALSE;  /* Allow for further processing */\r
6765 \r
6766   case WM_COMMAND:\r
6767     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6768       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6769     }\r
6770     return FALSE;  /* Allow for further processing */\r
6771   }\r
6772   return FALSE;\r
6773 }\r
6774 \r
6775 UINT APIENTRY\r
6776 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6777 {\r
6778   static UINT *number;\r
6779   OPENFILENAME *ofname;\r
6780   OFNOTIFY *ofnot;\r
6781   switch (uiMsg) {\r
6782   case WM_INITDIALOG:\r
6783     ofname = (OPENFILENAME *)lParam;\r
6784     number = (UINT *)(ofname->lCustData);\r
6785     break;\r
6786   case WM_NOTIFY:\r
6787     ofnot = (OFNOTIFY *)lParam;\r
6788     if (ofnot->hdr.code == CDN_FILEOK) {\r
6789       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6790     }\r
6791     break;\r
6792   }\r
6793   return 0;\r
6794 }\r
6795 \r
6796 \r
6797 FILE *\r
6798 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6799                char *nameFilt, char *dlgTitle, UINT *number,\r
6800                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6801 {\r
6802   OPENFILENAME openFileName;\r
6803   char buf1[MSG_SIZ];\r
6804   FILE *f;\r
6805 \r
6806   if (fileName == NULL) fileName = buf1;\r
6807   if (defName == NULL) {\r
6808     strcpy(fileName, "*.");\r
6809     strcat(fileName, defExt);\r
6810   } else {\r
6811     strcpy(fileName, defName);\r
6812   }\r
6813   if (fileTitle) strcpy(fileTitle, "");\r
6814   if (number) *number = 0;\r
6815 \r
6816   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6817   openFileName.hwndOwner         = hwnd;\r
6818   openFileName.hInstance         = (HANDLE) hInst;\r
6819   openFileName.lpstrFilter       = nameFilt;\r
6820   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6821   openFileName.nMaxCustFilter    = 0L;\r
6822   openFileName.nFilterIndex      = 1L;\r
6823   openFileName.lpstrFile         = fileName;\r
6824   openFileName.nMaxFile          = MSG_SIZ;\r
6825   openFileName.lpstrFileTitle    = fileTitle;\r
6826   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6827   openFileName.lpstrInitialDir   = NULL;\r
6828   openFileName.lpstrTitle        = dlgTitle;\r
6829   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6830     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6831     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6832     | (oldDialog ? 0 : OFN_EXPLORER);\r
6833   openFileName.nFileOffset       = 0;\r
6834   openFileName.nFileExtension    = 0;\r
6835   openFileName.lpstrDefExt       = defExt;\r
6836   openFileName.lCustData         = (LONG) number;\r
6837   openFileName.lpfnHook          = oldDialog ?\r
6838     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6839   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6840 \r
6841   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6842                         GetOpenFileName(&openFileName)) {\r
6843     /* open the file */\r
6844     f = fopen(openFileName.lpstrFile, write);\r
6845     if (f == NULL) {\r
6846       MessageBox(hwnd, "File open failed", NULL,\r
6847                  MB_OK|MB_ICONEXCLAMATION);\r
6848       return NULL;\r
6849     }\r
6850   } else {\r
6851     int err = CommDlgExtendedError();\r
6852     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6853     return FALSE;\r
6854   }\r
6855   return f;\r
6856 }\r
6857 \r
6858 \r
6859 \r
6860 VOID APIENTRY\r
6861 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6862 {\r
6863   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6864 \r
6865   /*\r
6866    * Get the first pop-up menu in the menu template. This is the\r
6867    * menu that TrackPopupMenu displays.\r
6868    */\r
6869   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6870 \r
6871   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6872 \r
6873   /*\r
6874    * TrackPopup uses screen coordinates, so convert the\r
6875    * coordinates of the mouse click to screen coordinates.\r
6876    */\r
6877   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6878 \r
6879   /* Draw and track the floating pop-up menu. */\r
6880   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6881                  pt.x, pt.y, 0, hwnd, NULL);\r
6882 \r
6883   /* Destroy the menu.*/\r
6884   DestroyMenu(hmenu);\r
6885 }\r
6886    \r
6887 typedef struct {\r
6888   HWND hDlg, hText;\r
6889   int sizeX, sizeY, newSizeX, newSizeY;\r
6890   HDWP hdwp;\r
6891 } ResizeEditPlusButtonsClosure;\r
6892 \r
6893 BOOL CALLBACK\r
6894 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6895 {\r
6896   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6897   RECT rect;\r
6898   POINT pt;\r
6899 \r
6900   if (hChild == cl->hText) return TRUE;\r
6901   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6902   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6903   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6904   ScreenToClient(cl->hDlg, &pt);\r
6905   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6906     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6907   return TRUE;\r
6908 }\r
6909 \r
6910 /* Resize a dialog that has a (rich) edit field filling most of\r
6911    the top, with a row of buttons below */\r
6912 VOID\r
6913 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6914 {\r
6915   RECT rectText;\r
6916   int newTextHeight, newTextWidth;\r
6917   ResizeEditPlusButtonsClosure cl;\r
6918   \r
6919   /*if (IsIconic(hDlg)) return;*/\r
6920   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6921   \r
6922   cl.hdwp = BeginDeferWindowPos(8);\r
6923 \r
6924   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6925   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6926   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6927   if (newTextHeight < 0) {\r
6928     newSizeY += -newTextHeight;\r
6929     newTextHeight = 0;\r
6930   }\r
6931   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6932     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6933 \r
6934   cl.hDlg = hDlg;\r
6935   cl.hText = hText;\r
6936   cl.sizeX = sizeX;\r
6937   cl.sizeY = sizeY;\r
6938   cl.newSizeX = newSizeX;\r
6939   cl.newSizeY = newSizeY;\r
6940   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6941 \r
6942   EndDeferWindowPos(cl.hdwp);\r
6943 }\r
6944 \r
6945 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6946 {\r
6947     RECT    rChild, rParent;\r
6948     int     wChild, hChild, wParent, hParent;\r
6949     int     wScreen, hScreen, xNew, yNew;\r
6950     HDC     hdc;\r
6951 \r
6952     /* Get the Height and Width of the child window */\r
6953     GetWindowRect (hwndChild, &rChild);\r
6954     wChild = rChild.right - rChild.left;\r
6955     hChild = rChild.bottom - rChild.top;\r
6956 \r
6957     /* Get the Height and Width of the parent window */\r
6958     GetWindowRect (hwndParent, &rParent);\r
6959     wParent = rParent.right - rParent.left;\r
6960     hParent = rParent.bottom - rParent.top;\r
6961 \r
6962     /* Get the display limits */\r
6963     hdc = GetDC (hwndChild);\r
6964     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6965     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6966     ReleaseDC(hwndChild, hdc);\r
6967 \r
6968     /* Calculate new X position, then adjust for screen */\r
6969     xNew = rParent.left + ((wParent - wChild) /2);\r
6970     if (xNew < 0) {\r
6971         xNew = 0;\r
6972     } else if ((xNew+wChild) > wScreen) {\r
6973         xNew = wScreen - wChild;\r
6974     }\r
6975 \r
6976     /* Calculate new Y position, then adjust for screen */\r
6977     if( mode == 0 ) {\r
6978         yNew = rParent.top  + ((hParent - hChild) /2);\r
6979     }\r
6980     else {\r
6981         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6982     }\r
6983 \r
6984     if (yNew < 0) {\r
6985         yNew = 0;\r
6986     } else if ((yNew+hChild) > hScreen) {\r
6987         yNew = hScreen - hChild;\r
6988     }\r
6989 \r
6990     /* Set it, and return */\r
6991     return SetWindowPos (hwndChild, NULL,\r
6992                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6993 }\r
6994 \r
6995 /* Center one window over another */\r
6996 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6997 {\r
6998     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6999 }\r
7000 \r
7001 /*---------------------------------------------------------------------------*\\r
7002  *\r
7003  * Startup Dialog functions\r
7004  *\r
7005 \*---------------------------------------------------------------------------*/\r
7006 void\r
7007 InitComboStrings(HANDLE hwndCombo, char **cd)\r
7008 {\r
7009   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7010 \r
7011   while (*cd != NULL) {\r
7012     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
7013     cd++;\r
7014   }\r
7015 }\r
7016 \r
7017 void\r
7018 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
7019 {\r
7020   char buf1[ARG_MAX];\r
7021   int len;\r
7022 \r
7023   if (str[0] == '@') {\r
7024     FILE* f = fopen(str + 1, "r");\r
7025     if (f == NULL) {\r
7026       DisplayFatalError(str + 1, errno, 2);\r
7027       return;\r
7028     }\r
7029     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
7030     fclose(f);\r
7031     buf1[len] = NULLCHAR;\r
7032     str = buf1;\r
7033   }\r
7034 \r
7035   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7036 \r
7037   for (;;) {\r
7038     char buf[MSG_SIZ];\r
7039     char *end = strchr(str, '\n');\r
7040     if (end == NULL) return;\r
7041     memcpy(buf, str, end - str);\r
7042     buf[end - str] = NULLCHAR;\r
7043     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
7044     str = end + 1;\r
7045   }\r
7046 }\r
7047 \r
7048 void\r
7049 SetStartupDialogEnables(HWND hDlg)\r
7050 {\r
7051   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
7052     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7053     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
7054   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7055     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
7056   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
7057     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
7058   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
7059     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
7060   EnableWindow(GetDlgItem(hDlg, IDOK),\r
7061     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7062     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
7063     IsDlgButtonChecked(hDlg, OPT_View));\r
7064 }\r
7065 \r
7066 char *\r
7067 QuoteForFilename(char *filename)\r
7068 {\r
7069   int dquote, space;\r
7070   dquote = strchr(filename, '"') != NULL;\r
7071   space = strchr(filename, ' ') != NULL;\r
7072   if (dquote || space) {\r
7073     if (dquote) {\r
7074       return "'";\r
7075     } else {\r
7076       return "\"";\r
7077     }\r
7078   } else {\r
7079     return "";\r
7080   }\r
7081 }\r
7082 \r
7083 VOID\r
7084 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
7085 {\r
7086   char buf[MSG_SIZ];\r
7087   char *q;\r
7088 \r
7089   InitComboStringsFromOption(hwndCombo, nthnames);\r
7090   q = QuoteForFilename(nthcp);\r
7091   sprintf(buf, "%s%s%s", q, nthcp, q);\r
7092   if (*nthdir != NULLCHAR) {\r
7093     q = QuoteForFilename(nthdir);\r
7094     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
7095   }\r
7096   if (*nthcp == NULLCHAR) {\r
7097     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7098   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7099     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7100     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7101   }\r
7102 }\r
7103 \r
7104 LRESULT CALLBACK\r
7105 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7106 {\r
7107   char buf[MSG_SIZ];\r
7108   HANDLE hwndCombo;\r
7109   char *p;\r
7110 \r
7111   switch (message) {\r
7112   case WM_INITDIALOG:\r
7113     /* Center the dialog */\r
7114     CenterWindow (hDlg, GetDesktopWindow());\r
7115     /* Initialize the dialog items */\r
7116     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7117                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7118                   firstChessProgramNames);\r
7119     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7120                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7121                   secondChessProgramNames);\r
7122     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7123     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7124     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7125     if (*appData.icsHelper != NULLCHAR) {\r
7126       char *q = QuoteForFilename(appData.icsHelper);\r
7127       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7128     }\r
7129     if (*appData.icsHost == NULLCHAR) {\r
7130       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7131       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7132     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7133       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7134       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7135     }\r
7136 \r
7137     if (appData.icsActive) {\r
7138       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7139     }\r
7140     else if (appData.noChessProgram) {\r
7141       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7142     }\r
7143     else {\r
7144       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7145     }\r
7146 \r
7147     SetStartupDialogEnables(hDlg);\r
7148     return TRUE;\r
7149 \r
7150   case WM_COMMAND:\r
7151     switch (LOWORD(wParam)) {\r
7152     case IDOK:\r
7153       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7154         strcpy(buf, "/fcp=");\r
7155         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7156         p = buf;\r
7157         ParseArgs(StringGet, &p);\r
7158         strcpy(buf, "/scp=");\r
7159         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7160         p = buf;\r
7161         ParseArgs(StringGet, &p);\r
7162         appData.noChessProgram = FALSE;\r
7163         appData.icsActive = FALSE;\r
7164       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7165         strcpy(buf, "/ics /icshost=");\r
7166         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7167         p = buf;\r
7168         ParseArgs(StringGet, &p);\r
7169         if (appData.zippyPlay) {\r
7170           strcpy(buf, "/fcp=");\r
7171           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7172           p = buf;\r
7173           ParseArgs(StringGet, &p);\r
7174         }\r
7175       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7176         appData.noChessProgram = TRUE;\r
7177         appData.icsActive = FALSE;\r
7178       } else {\r
7179         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7180                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7181         return TRUE;\r
7182       }\r
7183       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7184         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7185         p = buf;\r
7186         ParseArgs(StringGet, &p);\r
7187       }\r
7188       EndDialog(hDlg, TRUE);\r
7189       return TRUE;\r
7190 \r
7191     case IDCANCEL:\r
7192       ExitEvent(0);\r
7193       return TRUE;\r
7194 \r
7195     case IDM_HELPCONTENTS:\r
7196       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7197         MessageBox (GetFocus(),\r
7198                     "Unable to activate help",\r
7199                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7200       }\r
7201       break;\r
7202 \r
7203     default:\r
7204       SetStartupDialogEnables(hDlg);\r
7205       break;\r
7206     }\r
7207     break;\r
7208   }\r
7209   return FALSE;\r
7210 }\r
7211 \r
7212 /*---------------------------------------------------------------------------*\\r
7213  *\r
7214  * About box dialog functions\r
7215  *\r
7216 \*---------------------------------------------------------------------------*/\r
7217 \r
7218 /* Process messages for "About" dialog box */\r
7219 LRESULT CALLBACK\r
7220 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7221 {\r
7222   switch (message) {\r
7223   case WM_INITDIALOG: /* message: initialize dialog box */\r
7224     /* Center the dialog over the application window */\r
7225     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7226     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7227     JAWS_COPYRIGHT\r
7228     return (TRUE);\r
7229 \r
7230   case WM_COMMAND: /* message: received a command */\r
7231     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7232         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7233       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7234       return (TRUE);\r
7235     }\r
7236     break;\r
7237   }\r
7238   return (FALSE);\r
7239 }\r
7240 \r
7241 /*---------------------------------------------------------------------------*\\r
7242  *\r
7243  * Comment Dialog functions\r
7244  *\r
7245 \*---------------------------------------------------------------------------*/\r
7246 \r
7247 LRESULT CALLBACK\r
7248 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7249 {\r
7250   static HANDLE hwndText = NULL;\r
7251   int len, newSizeX, newSizeY, flags;\r
7252   static int sizeX, sizeY;\r
7253   char *str;\r
7254   RECT rect;\r
7255   MINMAXINFO *mmi;\r
7256 \r
7257   switch (message) {\r
7258   case WM_INITDIALOG: /* message: initialize dialog box */\r
7259     /* Initialize the dialog items */\r
7260     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7261     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7262     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7263     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7264     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7265     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7266     SetWindowText(hDlg, commentTitle);\r
7267     if (editComment) {\r
7268       SetFocus(hwndText);\r
7269     } else {\r
7270       SetFocus(GetDlgItem(hDlg, IDOK));\r
7271     }\r
7272     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7273                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7274                 MAKELPARAM(FALSE, 0));\r
7275     /* Size and position the dialog */\r
7276     if (!commentDialog) {\r
7277       commentDialog = hDlg;\r
7278       flags = SWP_NOZORDER;\r
7279       GetClientRect(hDlg, &rect);\r
7280       sizeX = rect.right;\r
7281       sizeY = rect.bottom;\r
7282       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7283           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7284         WINDOWPLACEMENT wp;\r
7285         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7286         wp.length = sizeof(WINDOWPLACEMENT);\r
7287         wp.flags = 0;\r
7288         wp.showCmd = SW_SHOW;\r
7289         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7290         wp.rcNormalPosition.left = commentX;\r
7291         wp.rcNormalPosition.right = commentX + commentW;\r
7292         wp.rcNormalPosition.top = commentY;\r
7293         wp.rcNormalPosition.bottom = commentY + commentH;\r
7294         SetWindowPlacement(hDlg, &wp);\r
7295 \r
7296         GetClientRect(hDlg, &rect);\r
7297         newSizeX = rect.right;\r
7298         newSizeY = rect.bottom;\r
7299         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7300                               newSizeX, newSizeY);\r
7301         sizeX = newSizeX;\r
7302         sizeY = newSizeY;\r
7303       }\r
7304     }\r
7305     return FALSE;\r
7306 \r
7307   case WM_COMMAND: /* message: received a command */\r
7308     switch (LOWORD(wParam)) {\r
7309     case IDOK:\r
7310       if (editComment) {\r
7311         char *p, *q;\r
7312         /* Read changed options from the dialog box */\r
7313         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7314         len = GetWindowTextLength(hwndText);\r
7315         str = (char *) malloc(len + 1);\r
7316         GetWindowText(hwndText, str, len + 1);\r
7317         p = q = str;\r
7318         while (*q) {\r
7319           if (*q == '\r')\r
7320             q++;\r
7321           else\r
7322             *p++ = *q++;\r
7323         }\r
7324         *p = NULLCHAR;\r
7325         ReplaceComment(commentIndex, str);\r
7326         free(str);\r
7327       }\r
7328       CommentPopDown();\r
7329       return TRUE;\r
7330 \r
7331     case IDCANCEL:\r
7332     case OPT_CancelComment:\r
7333       CommentPopDown();\r
7334       return TRUE;\r
7335 \r
7336     case OPT_ClearComment:\r
7337       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7338       break;\r
7339 \r
7340     case OPT_EditComment:\r
7341       EditCommentEvent();\r
7342       return TRUE;\r
7343 \r
7344     default:\r
7345       break;\r
7346     }\r
7347     break;\r
7348 \r
7349   case WM_SIZE:\r
7350     newSizeX = LOWORD(lParam);\r
7351     newSizeY = HIWORD(lParam);\r
7352     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7353     sizeX = newSizeX;\r
7354     sizeY = newSizeY;\r
7355     break;\r
7356 \r
7357   case WM_GETMINMAXINFO:\r
7358     /* Prevent resizing window too small */\r
7359     mmi = (MINMAXINFO *) lParam;\r
7360     mmi->ptMinTrackSize.x = 100;\r
7361     mmi->ptMinTrackSize.y = 100;\r
7362     break;\r
7363   }\r
7364   return FALSE;\r
7365 }\r
7366 \r
7367 VOID\r
7368 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7369 {\r
7370   FARPROC lpProc;\r
7371   char *p, *q;\r
7372 \r
7373   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7374 \r
7375   if (str == NULL) str = "";\r
7376   p = (char *) malloc(2 * strlen(str) + 2);\r
7377   q = p;\r
7378   while (*str) {\r
7379     if (*str == '\n') *q++ = '\r';\r
7380     *q++ = *str++;\r
7381   }\r
7382   *q = NULLCHAR;\r
7383   if (commentText != NULL) free(commentText);\r
7384 \r
7385   commentIndex = index;\r
7386   commentTitle = title;\r
7387   commentText = p;\r
7388   editComment = edit;\r
7389 \r
7390   if (commentDialog) {\r
7391     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7392     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7393   } else {\r
7394     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7395     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7396                  hwndMain, (DLGPROC)lpProc);\r
7397     FreeProcInstance(lpProc);\r
7398   }\r
7399   commentDialogUp = TRUE;\r
7400 }\r
7401 \r
7402 \r
7403 /*---------------------------------------------------------------------------*\\r
7404  *\r
7405  * Type-in move dialog functions\r
7406  * \r
7407 \*---------------------------------------------------------------------------*/\r
7408 \r
7409 LRESULT CALLBACK\r
7410 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7411 {\r
7412   char move[MSG_SIZ];\r
7413   HWND hInput;\r
7414   ChessMove moveType;\r
7415   int fromX, fromY, toX, toY;\r
7416   char promoChar;\r
7417 \r
7418   switch (message) {\r
7419   case WM_INITDIALOG:\r
7420     move[0] = (char) lParam;\r
7421     move[1] = NULLCHAR;\r
7422     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7423     hInput = GetDlgItem(hDlg, OPT_Move);\r
7424     SetWindowText(hInput, move);\r
7425     SetFocus(hInput);\r
7426     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7427     return FALSE;\r
7428 \r
7429   case WM_COMMAND:\r
7430     switch (LOWORD(wParam)) {\r
7431     case IDOK:\r
7432       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7433       { int n; Board board;\r
7434         // [HGM] FENedit\r
7435         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7436                 EditPositionPasteFEN(move);\r
7437                 EndDialog(hDlg, TRUE);\r
7438                 return TRUE;\r
7439         }\r
7440         // [HGM] movenum: allow move number to be typed in any mode\r
7441         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7442           currentMove = 2*n-1;\r
7443           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7444           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7445           EndDialog(hDlg, TRUE);\r
7446           DrawPosition(TRUE, boards[currentMove]);\r
7447           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7448           else DisplayMessage("", "");\r
7449           return TRUE;\r
7450         }\r
7451       }\r
7452       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7453         gameMode != Training) {\r
7454         DisplayMoveError("Displayed move is not current");\r
7455       } else {\r
7456 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7457         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7458           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7459         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7460         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7461           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7462           if (gameMode != Training)\r
7463               forwardMostMove = currentMove;\r
7464           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7465         } else {\r
7466           DisplayMoveError("Could not parse move");\r
7467         }\r
7468       }\r
7469       EndDialog(hDlg, TRUE);\r
7470       return TRUE;\r
7471     case IDCANCEL:\r
7472       EndDialog(hDlg, FALSE);\r
7473       return TRUE;\r
7474     default:\r
7475       break;\r
7476     }\r
7477     break;\r
7478   }\r
7479   return FALSE;\r
7480 }\r
7481 \r
7482 VOID\r
7483 PopUpMoveDialog(char firstchar)\r
7484 {\r
7485     FARPROC lpProc;\r
7486     \r
7487     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7488         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7489         gameMode == AnalyzeMode || gameMode == EditGame || \r
7490         gameMode == EditPosition || gameMode == IcsExamining ||\r
7491         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7492         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7493                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7494                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7495         gameMode == Training) {\r
7496       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7497       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7498         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7499       FreeProcInstance(lpProc);\r
7500     }\r
7501 }\r
7502 \r
7503 /*---------------------------------------------------------------------------*\\r
7504  *\r
7505  * Type-in name dialog functions\r
7506  * \r
7507 \*---------------------------------------------------------------------------*/\r
7508 \r
7509 LRESULT CALLBACK\r
7510 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7511 {\r
7512   char move[MSG_SIZ];\r
7513   HWND hInput;\r
7514 \r
7515   switch (message) {\r
7516   case WM_INITDIALOG:\r
7517     move[0] = (char) lParam;\r
7518     move[1] = NULLCHAR;\r
7519     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7520     hInput = GetDlgItem(hDlg, OPT_Name);\r
7521     SetWindowText(hInput, move);\r
7522     SetFocus(hInput);\r
7523     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7524     return FALSE;\r
7525 \r
7526   case WM_COMMAND:\r
7527     switch (LOWORD(wParam)) {\r
7528     case IDOK:\r
7529       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7530       appData.userName = strdup(move);\r
7531       SetUserLogo();\r
7532 \r
7533       EndDialog(hDlg, TRUE);\r
7534       return TRUE;\r
7535     case IDCANCEL:\r
7536       EndDialog(hDlg, FALSE);\r
7537       return TRUE;\r
7538     default:\r
7539       break;\r
7540     }\r
7541     break;\r
7542   }\r
7543   return FALSE;\r
7544 }\r
7545 \r
7546 VOID\r
7547 PopUpNameDialog(char firstchar)\r
7548 {\r
7549     FARPROC lpProc;\r
7550     \r
7551       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7552       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7553         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7554       FreeProcInstance(lpProc);\r
7555 }\r
7556 \r
7557 /*---------------------------------------------------------------------------*\\r
7558  *\r
7559  *  Error dialogs\r
7560  * \r
7561 \*---------------------------------------------------------------------------*/\r
7562 \r
7563 /* Nonmodal error box */\r
7564 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7565                              WPARAM wParam, LPARAM lParam);\r
7566 \r
7567 VOID\r
7568 ErrorPopUp(char *title, char *content)\r
7569 {\r
7570   FARPROC lpProc;\r
7571   char *p, *q;\r
7572   BOOLEAN modal = hwndMain == NULL;\r
7573 \r
7574   p = content;\r
7575   q = errorMessage;\r
7576   while (*p) {\r
7577     if (*p == '\n') {\r
7578       if (modal) {\r
7579         *q++ = ' ';\r
7580         p++;\r
7581       } else {\r
7582         *q++ = '\r';\r
7583         *q++ = *p++;\r
7584       }\r
7585     } else {\r
7586       *q++ = *p++;\r
7587     }\r
7588   }\r
7589   *q = NULLCHAR;\r
7590   strncpy(errorTitle, title, sizeof(errorTitle));\r
7591   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7592   \r
7593   if (modal) {\r
7594     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7595   } else {\r
7596     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7597     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7598                  hwndMain, (DLGPROC)lpProc);\r
7599     FreeProcInstance(lpProc);\r
7600   }\r
7601 }\r
7602 \r
7603 VOID\r
7604 ErrorPopDown()\r
7605 {\r
7606   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7607   if (errorDialog == NULL) return;\r
7608   DestroyWindow(errorDialog);\r
7609   errorDialog = NULL;\r
7610 }\r
7611 \r
7612 LRESULT CALLBACK\r
7613 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7614 {\r
7615   HANDLE hwndText;\r
7616   RECT rChild;\r
7617 \r
7618   switch (message) {\r
7619   case WM_INITDIALOG:\r
7620     GetWindowRect(hDlg, &rChild);\r
7621 \r
7622     /*\r
7623     SetWindowPos(hDlg, NULL, rChild.left,\r
7624       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7625       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7626     */\r
7627 \r
7628     /* \r
7629         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7630         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7631         and it doesn't work when you resize the dialog.\r
7632         For now, just give it a default position.\r
7633     */\r
7634     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7635 \r
7636     errorDialog = hDlg;\r
7637     SetWindowText(hDlg, errorTitle);\r
7638     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7639     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7640     return FALSE;\r
7641 \r
7642   case WM_COMMAND:\r
7643     switch (LOWORD(wParam)) {\r
7644     case IDOK:\r
7645     case IDCANCEL:\r
7646       if (errorDialog == hDlg) errorDialog = NULL;\r
7647       DestroyWindow(hDlg);\r
7648       return TRUE;\r
7649 \r
7650     default:\r
7651       break;\r
7652     }\r
7653     break;\r
7654   }\r
7655   return FALSE;\r
7656 }\r
7657 \r
7658 #ifdef GOTHIC\r
7659 HWND gothicDialog = NULL;\r
7660 \r
7661 LRESULT CALLBACK\r
7662 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7663 {\r
7664   HANDLE hwndText;\r
7665   RECT rChild;\r
7666   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7667 \r
7668   switch (message) {\r
7669   case WM_INITDIALOG:\r
7670     GetWindowRect(hDlg, &rChild);\r
7671 \r
7672     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7673                                                              SWP_NOZORDER);\r
7674 \r
7675     /* \r
7676         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7677         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7678         and it doesn't work when you resize the dialog.\r
7679         For now, just give it a default position.\r
7680     */\r
7681     gothicDialog = hDlg;\r
7682     SetWindowText(hDlg, errorTitle);\r
7683     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7684     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7685     return FALSE;\r
7686 \r
7687   case WM_COMMAND:\r
7688     switch (LOWORD(wParam)) {\r
7689     case IDOK:\r
7690     case IDCANCEL:\r
7691       if (errorDialog == hDlg) errorDialog = NULL;\r
7692       DestroyWindow(hDlg);\r
7693       return TRUE;\r
7694 \r
7695     default:\r
7696       break;\r
7697     }\r
7698     break;\r
7699   }\r
7700   return FALSE;\r
7701 }\r
7702 \r
7703 VOID\r
7704 GothicPopUp(char *title, VariantClass variant)\r
7705 {\r
7706   FARPROC lpProc;\r
7707   static char *lastTitle;\r
7708 \r
7709   strncpy(errorTitle, title, sizeof(errorTitle));\r
7710   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7711 \r
7712   if(lastTitle != title && gothicDialog != NULL) {\r
7713     DestroyWindow(gothicDialog);\r
7714     gothicDialog = NULL;\r
7715   }\r
7716   if(variant != VariantNormal && gothicDialog == NULL) {\r
7717     title = lastTitle;\r
7718     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7719     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7720                  hwndMain, (DLGPROC)lpProc);\r
7721     FreeProcInstance(lpProc);\r
7722   }\r
7723 }\r
7724 #endif\r
7725 \r
7726 /*---------------------------------------------------------------------------*\\r
7727  *\r
7728  *  Ics Interaction console functions\r
7729  *\r
7730 \*---------------------------------------------------------------------------*/\r
7731 \r
7732 #define HISTORY_SIZE 64\r
7733 static char *history[HISTORY_SIZE];\r
7734 int histIn = 0, histP = 0;\r
7735 \r
7736 VOID\r
7737 SaveInHistory(char *cmd)\r
7738 {\r
7739   if (history[histIn] != NULL) {\r
7740     free(history[histIn]);\r
7741     history[histIn] = NULL;\r
7742   }\r
7743   if (*cmd == NULLCHAR) return;\r
7744   history[histIn] = StrSave(cmd);\r
7745   histIn = (histIn + 1) % HISTORY_SIZE;\r
7746   if (history[histIn] != NULL) {\r
7747     free(history[histIn]);\r
7748     history[histIn] = NULL;\r
7749   }\r
7750   histP = histIn;\r
7751 }\r
7752 \r
7753 char *\r
7754 PrevInHistory(char *cmd)\r
7755 {\r
7756   int newhp;\r
7757   if (histP == histIn) {\r
7758     if (history[histIn] != NULL) free(history[histIn]);\r
7759     history[histIn] = StrSave(cmd);\r
7760   }\r
7761   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7762   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7763   histP = newhp;\r
7764   return history[histP];\r
7765 }\r
7766 \r
7767 char *\r
7768 NextInHistory()\r
7769 {\r
7770   if (histP == histIn) return NULL;\r
7771   histP = (histP + 1) % HISTORY_SIZE;\r
7772   return history[histP];\r
7773 }\r
7774 \r
7775 typedef struct {\r
7776   char *item;\r
7777   char *command;\r
7778   BOOLEAN getname;\r
7779   BOOLEAN immediate;\r
7780 } IcsTextMenuEntry;\r
7781 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7782 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7783 \r
7784 void\r
7785 ParseIcsTextMenu(char *icsTextMenuString)\r
7786 {\r
7787 //  int flags = 0;\r
7788   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7789   char *p = icsTextMenuString;\r
7790   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7791     free(e->item);\r
7792     e->item = NULL;\r
7793     if (e->command != NULL) {\r
7794       free(e->command);\r
7795       e->command = NULL;\r
7796     }\r
7797     e++;\r
7798   }\r
7799   e = icsTextMenuEntry;\r
7800   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7801     if (*p == ';' || *p == '\n') {\r
7802       e->item = strdup("-");\r
7803       e->command = NULL;\r
7804       p++;\r
7805     } else if (*p == '-') {\r
7806       e->item = strdup("-");\r
7807       e->command = NULL;\r
7808       p++;\r
7809       if (*p) p++;\r
7810     } else {\r
7811       char *q, *r, *s, *t;\r
7812       char c;\r
7813       q = strchr(p, ',');\r
7814       if (q == NULL) break;\r
7815       *q = NULLCHAR;\r
7816       r = strchr(q + 1, ',');\r
7817       if (r == NULL) break;\r
7818       *r = NULLCHAR;\r
7819       s = strchr(r + 1, ',');\r
7820       if (s == NULL) break;\r
7821       *s = NULLCHAR;\r
7822       c = ';';\r
7823       t = strchr(s + 1, c);\r
7824       if (t == NULL) {\r
7825         c = '\n';\r
7826         t = strchr(s + 1, c);\r
7827       }\r
7828       if (t != NULL) *t = NULLCHAR;\r
7829       e->item = strdup(p);\r
7830       e->command = strdup(q + 1);\r
7831       e->getname = *(r + 1) != '0';\r
7832       e->immediate = *(s + 1) != '0';\r
7833       *q = ',';\r
7834       *r = ',';\r
7835       *s = ',';\r
7836       if (t == NULL) break;\r
7837       *t = c;\r
7838       p = t + 1;\r
7839     }\r
7840     e++;\r
7841   } \r
7842 }\r
7843 \r
7844 HMENU\r
7845 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7846 {\r
7847   HMENU hmenu, h;\r
7848   int i = 0;\r
7849   hmenu = LoadMenu(hInst, "TextMenu");\r
7850   h = GetSubMenu(hmenu, 0);\r
7851   while (e->item) {\r
7852     if (strcmp(e->item, "-") == 0) {\r
7853       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7854     } else {\r
7855       if (e->item[0] == '|') {\r
7856         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7857                    IDM_CommandX + i, &e->item[1]);\r
7858       } else {\r
7859         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7860       }\r
7861     }\r
7862     e++;\r
7863     i++;\r
7864   } \r
7865   return hmenu;\r
7866 }\r
7867 \r
7868 WNDPROC consoleTextWindowProc;\r
7869 \r
7870 void\r
7871 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7872 {\r
7873   char buf[MSG_SIZ], name[MSG_SIZ];\r
7874   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7875   CHARRANGE sel;\r
7876 \r
7877   if (!getname) {\r
7878     SetWindowText(hInput, command);\r
7879     if (immediate) {\r
7880       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7881     } else {\r
7882       sel.cpMin = 999999;\r
7883       sel.cpMax = 999999;\r
7884       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7885       SetFocus(hInput);\r
7886     }\r
7887     return;\r
7888   }    \r
7889   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7890   if (sel.cpMin == sel.cpMax) {\r
7891     /* Expand to surrounding word */\r
7892     TEXTRANGE tr;\r
7893     do {\r
7894       tr.chrg.cpMax = sel.cpMin;\r
7895       tr.chrg.cpMin = --sel.cpMin;\r
7896       if (sel.cpMin < 0) break;\r
7897       tr.lpstrText = name;\r
7898       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7899     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7900     sel.cpMin++;\r
7901 \r
7902     do {\r
7903       tr.chrg.cpMin = sel.cpMax;\r
7904       tr.chrg.cpMax = ++sel.cpMax;\r
7905       tr.lpstrText = name;\r
7906       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7907     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7908     sel.cpMax--;\r
7909 \r
7910     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7911       MessageBeep(MB_ICONEXCLAMATION);\r
7912       return;\r
7913     }\r
7914     tr.chrg = sel;\r
7915     tr.lpstrText = name;\r
7916     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7917   } else {\r
7918     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7919       MessageBeep(MB_ICONEXCLAMATION);\r
7920       return;\r
7921     }\r
7922     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7923   }\r
7924   if (immediate) {\r
7925     sprintf(buf, "%s %s", command, name);\r
7926     SetWindowText(hInput, buf);\r
7927     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7928   } else {\r
7929     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7930     SetWindowText(hInput, buf);\r
7931     sel.cpMin = 999999;\r
7932     sel.cpMax = 999999;\r
7933     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7934     SetFocus(hInput);\r
7935   }\r
7936 }\r
7937 \r
7938 LRESULT CALLBACK \r
7939 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7940 {\r
7941   HWND hInput;\r
7942   CHARRANGE sel;\r
7943 \r
7944   switch (message) {\r
7945   case WM_KEYDOWN:\r
7946     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7947     switch (wParam) {\r
7948     case VK_PRIOR:\r
7949       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7950       return 0;\r
7951     case VK_NEXT:\r
7952       sel.cpMin = 999999;\r
7953       sel.cpMax = 999999;\r
7954       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7955       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7956       return 0;\r
7957     }\r
7958     break;\r
7959   case WM_CHAR:\r
7960    if(wParam != '\022') {\r
7961     if (wParam == '\t') {\r
7962       if (GetKeyState(VK_SHIFT) < 0) {\r
7963         /* shifted */\r
7964         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7965         if (buttonDesc[0].hwnd) {\r
7966           SetFocus(buttonDesc[0].hwnd);\r
7967         } else {\r
7968           SetFocus(hwndMain);\r
7969         }\r
7970       } else {\r
7971         /* unshifted */\r
7972         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7973       }\r
7974     } else {\r
7975       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7976       JAWS_DELETE( SetFocus(hInput); )\r
7977       SendMessage(hInput, message, wParam, lParam);\r
7978     }\r
7979     return 0;\r
7980    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7981   case WM_RBUTTONUP:\r
7982     if (GetKeyState(VK_SHIFT) & ~1) {\r
7983       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7984         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7985     } else {\r
7986       POINT pt;\r
7987       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7988       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7989       if (sel.cpMin == sel.cpMax) {\r
7990         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7991         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7992       }\r
7993       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7994         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7995       }\r
7996       pt.x = LOWORD(lParam);\r
7997       pt.y = HIWORD(lParam);\r
7998       MenuPopup(hwnd, pt, hmenu, -1);\r
7999     }\r
8000     return 0;\r
8001   case WM_PASTE:\r
8002     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8003     SetFocus(hInput);\r
8004     return SendMessage(hInput, message, wParam, lParam);\r
8005   case WM_MBUTTONDOWN:\r
8006     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8007   case WM_RBUTTONDOWN:\r
8008     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
8009       /* Move selection here if it was empty */\r
8010       POINT pt;\r
8011       pt.x = LOWORD(lParam);\r
8012       pt.y = HIWORD(lParam);\r
8013       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8014       if (sel.cpMin == sel.cpMax) {\r
8015         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
8016         sel.cpMax = sel.cpMin;\r
8017         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8018       }\r
8019       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
8020     }\r
8021     return 0;\r
8022   case WM_COMMAND:\r
8023     switch (LOWORD(wParam)) {\r
8024     case IDM_QuickPaste:\r
8025       {\r
8026         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8027         if (sel.cpMin == sel.cpMax) {\r
8028           MessageBeep(MB_ICONEXCLAMATION);\r
8029           return 0;\r
8030         }\r
8031         SendMessage(hwnd, WM_COPY, 0, 0);\r
8032         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8033         SendMessage(hInput, WM_PASTE, 0, 0);\r
8034         SetFocus(hInput);\r
8035         return 0;\r
8036       }\r
8037     case IDM_Cut:\r
8038       SendMessage(hwnd, WM_CUT, 0, 0);\r
8039       return 0;\r
8040     case IDM_Paste:\r
8041       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8042       return 0;\r
8043     case IDM_Copy:\r
8044       SendMessage(hwnd, WM_COPY, 0, 0);\r
8045       return 0;\r
8046     default:\r
8047       {\r
8048         int i = LOWORD(wParam) - IDM_CommandX;\r
8049         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
8050             icsTextMenuEntry[i].command != NULL) {\r
8051           CommandX(hwnd, icsTextMenuEntry[i].command,\r
8052                    icsTextMenuEntry[i].getname,\r
8053                    icsTextMenuEntry[i].immediate);\r
8054           return 0;\r
8055         }\r
8056       }\r
8057       break;\r
8058     }\r
8059     break;\r
8060   }\r
8061   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
8062 }\r
8063 \r
8064 WNDPROC consoleInputWindowProc;\r
8065 \r
8066 LRESULT CALLBACK\r
8067 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8068 {\r
8069   char buf[MSG_SIZ];\r
8070   char *p;\r
8071   static BOOL sendNextChar = FALSE;\r
8072   static BOOL quoteNextChar = FALSE;\r
8073   InputSource *is = consoleInputSource;\r
8074   CHARFORMAT cf;\r
8075   CHARRANGE sel;\r
8076 \r
8077   switch (message) {\r
8078   case WM_CHAR:\r
8079     if (!appData.localLineEditing || sendNextChar) {\r
8080       is->buf[0] = (CHAR) wParam;\r
8081       is->count = 1;\r
8082       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8083       sendNextChar = FALSE;\r
8084       return 0;\r
8085     }\r
8086     if (quoteNextChar) {\r
8087       buf[0] = (char) wParam;\r
8088       buf[1] = NULLCHAR;\r
8089       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
8090       quoteNextChar = FALSE;\r
8091       return 0;\r
8092     }\r
8093     switch (wParam) {\r
8094     case '\r':   /* Enter key */\r
8095       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
8096       if (consoleEcho) SaveInHistory(is->buf);\r
8097       is->buf[is->count++] = '\n';\r
8098       is->buf[is->count] = NULLCHAR;\r
8099       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8100       if (consoleEcho) {\r
8101         ConsoleOutput(is->buf, is->count, TRUE);\r
8102       } else if (appData.localLineEditing) {\r
8103         ConsoleOutput("\n", 1, TRUE);\r
8104       }\r
8105       /* fall thru */\r
8106     case '\033': /* Escape key */\r
8107       SetWindowText(hwnd, "");\r
8108       cf.cbSize = sizeof(CHARFORMAT);\r
8109       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8110       if (consoleEcho) {\r
8111         cf.crTextColor = textAttribs[ColorNormal].color;\r
8112       } else {\r
8113         cf.crTextColor = COLOR_ECHOOFF;\r
8114       }\r
8115       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8116       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8117       return 0;\r
8118     case '\t':   /* Tab key */\r
8119       if (GetKeyState(VK_SHIFT) < 0) {\r
8120         /* shifted */\r
8121         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8122       } else {\r
8123         /* unshifted */\r
8124         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8125         if (buttonDesc[0].hwnd) {\r
8126           SetFocus(buttonDesc[0].hwnd);\r
8127         } else {\r
8128           SetFocus(hwndMain);\r
8129         }\r
8130       }\r
8131       return 0;\r
8132     case '\023': /* Ctrl+S */\r
8133       sendNextChar = TRUE;\r
8134       return 0;\r
8135     case '\021': /* Ctrl+Q */\r
8136       quoteNextChar = TRUE;\r
8137       return 0;\r
8138     JAWS_REPLAY\r
8139     default:\r
8140       break;\r
8141     }\r
8142     break;\r
8143   case WM_KEYDOWN:\r
8144     switch (wParam) {\r
8145     case VK_UP:\r
8146       GetWindowText(hwnd, buf, MSG_SIZ);\r
8147       p = PrevInHistory(buf);\r
8148       if (p != NULL) {\r
8149         SetWindowText(hwnd, p);\r
8150         sel.cpMin = 999999;\r
8151         sel.cpMax = 999999;\r
8152         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8153         return 0;\r
8154       }\r
8155       break;\r
8156     case VK_DOWN:\r
8157       p = NextInHistory();\r
8158       if (p != NULL) {\r
8159         SetWindowText(hwnd, p);\r
8160         sel.cpMin = 999999;\r
8161         sel.cpMax = 999999;\r
8162         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8163         return 0;\r
8164       }\r
8165       break;\r
8166     case VK_HOME:\r
8167     case VK_END:\r
8168       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8169       /* fall thru */\r
8170     case VK_PRIOR:\r
8171     case VK_NEXT:\r
8172       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8173       return 0;\r
8174     }\r
8175     break;\r
8176   case WM_MBUTTONDOWN:\r
8177     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8178       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8179     break;\r
8180   case WM_RBUTTONUP:\r
8181     if (GetKeyState(VK_SHIFT) & ~1) {\r
8182       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8183         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8184     } else {\r
8185       POINT pt;\r
8186       HMENU hmenu;\r
8187       hmenu = LoadMenu(hInst, "InputMenu");\r
8188       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8189       if (sel.cpMin == sel.cpMax) {\r
8190         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8191         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8192       }\r
8193       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8194         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8195       }\r
8196       pt.x = LOWORD(lParam);\r
8197       pt.y = HIWORD(lParam);\r
8198       MenuPopup(hwnd, pt, hmenu, -1);\r
8199     }\r
8200     return 0;\r
8201   case WM_COMMAND:\r
8202     switch (LOWORD(wParam)) { \r
8203     case IDM_Undo:\r
8204       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8205       return 0;\r
8206     case IDM_SelectAll:\r
8207       sel.cpMin = 0;\r
8208       sel.cpMax = -1; /*999999?*/\r
8209       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8210       return 0;\r
8211     case IDM_Cut:\r
8212       SendMessage(hwnd, WM_CUT, 0, 0);\r
8213       return 0;\r
8214     case IDM_Paste:\r
8215       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8216       return 0;\r
8217     case IDM_Copy:\r
8218       SendMessage(hwnd, WM_COPY, 0, 0);\r
8219       return 0;\r
8220     }\r
8221     break;\r
8222   }\r
8223   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8224 }\r
8225 \r
8226 #define CO_MAX  100000\r
8227 #define CO_TRIM   1000\r
8228 \r
8229 LRESULT CALLBACK\r
8230 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8231 {\r
8232   static SnapData sd;\r
8233   static HWND hText, hInput /*, hFocus*/;\r
8234 //  InputSource *is = consoleInputSource;\r
8235   RECT rect;\r
8236   static int sizeX, sizeY;\r
8237   int newSizeX, newSizeY;\r
8238   MINMAXINFO *mmi;\r
8239 \r
8240   switch (message) {\r
8241   case WM_INITDIALOG: /* message: initialize dialog box */\r
8242     hwndConsole = hDlg;\r
8243     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8244     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8245     SetFocus(hInput);\r
8246     consoleTextWindowProc = (WNDPROC)\r
8247       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8248     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8249     consoleInputWindowProc = (WNDPROC)\r
8250       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8251     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8252     Colorize(ColorNormal, TRUE);\r
8253     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8254     ChangedConsoleFont();\r
8255     GetClientRect(hDlg, &rect);\r
8256     sizeX = rect.right;\r
8257     sizeY = rect.bottom;\r
8258     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8259         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8260       WINDOWPLACEMENT wp;\r
8261       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8262       wp.length = sizeof(WINDOWPLACEMENT);\r
8263       wp.flags = 0;\r
8264       wp.showCmd = SW_SHOW;\r
8265       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8266       wp.rcNormalPosition.left = wpConsole.x;\r
8267       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8268       wp.rcNormalPosition.top = wpConsole.y;\r
8269       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8270       SetWindowPlacement(hDlg, &wp);\r
8271     }\r
8272 #if 1\r
8273    // [HGM] Chessknight's change 2004-07-13\r
8274    else { /* Determine Defaults */\r
8275        WINDOWPLACEMENT wp;\r
8276        wpConsole.x = winWidth + 1;\r
8277        wpConsole.y = boardY;\r
8278        wpConsole.width = screenWidth -  winWidth;\r
8279        wpConsole.height = winHeight;\r
8280        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8281        wp.length = sizeof(WINDOWPLACEMENT);\r
8282        wp.flags = 0;\r
8283        wp.showCmd = SW_SHOW;\r
8284        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8285        wp.rcNormalPosition.left = wpConsole.x;\r
8286        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8287        wp.rcNormalPosition.top = wpConsole.y;\r
8288        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8289        SetWindowPlacement(hDlg, &wp);\r
8290     }\r
8291 #endif\r
8292     return FALSE;\r
8293 \r
8294   case WM_SETFOCUS:\r
8295     SetFocus(hInput);\r
8296     return 0;\r
8297 \r
8298   case WM_CLOSE:\r
8299     ExitEvent(0);\r
8300     /* not reached */\r
8301     break;\r
8302 \r
8303   case WM_SIZE:\r
8304     if (IsIconic(hDlg)) break;\r
8305     newSizeX = LOWORD(lParam);\r
8306     newSizeY = HIWORD(lParam);\r
8307     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8308       RECT rectText, rectInput;\r
8309       POINT pt;\r
8310       int newTextHeight, newTextWidth;\r
8311       GetWindowRect(hText, &rectText);\r
8312       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8313       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8314       if (newTextHeight < 0) {\r
8315         newSizeY += -newTextHeight;\r
8316         newTextHeight = 0;\r
8317       }\r
8318       SetWindowPos(hText, NULL, 0, 0,\r
8319         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8320       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8321       pt.x = rectInput.left;\r
8322       pt.y = rectInput.top + newSizeY - sizeY;\r
8323       ScreenToClient(hDlg, &pt);\r
8324       SetWindowPos(hInput, NULL, \r
8325         pt.x, pt.y, /* needs client coords */   \r
8326         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8327         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8328     }\r
8329     sizeX = newSizeX;\r
8330     sizeY = newSizeY;\r
8331     break;\r
8332 \r
8333   case WM_GETMINMAXINFO:\r
8334     /* Prevent resizing window too small */\r
8335     mmi = (MINMAXINFO *) lParam;\r
8336     mmi->ptMinTrackSize.x = 100;\r
8337     mmi->ptMinTrackSize.y = 100;\r
8338     break;\r
8339 \r
8340   /* [AS] Snapping */\r
8341   case WM_ENTERSIZEMOVE:\r
8342     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8343 \r
8344   case WM_SIZING:\r
8345     return OnSizing( &sd, hDlg, wParam, lParam );\r
8346 \r
8347   case WM_MOVING:\r
8348     return OnMoving( &sd, hDlg, wParam, lParam );\r
8349 \r
8350   case WM_EXITSIZEMOVE:\r
8351     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8352   }\r
8353 \r
8354   return DefWindowProc(hDlg, message, wParam, lParam);\r
8355 }\r
8356 \r
8357 \r
8358 VOID\r
8359 ConsoleCreate()\r
8360 {\r
8361   HWND hCons;\r
8362   if (hwndConsole) return;\r
8363   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8364   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8365 }\r
8366 \r
8367 \r
8368 VOID\r
8369 ConsoleOutput(char* data, int length, int forceVisible)\r
8370 {\r
8371   HWND hText;\r
8372   int trim, exlen;\r
8373   char *p, *q;\r
8374   char buf[CO_MAX+1];\r
8375   POINT pEnd;\r
8376   RECT rect;\r
8377   static int delayLF = 0;\r
8378   CHARRANGE savesel, sel;\r
8379 \r
8380   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8381   p = data;\r
8382   q = buf;\r
8383   if (delayLF) {\r
8384     *q++ = '\r';\r
8385     *q++ = '\n';\r
8386     delayLF = 0;\r
8387   }\r
8388   while (length--) {\r
8389     if (*p == '\n') {\r
8390       if (*++p) {\r
8391         *q++ = '\r';\r
8392         *q++ = '\n';\r
8393       } else {\r
8394         delayLF = 1;\r
8395       }\r
8396     } else if (*p == '\007') {\r
8397        MyPlaySound(&sounds[(int)SoundBell]);\r
8398        p++;\r
8399     } else {\r
8400       *q++ = *p++;\r
8401     }\r
8402   }\r
8403   *q = NULLCHAR;\r
8404   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8405   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8406   /* Save current selection */\r
8407   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8408   exlen = GetWindowTextLength(hText);\r
8409   /* Find out whether current end of text is visible */\r
8410   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8411   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8412   /* Trim existing text if it's too long */\r
8413   if (exlen + (q - buf) > CO_MAX) {\r
8414     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8415     sel.cpMin = 0;\r
8416     sel.cpMax = trim;\r
8417     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8418     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8419     exlen -= trim;\r
8420     savesel.cpMin -= trim;\r
8421     savesel.cpMax -= trim;\r
8422     if (exlen < 0) exlen = 0;\r
8423     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8424     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8425   }\r
8426   /* Append the new text */\r
8427   sel.cpMin = exlen;\r
8428   sel.cpMax = exlen;\r
8429   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8430   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8431   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8432   if (forceVisible || exlen == 0 ||\r
8433       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8434        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8435     /* Scroll to make new end of text visible if old end of text\r
8436        was visible or new text is an echo of user typein */\r
8437     sel.cpMin = 9999999;\r
8438     sel.cpMax = 9999999;\r
8439     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8440     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8441     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8442     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8443   }\r
8444   if (savesel.cpMax == exlen || forceVisible) {\r
8445     /* Move insert point to new end of text if it was at the old\r
8446        end of text or if the new text is an echo of user typein */\r
8447     sel.cpMin = 9999999;\r
8448     sel.cpMax = 9999999;\r
8449     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8450   } else {\r
8451     /* Restore previous selection */\r
8452     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8453   }\r
8454   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8455 }\r
8456 \r
8457 /*---------*/\r
8458 \r
8459 \r
8460 void\r
8461 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8462 {\r
8463   char buf[100];\r
8464   char *str;\r
8465   COLORREF oldFg, oldBg;\r
8466   HFONT oldFont;\r
8467   RECT rect;\r
8468 \r
8469   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8470 \r
8471   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8472   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8473   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8474 \r
8475   rect.left = x;\r
8476   rect.right = x + squareSize;\r
8477   rect.top  = y;\r
8478   rect.bottom = y + squareSize;\r
8479   str = buf;\r
8480 \r
8481   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8482                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8483              y, ETO_CLIPPED|ETO_OPAQUE,\r
8484              &rect, str, strlen(str), NULL);\r
8485 \r
8486   (void) SetTextColor(hdc, oldFg);\r
8487   (void) SetBkColor(hdc, oldBg);\r
8488   (void) SelectObject(hdc, oldFont);\r
8489 }\r
8490 \r
8491 void\r
8492 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8493               RECT *rect, char *color, char *flagFell)\r
8494 {\r
8495   char buf[100];\r
8496   char *str;\r
8497   COLORREF oldFg, oldBg;\r
8498   HFONT oldFont;\r
8499 \r
8500   if (appData.clockMode) {\r
8501     if (tinyLayout)\r
8502       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8503     else\r
8504       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8505     str = buf;\r
8506   } else {\r
8507     str = color;\r
8508   }\r
8509 \r
8510   if (highlight) {\r
8511     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8512     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8513   } else {\r
8514     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8515     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8516   }\r
8517   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8518 \r
8519   JAWS_SILENCE\r
8520 \r
8521   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8522              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8523              rect, str, strlen(str), NULL);\r
8524   if(logoHeight > 0 && appData.clockMode) {\r
8525       RECT r;\r
8526       sprintf(buf, "%s %s", buf+7, flagFell);\r
8527       r.top = rect->top + logoHeight/2;\r
8528       r.left = rect->left;\r
8529       r.right = rect->right;\r
8530       r.bottom = rect->bottom;\r
8531       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8532                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8533                  &r, str, strlen(str), NULL);\r
8534   }\r
8535   (void) SetTextColor(hdc, oldFg);\r
8536   (void) SetBkColor(hdc, oldBg);\r
8537   (void) SelectObject(hdc, oldFont);\r
8538 }\r
8539 \r
8540 \r
8541 int\r
8542 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8543            OVERLAPPED *ovl)\r
8544 {\r
8545   int ok, err;\r
8546 \r
8547   /* [AS]  */\r
8548   if( count <= 0 ) {\r
8549     if (appData.debugMode) {\r
8550       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8551     }\r
8552 \r
8553     return ERROR_INVALID_USER_BUFFER;\r
8554   }\r
8555 \r
8556   ResetEvent(ovl->hEvent);\r
8557   ovl->Offset = ovl->OffsetHigh = 0;\r
8558   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8559   if (ok) {\r
8560     err = NO_ERROR;\r
8561   } else {\r
8562     err = GetLastError();\r
8563     if (err == ERROR_IO_PENDING) {\r
8564       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8565       if (ok)\r
8566         err = NO_ERROR;\r
8567       else\r
8568         err = GetLastError();\r
8569     }\r
8570   }\r
8571   return err;\r
8572 }\r
8573 \r
8574 int\r
8575 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8576             OVERLAPPED *ovl)\r
8577 {\r
8578   int ok, err;\r
8579 \r
8580   ResetEvent(ovl->hEvent);\r
8581   ovl->Offset = ovl->OffsetHigh = 0;\r
8582   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8583   if (ok) {\r
8584     err = NO_ERROR;\r
8585   } else {\r
8586     err = GetLastError();\r
8587     if (err == ERROR_IO_PENDING) {\r
8588       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8589       if (ok)\r
8590         err = NO_ERROR;\r
8591       else\r
8592         err = GetLastError();\r
8593     }\r
8594   }\r
8595   return err;\r
8596 }\r
8597 \r
8598 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8599 void CheckForInputBufferFull( InputSource * is )\r
8600 {\r
8601     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8602         /* Look for end of line */\r
8603         char * p = is->buf;\r
8604         \r
8605         while( p < is->next && *p != '\n' ) {\r
8606             p++;\r
8607         }\r
8608 \r
8609         if( p >= is->next ) {\r
8610             if (appData.debugMode) {\r
8611                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8612             }\r
8613 \r
8614             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8615             is->count = (DWORD) -1;\r
8616             is->next = is->buf;\r
8617         }\r
8618     }\r
8619 }\r
8620 \r
8621 DWORD\r
8622 InputThread(LPVOID arg)\r
8623 {\r
8624   InputSource *is;\r
8625   OVERLAPPED ovl;\r
8626 \r
8627   is = (InputSource *) arg;\r
8628   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8629   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8630   while (is->hThread != NULL) {\r
8631     is->error = DoReadFile(is->hFile, is->next,\r
8632                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8633                            &is->count, &ovl);\r
8634     if (is->error == NO_ERROR) {\r
8635       is->next += is->count;\r
8636     } else {\r
8637       if (is->error == ERROR_BROKEN_PIPE) {\r
8638         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8639         is->count = 0;\r
8640       } else {\r
8641         is->count = (DWORD) -1;\r
8642         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8643         break; \r
8644       }\r
8645     }\r
8646 \r
8647     CheckForInputBufferFull( is );\r
8648 \r
8649     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8650 \r
8651     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8652 \r
8653     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8654   }\r
8655 \r
8656   CloseHandle(ovl.hEvent);\r
8657   CloseHandle(is->hFile);\r
8658 \r
8659   if (appData.debugMode) {\r
8660     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8661   }\r
8662 \r
8663   return 0;\r
8664 }\r
8665 \r
8666 \r
8667 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8668 DWORD\r
8669 NonOvlInputThread(LPVOID arg)\r
8670 {\r
8671   InputSource *is;\r
8672   char *p, *q;\r
8673   int i;\r
8674   char prev;\r
8675 \r
8676   is = (InputSource *) arg;\r
8677   while (is->hThread != NULL) {\r
8678     is->error = ReadFile(is->hFile, is->next,\r
8679                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8680                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8681     if (is->error == NO_ERROR) {\r
8682       /* Change CRLF to LF */\r
8683       if (is->next > is->buf) {\r
8684         p = is->next - 1;\r
8685         i = is->count + 1;\r
8686       } else {\r
8687         p = is->next;\r
8688         i = is->count;\r
8689       }\r
8690       q = p;\r
8691       prev = NULLCHAR;\r
8692       while (i > 0) {\r
8693         if (prev == '\r' && *p == '\n') {\r
8694           *(q-1) = '\n';\r
8695           is->count--;\r
8696         } else { \r
8697           *q++ = *p;\r
8698         }\r
8699         prev = *p++;\r
8700         i--;\r
8701       }\r
8702       *q = NULLCHAR;\r
8703       is->next = q;\r
8704     } else {\r
8705       if (is->error == ERROR_BROKEN_PIPE) {\r
8706         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8707         is->count = 0; \r
8708       } else {\r
8709         is->count = (DWORD) -1;\r
8710       }\r
8711     }\r
8712 \r
8713     CheckForInputBufferFull( is );\r
8714 \r
8715     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8716 \r
8717     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8718 \r
8719     if (is->count < 0) break;  /* Quit on error */\r
8720   }\r
8721   CloseHandle(is->hFile);\r
8722   return 0;\r
8723 }\r
8724 \r
8725 DWORD\r
8726 SocketInputThread(LPVOID arg)\r
8727 {\r
8728   InputSource *is;\r
8729 \r
8730   is = (InputSource *) arg;\r
8731   while (is->hThread != NULL) {\r
8732     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8733     if ((int)is->count == SOCKET_ERROR) {\r
8734       is->count = (DWORD) -1;\r
8735       is->error = WSAGetLastError();\r
8736     } else {\r
8737       is->error = NO_ERROR;\r
8738       is->next += is->count;\r
8739       if (is->count == 0 && is->second == is) {\r
8740         /* End of file on stderr; quit with no message */\r
8741         break;\r
8742       }\r
8743     }\r
8744     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8745 \r
8746     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8747 \r
8748     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8749   }\r
8750   return 0;\r
8751 }\r
8752 \r
8753 VOID\r
8754 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8755 {\r
8756   InputSource *is;\r
8757 \r
8758   is = (InputSource *) lParam;\r
8759   if (is->lineByLine) {\r
8760     /* Feed in lines one by one */\r
8761     char *p = is->buf;\r
8762     char *q = p;\r
8763     while (q < is->next) {\r
8764       if (*q++ == '\n') {\r
8765         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8766         p = q;\r
8767       }\r
8768     }\r
8769     \r
8770     /* Move any partial line to the start of the buffer */\r
8771     q = is->buf;\r
8772     while (p < is->next) {\r
8773       *q++ = *p++;\r
8774     }\r
8775     is->next = q;\r
8776 \r
8777     if (is->error != NO_ERROR || is->count == 0) {\r
8778       /* Notify backend of the error.  Note: If there was a partial\r
8779          line at the end, it is not flushed through. */\r
8780       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8781     }\r
8782   } else {\r
8783     /* Feed in the whole chunk of input at once */\r
8784     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8785     is->next = is->buf;\r
8786   }\r
8787 }\r
8788 \r
8789 /*---------------------------------------------------------------------------*\\r
8790  *\r
8791  *  Menu enables. Used when setting various modes.\r
8792  *\r
8793 \*---------------------------------------------------------------------------*/\r
8794 \r
8795 typedef struct {\r
8796   int item;\r
8797   int flags;\r
8798 } Enables;\r
8799 \r
8800 VOID\r
8801 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8802 {\r
8803   while (enab->item > 0) {\r
8804     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8805     enab++;\r
8806   }\r
8807 }\r
8808 \r
8809 Enables gnuEnables[] = {\r
8810   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8811   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8812   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8813   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8814   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8815   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8816   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8817   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8818   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8819   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8820   { -1, -1 }\r
8821 };\r
8822 \r
8823 Enables icsEnables[] = {\r
8824   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8825   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8826   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8827   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8828   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8829   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8830   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8831   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8832   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8833   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8834   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8835   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8836   { -1, -1 }\r
8837 };\r
8838 \r
8839 #ifdef ZIPPY\r
8840 Enables zippyEnables[] = {\r
8841   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8842   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8843   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8844   { -1, -1 }\r
8845 };\r
8846 #endif\r
8847 \r
8848 Enables ncpEnables[] = {\r
8849   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8850   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8851   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8852   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8853   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8854   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8855   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8856   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8857   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8858   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8859   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8860   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8861   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8862   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8863   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8864   { -1, -1 }\r
8865 };\r
8866 \r
8867 Enables trainingOnEnables[] = {\r
8868   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8869   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8870   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8871   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8872   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8873   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8874   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8875   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8876   { -1, -1 }\r
8877 };\r
8878 \r
8879 Enables trainingOffEnables[] = {\r
8880   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8881   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8882   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8883   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8884   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8885   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8886   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8887   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8888   { -1, -1 }\r
8889 };\r
8890 \r
8891 /* These modify either ncpEnables or gnuEnables */\r
8892 Enables cmailEnables[] = {\r
8893   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8894   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8895   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8896   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8897   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8898   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8899   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8900   { -1, -1 }\r
8901 };\r
8902 \r
8903 Enables machineThinkingEnables[] = {\r
8904   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8905   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8906   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8907   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8908   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8909   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8910   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8911   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8912   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8913   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8914   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8915   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8916   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8917   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8918   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8919   { -1, -1 }\r
8920 };\r
8921 \r
8922 Enables userThinkingEnables[] = {\r
8923   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8924   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8925   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8926   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8927   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8928   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8929   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8930   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8931   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8932   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8933   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8934   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8935   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8936   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8937   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8938   { -1, -1 }\r
8939 };\r
8940 \r
8941 /*---------------------------------------------------------------------------*\\r
8942  *\r
8943  *  Front-end interface functions exported by XBoard.\r
8944  *  Functions appear in same order as prototypes in frontend.h.\r
8945  * \r
8946 \*---------------------------------------------------------------------------*/\r
8947 VOID\r
8948 ModeHighlight()\r
8949 {\r
8950   static UINT prevChecked = 0;\r
8951   static int prevPausing = 0;\r
8952   UINT nowChecked;\r
8953 \r
8954   if (pausing != prevPausing) {\r
8955     prevPausing = pausing;\r
8956     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8957                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8958     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8959   }\r
8960 \r
8961   switch (gameMode) {\r
8962   case BeginningOfGame:\r
8963     if (appData.icsActive)\r
8964       nowChecked = IDM_IcsClient;\r
8965     else if (appData.noChessProgram)\r
8966       nowChecked = IDM_EditGame;\r
8967     else\r
8968       nowChecked = IDM_MachineBlack;\r
8969     break;\r
8970   case MachinePlaysBlack:\r
8971     nowChecked = IDM_MachineBlack;\r
8972     break;\r
8973   case MachinePlaysWhite:\r
8974     nowChecked = IDM_MachineWhite;\r
8975     break;\r
8976   case TwoMachinesPlay:\r
8977     nowChecked = IDM_TwoMachines;\r
8978     break;\r
8979   case AnalyzeMode:\r
8980     nowChecked = IDM_AnalysisMode;\r
8981     break;\r
8982   case AnalyzeFile:\r
8983     nowChecked = IDM_AnalyzeFile;\r
8984     break;\r
8985   case EditGame:\r
8986     nowChecked = IDM_EditGame;\r
8987     break;\r
8988   case PlayFromGameFile:\r
8989     nowChecked = IDM_LoadGame;\r
8990     break;\r
8991   case EditPosition:\r
8992     nowChecked = IDM_EditPosition;\r
8993     break;\r
8994   case Training:\r
8995     nowChecked = IDM_Training;\r
8996     break;\r
8997   case IcsPlayingWhite:\r
8998   case IcsPlayingBlack:\r
8999   case IcsObserving:\r
9000   case IcsIdle:\r
9001     nowChecked = IDM_IcsClient;\r
9002     break;\r
9003   default:\r
9004   case EndOfGame:\r
9005     nowChecked = 0;\r
9006     break;\r
9007   }\r
9008   if (prevChecked != 0)\r
9009     (void) CheckMenuItem(GetMenu(hwndMain),\r
9010                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
9011   if (nowChecked != 0)\r
9012     (void) CheckMenuItem(GetMenu(hwndMain),\r
9013                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
9014 \r
9015   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
9016     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
9017                           MF_BYCOMMAND|MF_ENABLED);\r
9018   } else {\r
9019     (void) EnableMenuItem(GetMenu(hwndMain), \r
9020                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
9021   }\r
9022 \r
9023   prevChecked = nowChecked;\r
9024 \r
9025   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
9026   if (appData.icsActive) {\r
9027        if (appData.icsEngineAnalyze) {\r
9028                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9029                        MF_BYCOMMAND|MF_CHECKED);\r
9030        } else {\r
9031                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9032                        MF_BYCOMMAND|MF_UNCHECKED);\r
9033        }\r
9034   }\r
9035 }\r
9036 \r
9037 VOID\r
9038 SetICSMode()\r
9039 {\r
9040   HMENU hmenu = GetMenu(hwndMain);\r
9041   SetMenuEnables(hmenu, icsEnables);\r
9042   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
9043     MF_BYPOSITION|MF_ENABLED);\r
9044 #ifdef ZIPPY\r
9045   if (appData.zippyPlay) {\r
9046     SetMenuEnables(hmenu, zippyEnables);\r
9047     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
9048          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9049           MF_BYCOMMAND|MF_ENABLED);\r
9050   }\r
9051 #endif\r
9052 }\r
9053 \r
9054 VOID\r
9055 SetGNUMode()\r
9056 {\r
9057   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
9058 }\r
9059 \r
9060 VOID\r
9061 SetNCPMode()\r
9062 {\r
9063   HMENU hmenu = GetMenu(hwndMain);\r
9064   SetMenuEnables(hmenu, ncpEnables);\r
9065   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
9066     MF_BYPOSITION|MF_GRAYED);\r
9067     DrawMenuBar(hwndMain);\r
9068 }\r
9069 \r
9070 VOID\r
9071 SetCmailMode()\r
9072 {\r
9073   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9074 }\r
9075 \r
9076 VOID \r
9077 SetTrainingModeOn()\r
9078 {\r
9079   int i;\r
9080   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9081   for (i = 0; i < N_BUTTONS; i++) {\r
9082     if (buttonDesc[i].hwnd != NULL)\r
9083       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9084   }\r
9085   CommentPopDown();\r
9086 }\r
9087 \r
9088 VOID SetTrainingModeOff()\r
9089 {\r
9090   int i;\r
9091   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9092   for (i = 0; i < N_BUTTONS; i++) {\r
9093     if (buttonDesc[i].hwnd != NULL)\r
9094       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9095   }\r
9096 }\r
9097 \r
9098 \r
9099 VOID\r
9100 SetUserThinkingEnables()\r
9101 {\r
9102   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9103 }\r
9104 \r
9105 VOID\r
9106 SetMachineThinkingEnables()\r
9107 {\r
9108   HMENU hMenu = GetMenu(hwndMain);\r
9109   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9110 \r
9111   SetMenuEnables(hMenu, machineThinkingEnables);\r
9112 \r
9113   if (gameMode == MachinePlaysBlack) {\r
9114     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9115   } else if (gameMode == MachinePlaysWhite) {\r
9116     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9117   } else if (gameMode == TwoMachinesPlay) {\r
9118     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9119   }\r
9120 }\r
9121 \r
9122 \r
9123 VOID\r
9124 DisplayTitle(char *str)\r
9125 {\r
9126   char title[MSG_SIZ], *host;\r
9127   if (str[0] != NULLCHAR) {\r
9128     strcpy(title, str);\r
9129   } else if (appData.icsActive) {\r
9130     if (appData.icsCommPort[0] != NULLCHAR)\r
9131       host = "ICS";\r
9132     else \r
9133       host = appData.icsHost;\r
9134     sprintf(title, "%s: %s", szTitle, host);\r
9135   } else if (appData.noChessProgram) {\r
9136     strcpy(title, szTitle);\r
9137   } else {\r
9138     strcpy(title, szTitle);\r
9139     strcat(title, ": ");\r
9140     strcat(title, first.tidy);\r
9141   }\r
9142   SetWindowText(hwndMain, title);\r
9143 }\r
9144 \r
9145 \r
9146 VOID\r
9147 DisplayMessage(char *str1, char *str2)\r
9148 {\r
9149   HDC hdc;\r
9150   HFONT oldFont;\r
9151   int remain = MESSAGE_TEXT_MAX - 1;\r
9152   int len;\r
9153 \r
9154   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9155   messageText[0] = NULLCHAR;\r
9156   if (*str1) {\r
9157     len = strlen(str1);\r
9158     if (len > remain) len = remain;\r
9159     strncpy(messageText, str1, len);\r
9160     messageText[len] = NULLCHAR;\r
9161     remain -= len;\r
9162   }\r
9163   if (*str2 && remain >= 2) {\r
9164     if (*str1) {\r
9165       strcat(messageText, "  ");\r
9166       remain -= 2;\r
9167     }\r
9168     len = strlen(str2);\r
9169     if (len > remain) len = remain;\r
9170     strncat(messageText, str2, len);\r
9171   }\r
9172   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9173 \r
9174   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9175 \r
9176   SAYMACHINEMOVE();\r
9177 \r
9178   hdc = GetDC(hwndMain);\r
9179   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9180   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9181              &messageRect, messageText, strlen(messageText), NULL);\r
9182   (void) SelectObject(hdc, oldFont);\r
9183   (void) ReleaseDC(hwndMain, hdc);\r
9184 }\r
9185 \r
9186 VOID\r
9187 DisplayError(char *str, int error)\r
9188 {\r
9189   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9190   int len;\r
9191 \r
9192   if (error == 0) {\r
9193     strcpy(buf, str);\r
9194   } else {\r
9195     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9196                         NULL, error, LANG_NEUTRAL,\r
9197                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9198     if (len > 0) {\r
9199       sprintf(buf, "%s:\n%s", str, buf2);\r
9200     } else {\r
9201       ErrorMap *em = errmap;\r
9202       while (em->err != 0 && em->err != error) em++;\r
9203       if (em->err != 0) {\r
9204         sprintf(buf, "%s:\n%s", str, em->msg);\r
9205       } else {\r
9206         sprintf(buf, "%s:\nError code %d", str, error);\r
9207       }\r
9208     }\r
9209   }\r
9210   \r
9211   ErrorPopUp("Error", buf);\r
9212 }\r
9213 \r
9214 \r
9215 VOID\r
9216 DisplayMoveError(char *str)\r
9217 {\r
9218   fromX = fromY = -1;\r
9219   ClearHighlights();\r
9220   DrawPosition(FALSE, NULL);\r
9221   if (appData.popupMoveErrors) {\r
9222     ErrorPopUp("Error", str);\r
9223   } else {\r
9224     DisplayMessage(str, "");\r
9225     moveErrorMessageUp = TRUE;\r
9226   }\r
9227 }\r
9228 \r
9229 VOID\r
9230 DisplayFatalError(char *str, int error, int exitStatus)\r
9231 {\r
9232   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9233   int len;\r
9234   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9235 \r
9236   if (error != 0) {\r
9237     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9238                         NULL, error, LANG_NEUTRAL,\r
9239                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9240     if (len > 0) {\r
9241       sprintf(buf, "%s:\n%s", str, buf2);\r
9242     } else {\r
9243       ErrorMap *em = errmap;\r
9244       while (em->err != 0 && em->err != error) em++;\r
9245       if (em->err != 0) {\r
9246         sprintf(buf, "%s:\n%s", str, em->msg);\r
9247       } else {\r
9248         sprintf(buf, "%s:\nError code %d", str, error);\r
9249       }\r
9250     }\r
9251     str = buf;\r
9252   }\r
9253   if (appData.debugMode) {\r
9254     fprintf(debugFP, "%s: %s\n", label, str);\r
9255   }\r
9256   if (appData.popupExitMessage) {\r
9257     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9258                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9259   }\r
9260   ExitEvent(exitStatus);\r
9261 }\r
9262 \r
9263 \r
9264 VOID\r
9265 DisplayInformation(char *str)\r
9266 {\r
9267   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9268 }\r
9269 \r
9270 \r
9271 VOID\r
9272 DisplayNote(char *str)\r
9273 {\r
9274   ErrorPopUp("Note", str);\r
9275 }\r
9276 \r
9277 \r
9278 typedef struct {\r
9279   char *title, *question, *replyPrefix;\r
9280   ProcRef pr;\r
9281 } QuestionParams;\r
9282 \r
9283 LRESULT CALLBACK\r
9284 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9285 {\r
9286   static QuestionParams *qp;\r
9287   char reply[MSG_SIZ];\r
9288   int len, err;\r
9289 \r
9290   switch (message) {\r
9291   case WM_INITDIALOG:\r
9292     qp = (QuestionParams *) lParam;\r
9293     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9294     SetWindowText(hDlg, qp->title);\r
9295     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9296     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9297     return FALSE;\r
9298 \r
9299   case WM_COMMAND:\r
9300     switch (LOWORD(wParam)) {\r
9301     case IDOK:\r
9302       strcpy(reply, qp->replyPrefix);\r
9303       if (*reply) strcat(reply, " ");\r
9304       len = strlen(reply);\r
9305       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9306       strcat(reply, "\n");\r
9307       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9308       EndDialog(hDlg, TRUE);\r
9309       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9310       return TRUE;\r
9311     case IDCANCEL:\r
9312       EndDialog(hDlg, FALSE);\r
9313       return TRUE;\r
9314     default:\r
9315       break;\r
9316     }\r
9317     break;\r
9318   }\r
9319   return FALSE;\r
9320 }\r
9321 \r
9322 VOID\r
9323 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9324 {\r
9325     QuestionParams qp;\r
9326     FARPROC lpProc;\r
9327     \r
9328     qp.title = title;\r
9329     qp.question = question;\r
9330     qp.replyPrefix = replyPrefix;\r
9331     qp.pr = pr;\r
9332     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9333     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9334       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9335     FreeProcInstance(lpProc);\r
9336 }\r
9337 \r
9338 /* [AS] Pick FRC position */\r
9339 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9340 {\r
9341     static int * lpIndexFRC;\r
9342     BOOL index_is_ok;\r
9343     char buf[16];\r
9344 \r
9345     switch( message )\r
9346     {\r
9347     case WM_INITDIALOG:\r
9348         lpIndexFRC = (int *) lParam;\r
9349 \r
9350         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9351 \r
9352         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9353         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9354         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9355         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9356 \r
9357         break;\r
9358 \r
9359     case WM_COMMAND:\r
9360         switch( LOWORD(wParam) ) {\r
9361         case IDOK:\r
9362             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9363             EndDialog( hDlg, 0 );\r
9364             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9365             return TRUE;\r
9366         case IDCANCEL:\r
9367             EndDialog( hDlg, 1 );   \r
9368             return TRUE;\r
9369         case IDC_NFG_Edit:\r
9370             if( HIWORD(wParam) == EN_CHANGE ) {\r
9371                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9372 \r
9373                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9374             }\r
9375             return TRUE;\r
9376         case IDC_NFG_Random:\r
9377             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9378             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9379             return TRUE;\r
9380         }\r
9381 \r
9382         break;\r
9383     }\r
9384 \r
9385     return FALSE;\r
9386 }\r
9387 \r
9388 int NewGameFRC()\r
9389 {\r
9390     int result;\r
9391     int index = appData.defaultFrcPosition;\r
9392     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9393 \r
9394     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9395 \r
9396     if( result == 0 ) {\r
9397         appData.defaultFrcPosition = index;\r
9398     }\r
9399 \r
9400     return result;\r
9401 }\r
9402 \r
9403 /* [AS] Game list options */\r
9404 typedef struct {\r
9405     char id;\r
9406     char * name;\r
9407 } GLT_Item;\r
9408 \r
9409 static GLT_Item GLT_ItemInfo[] = {\r
9410     { GLT_EVENT,      "Event" },\r
9411     { GLT_SITE,       "Site" },\r
9412     { GLT_DATE,       "Date" },\r
9413     { GLT_ROUND,      "Round" },\r
9414     { GLT_PLAYERS,    "Players" },\r
9415     { GLT_RESULT,     "Result" },\r
9416     { GLT_WHITE_ELO,  "White Rating" },\r
9417     { GLT_BLACK_ELO,  "Black Rating" },\r
9418     { GLT_TIME_CONTROL,"Time Control" },\r
9419     { GLT_VARIANT,    "Variant" },\r
9420     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9421     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9422     { 0, 0 }\r
9423 };\r
9424 \r
9425 const char * GLT_FindItem( char id )\r
9426 {\r
9427     const char * result = 0;\r
9428 \r
9429     GLT_Item * list = GLT_ItemInfo;\r
9430 \r
9431     while( list->id != 0 ) {\r
9432         if( list->id == id ) {\r
9433             result = list->name;\r
9434             break;\r
9435         }\r
9436 \r
9437         list++;\r
9438     }\r
9439 \r
9440     return result;\r
9441 }\r
9442 \r
9443 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9444 {\r
9445     const char * name = GLT_FindItem( id );\r
9446 \r
9447     if( name != 0 ) {\r
9448         if( index >= 0 ) {\r
9449             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9450         }\r
9451         else {\r
9452             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9453         }\r
9454     }\r
9455 }\r
9456 \r
9457 void GLT_TagsToList( HWND hDlg, char * tags )\r
9458 {\r
9459     char * pc = tags;\r
9460 \r
9461     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9462 \r
9463     while( *pc ) {\r
9464         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9465         pc++;\r
9466     }\r
9467 \r
9468     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9469 \r
9470     pc = GLT_ALL_TAGS;\r
9471 \r
9472     while( *pc ) {\r
9473         if( strchr( tags, *pc ) == 0 ) {\r
9474             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9475         }\r
9476         pc++;\r
9477     }\r
9478 \r
9479     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9480 }\r
9481 \r
9482 char GLT_ListItemToTag( HWND hDlg, int index )\r
9483 {\r
9484     char result = '\0';\r
9485     char name[128];\r
9486 \r
9487     GLT_Item * list = GLT_ItemInfo;\r
9488 \r
9489     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9490         while( list->id != 0 ) {\r
9491             if( strcmp( list->name, name ) == 0 ) {\r
9492                 result = list->id;\r
9493                 break;\r
9494             }\r
9495 \r
9496             list++;\r
9497         }\r
9498     }\r
9499 \r
9500     return result;\r
9501 }\r
9502 \r
9503 void GLT_MoveSelection( HWND hDlg, int delta )\r
9504 {\r
9505     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9506     int idx2 = idx1 + delta;\r
9507     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9508 \r
9509     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9510         char buf[128];\r
9511 \r
9512         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9513         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9514         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9515         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9516     }\r
9517 }\r
9518 \r
9519 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9520 {\r
9521     static char glt[64];\r
9522     static char * lpUserGLT;\r
9523 \r
9524     switch( message )\r
9525     {\r
9526     case WM_INITDIALOG:\r
9527         lpUserGLT = (char *) lParam;\r
9528         \r
9529         strcpy( glt, lpUserGLT );\r
9530 \r
9531         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9532 \r
9533         /* Initialize list */\r
9534         GLT_TagsToList( hDlg, glt );\r
9535 \r
9536         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9537 \r
9538         break;\r
9539 \r
9540     case WM_COMMAND:\r
9541         switch( LOWORD(wParam) ) {\r
9542         case IDOK:\r
9543             {\r
9544                 char * pc = lpUserGLT;\r
9545                 int idx = 0;\r
9546 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9547                 char id;\r
9548 \r
9549                 do {\r
9550                     id = GLT_ListItemToTag( hDlg, idx );\r
9551 \r
9552                     *pc++ = id;\r
9553                     idx++;\r
9554                 } while( id != '\0' );\r
9555             }\r
9556             EndDialog( hDlg, 0 );\r
9557             return TRUE;\r
9558         case IDCANCEL:\r
9559             EndDialog( hDlg, 1 );\r
9560             return TRUE;\r
9561 \r
9562         case IDC_GLT_Default:\r
9563             strcpy( glt, GLT_DEFAULT_TAGS );\r
9564             GLT_TagsToList( hDlg, glt );\r
9565             return TRUE;\r
9566 \r
9567         case IDC_GLT_Restore:\r
9568             strcpy( glt, lpUserGLT );\r
9569             GLT_TagsToList( hDlg, glt );\r
9570             return TRUE;\r
9571 \r
9572         case IDC_GLT_Up:\r
9573             GLT_MoveSelection( hDlg, -1 );\r
9574             return TRUE;\r
9575 \r
9576         case IDC_GLT_Down:\r
9577             GLT_MoveSelection( hDlg, +1 );\r
9578             return TRUE;\r
9579         }\r
9580 \r
9581         break;\r
9582     }\r
9583 \r
9584     return FALSE;\r
9585 }\r
9586 \r
9587 int GameListOptions()\r
9588 {\r
9589     char glt[64];\r
9590     int result;\r
9591     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9592 \r
9593     strcpy( glt, appData.gameListTags );\r
9594 \r
9595     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9596 \r
9597     if( result == 0 ) {\r
9598         /* [AS] Memory leak here! */\r
9599         appData.gameListTags = strdup( glt ); \r
9600     }\r
9601 \r
9602     return result;\r
9603 }\r
9604 \r
9605 \r
9606 VOID\r
9607 DisplayIcsInteractionTitle(char *str)\r
9608 {\r
9609   char consoleTitle[MSG_SIZ];\r
9610 \r
9611   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9612   SetWindowText(hwndConsole, consoleTitle);\r
9613 }\r
9614 \r
9615 void\r
9616 DrawPosition(int fullRedraw, Board board)\r
9617 {\r
9618   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9619 }\r
9620 \r
9621 \r
9622 VOID\r
9623 ResetFrontEnd()\r
9624 {\r
9625   fromX = fromY = -1;\r
9626   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9627     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9628     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9629     dragInfo.lastpos = dragInfo.pos;\r
9630     dragInfo.start.x = dragInfo.start.y = -1;\r
9631     dragInfo.from = dragInfo.start;\r
9632     ReleaseCapture();\r
9633     DrawPosition(TRUE, NULL);\r
9634   }\r
9635 }\r
9636 \r
9637 \r
9638 VOID\r
9639 CommentPopUp(char *title, char *str)\r
9640 {\r
9641   HWND hwnd = GetActiveWindow();\r
9642   EitherCommentPopUp(0, title, str, FALSE);\r
9643   SetActiveWindow(hwnd);\r
9644 }\r
9645 \r
9646 VOID\r
9647 CommentPopDown(void)\r
9648 {\r
9649   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9650   if (commentDialog) {\r
9651     ShowWindow(commentDialog, SW_HIDE);\r
9652   }\r
9653   commentDialogUp = FALSE;\r
9654 }\r
9655 \r
9656 VOID\r
9657 EditCommentPopUp(int index, char *title, char *str)\r
9658 {\r
9659   EitherCommentPopUp(index, title, str, TRUE);\r
9660 }\r
9661 \r
9662 \r
9663 VOID\r
9664 RingBell()\r
9665 {\r
9666   MyPlaySound(&sounds[(int)SoundMove]);\r
9667 }\r
9668 \r
9669 VOID PlayIcsWinSound()\r
9670 {\r
9671   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9672 }\r
9673 \r
9674 VOID PlayIcsLossSound()\r
9675 {\r
9676   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9677 }\r
9678 \r
9679 VOID PlayIcsDrawSound()\r
9680 {\r
9681   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9682 }\r
9683 \r
9684 VOID PlayIcsUnfinishedSound()\r
9685 {\r
9686   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9687 }\r
9688 \r
9689 VOID\r
9690 PlayAlarmSound()\r
9691 {\r
9692   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9693 }\r
9694 \r
9695 \r
9696 VOID\r
9697 EchoOn()\r
9698 {\r
9699   HWND hInput;\r
9700   consoleEcho = TRUE;\r
9701   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9702   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9703   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9704 }\r
9705 \r
9706 \r
9707 VOID\r
9708 EchoOff()\r
9709 {\r
9710   CHARFORMAT cf;\r
9711   HWND hInput;\r
9712   consoleEcho = FALSE;\r
9713   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9714   /* This works OK: set text and background both to the same color */\r
9715   cf = consoleCF;\r
9716   cf.crTextColor = COLOR_ECHOOFF;\r
9717   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9718   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9719 }\r
9720 \r
9721 /* No Raw()...? */\r
9722 \r
9723 void Colorize(ColorClass cc, int continuation)\r
9724 {\r
9725   currentColorClass = cc;\r
9726   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9727   consoleCF.crTextColor = textAttribs[cc].color;\r
9728   consoleCF.dwEffects = textAttribs[cc].effects;\r
9729   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9730 }\r
9731 \r
9732 char *\r
9733 UserName()\r
9734 {\r
9735   static char buf[MSG_SIZ];\r
9736   DWORD bufsiz = MSG_SIZ;\r
9737 \r
9738   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9739         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9740   }\r
9741   if (!GetUserName(buf, &bufsiz)) {\r
9742     /*DisplayError("Error getting user name", GetLastError());*/\r
9743     strcpy(buf, "User");\r
9744   }\r
9745   return buf;\r
9746 }\r
9747 \r
9748 char *\r
9749 HostName()\r
9750 {\r
9751   static char buf[MSG_SIZ];\r
9752   DWORD bufsiz = MSG_SIZ;\r
9753 \r
9754   if (!GetComputerName(buf, &bufsiz)) {\r
9755     /*DisplayError("Error getting host name", GetLastError());*/\r
9756     strcpy(buf, "Unknown");\r
9757   }\r
9758   return buf;\r
9759 }\r
9760 \r
9761 \r
9762 int\r
9763 ClockTimerRunning()\r
9764 {\r
9765   return clockTimerEvent != 0;\r
9766 }\r
9767 \r
9768 int\r
9769 StopClockTimer()\r
9770 {\r
9771   if (clockTimerEvent == 0) return FALSE;\r
9772   KillTimer(hwndMain, clockTimerEvent);\r
9773   clockTimerEvent = 0;\r
9774   return TRUE;\r
9775 }\r
9776 \r
9777 void\r
9778 StartClockTimer(long millisec)\r
9779 {\r
9780   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9781                              (UINT) millisec, NULL);\r
9782 }\r
9783 \r
9784 void\r
9785 DisplayWhiteClock(long timeRemaining, int highlight)\r
9786 {\r
9787   HDC hdc;\r
9788   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9789 \r
9790   if(appData.noGUI) return;\r
9791   hdc = GetDC(hwndMain);\r
9792   if (!IsIconic(hwndMain)) {\r
9793     DisplayAClock(hdc, timeRemaining, highlight, \r
9794                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9795   }\r
9796   if (highlight && iconCurrent == iconBlack) {\r
9797     iconCurrent = iconWhite;\r
9798     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9799     if (IsIconic(hwndMain)) {\r
9800       DrawIcon(hdc, 2, 2, iconCurrent);\r
9801     }\r
9802   }\r
9803   (void) ReleaseDC(hwndMain, hdc);\r
9804   if (hwndConsole)\r
9805     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9806 }\r
9807 \r
9808 void\r
9809 DisplayBlackClock(long timeRemaining, int highlight)\r
9810 {\r
9811   HDC hdc;\r
9812   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9813 \r
9814   if(appData.noGUI) return;\r
9815   hdc = GetDC(hwndMain);\r
9816   if (!IsIconic(hwndMain)) {\r
9817     DisplayAClock(hdc, timeRemaining, highlight, \r
9818                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9819   }\r
9820   if (highlight && iconCurrent == iconWhite) {\r
9821     iconCurrent = iconBlack;\r
9822     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9823     if (IsIconic(hwndMain)) {\r
9824       DrawIcon(hdc, 2, 2, iconCurrent);\r
9825     }\r
9826   }\r
9827   (void) ReleaseDC(hwndMain, hdc);\r
9828   if (hwndConsole)\r
9829     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9830 }\r
9831 \r
9832 \r
9833 int\r
9834 LoadGameTimerRunning()\r
9835 {\r
9836   return loadGameTimerEvent != 0;\r
9837 }\r
9838 \r
9839 int\r
9840 StopLoadGameTimer()\r
9841 {\r
9842   if (loadGameTimerEvent == 0) return FALSE;\r
9843   KillTimer(hwndMain, loadGameTimerEvent);\r
9844   loadGameTimerEvent = 0;\r
9845   return TRUE;\r
9846 }\r
9847 \r
9848 void\r
9849 StartLoadGameTimer(long millisec)\r
9850 {\r
9851   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9852                                 (UINT) millisec, NULL);\r
9853 }\r
9854 \r
9855 void\r
9856 AutoSaveGame()\r
9857 {\r
9858   char *defName;\r
9859   FILE *f;\r
9860   char fileTitle[MSG_SIZ];\r
9861 \r
9862   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9863   f = OpenFileDialog(hwndMain, "a", defName,\r
9864                      appData.oldSaveStyle ? "gam" : "pgn",\r
9865                      GAME_FILT, \r
9866                      "Save Game to File", NULL, fileTitle, NULL);\r
9867   if (f != NULL) {\r
9868     SaveGame(f, 0, "");\r
9869     fclose(f);\r
9870   }\r
9871 }\r
9872 \r
9873 \r
9874 void\r
9875 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9876 {\r
9877   if (delayedTimerEvent != 0) {\r
9878     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9879       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9880     }\r
9881     KillTimer(hwndMain, delayedTimerEvent);\r
9882     delayedTimerEvent = 0;\r
9883     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9884     delayedTimerCallback();\r
9885   }\r
9886   delayedTimerCallback = cb;\r
9887   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9888                                 (UINT) millisec, NULL);\r
9889 }\r
9890 \r
9891 DelayedEventCallback\r
9892 GetDelayedEvent()\r
9893 {\r
9894   if (delayedTimerEvent) {\r
9895     return delayedTimerCallback;\r
9896   } else {\r
9897     return NULL;\r
9898   }\r
9899 }\r
9900 \r
9901 void\r
9902 CancelDelayedEvent()\r
9903 {\r
9904   if (delayedTimerEvent) {\r
9905     KillTimer(hwndMain, delayedTimerEvent);\r
9906     delayedTimerEvent = 0;\r
9907   }\r
9908 }\r
9909 \r
9910 DWORD GetWin32Priority(int nice)\r
9911 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9912 /*\r
9913 REALTIME_PRIORITY_CLASS     0x00000100\r
9914 HIGH_PRIORITY_CLASS         0x00000080\r
9915 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9916 NORMAL_PRIORITY_CLASS       0x00000020\r
9917 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9918 IDLE_PRIORITY_CLASS         0x00000040\r
9919 */\r
9920         if (nice < -15) return 0x00000080;\r
9921         if (nice < 0)   return 0x00008000;\r
9922         if (nice == 0)  return 0x00000020;\r
9923         if (nice < 15)  return 0x00004000;\r
9924         return 0x00000040;\r
9925 }\r
9926 \r
9927 /* Start a child process running the given program.\r
9928    The process's standard output can be read from "from", and its\r
9929    standard input can be written to "to".\r
9930    Exit with fatal error if anything goes wrong.\r
9931    Returns an opaque pointer that can be used to destroy the process\r
9932    later.\r
9933 */\r
9934 int\r
9935 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9936 {\r
9937 #define BUFSIZE 4096\r
9938 \r
9939   HANDLE hChildStdinRd, hChildStdinWr,\r
9940     hChildStdoutRd, hChildStdoutWr;\r
9941   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9942   SECURITY_ATTRIBUTES saAttr;\r
9943   BOOL fSuccess;\r
9944   PROCESS_INFORMATION piProcInfo;\r
9945   STARTUPINFO siStartInfo;\r
9946   ChildProc *cp;\r
9947   char buf[MSG_SIZ];\r
9948   DWORD err;\r
9949 \r
9950   if (appData.debugMode) {\r
9951     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9952   }\r
9953 \r
9954   *pr = NoProc;\r
9955 \r
9956   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9957   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9958   saAttr.bInheritHandle = TRUE;\r
9959   saAttr.lpSecurityDescriptor = NULL;\r
9960 \r
9961   /*\r
9962    * The steps for redirecting child's STDOUT:\r
9963    *     1. Create anonymous pipe to be STDOUT for child.\r
9964    *     2. Create a noninheritable duplicate of read handle,\r
9965    *         and close the inheritable read handle.\r
9966    */\r
9967 \r
9968   /* Create a pipe for the child's STDOUT. */\r
9969   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9970     return GetLastError();\r
9971   }\r
9972 \r
9973   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9974   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9975                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9976                              FALSE,     /* not inherited */\r
9977                              DUPLICATE_SAME_ACCESS);\r
9978   if (! fSuccess) {\r
9979     return GetLastError();\r
9980   }\r
9981   CloseHandle(hChildStdoutRd);\r
9982 \r
9983   /*\r
9984    * The steps for redirecting child's STDIN:\r
9985    *     1. Create anonymous pipe to be STDIN for child.\r
9986    *     2. Create a noninheritable duplicate of write handle,\r
9987    *         and close the inheritable write handle.\r
9988    */\r
9989 \r
9990   /* Create a pipe for the child's STDIN. */\r
9991   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9992     return GetLastError();\r
9993   }\r
9994 \r
9995   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9996   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9997                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9998                              FALSE,     /* not inherited */\r
9999                              DUPLICATE_SAME_ACCESS);\r
10000   if (! fSuccess) {\r
10001     return GetLastError();\r
10002   }\r
10003   CloseHandle(hChildStdinWr);\r
10004 \r
10005   /* Arrange to (1) look in dir for the child .exe file, and\r
10006    * (2) have dir be the child's working directory.  Interpret\r
10007    * dir relative to the directory WinBoard loaded from. */\r
10008   GetCurrentDirectory(MSG_SIZ, buf);\r
10009   SetCurrentDirectory(installDir);\r
10010   SetCurrentDirectory(dir);\r
10011 \r
10012   /* Now create the child process. */\r
10013 \r
10014   siStartInfo.cb = sizeof(STARTUPINFO);\r
10015   siStartInfo.lpReserved = NULL;\r
10016   siStartInfo.lpDesktop = NULL;\r
10017   siStartInfo.lpTitle = NULL;\r
10018   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
10019   siStartInfo.cbReserved2 = 0;\r
10020   siStartInfo.lpReserved2 = NULL;\r
10021   siStartInfo.hStdInput = hChildStdinRd;\r
10022   siStartInfo.hStdOutput = hChildStdoutWr;\r
10023   siStartInfo.hStdError = hChildStdoutWr;\r
10024 \r
10025   fSuccess = CreateProcess(NULL,\r
10026                            cmdLine,        /* command line */\r
10027                            NULL,           /* process security attributes */\r
10028                            NULL,           /* primary thread security attrs */\r
10029                            TRUE,           /* handles are inherited */\r
10030                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
10031                            NULL,           /* use parent's environment */\r
10032                            NULL,\r
10033                            &siStartInfo, /* STARTUPINFO pointer */\r
10034                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
10035 \r
10036   err = GetLastError();\r
10037   SetCurrentDirectory(buf); /* return to prev directory */\r
10038   if (! fSuccess) {\r
10039     return err;\r
10040   }\r
10041 \r
10042   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
10043     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
10044     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
10045   }\r
10046 \r
10047   /* Close the handles we don't need in the parent */\r
10048   CloseHandle(piProcInfo.hThread);\r
10049   CloseHandle(hChildStdinRd);\r
10050   CloseHandle(hChildStdoutWr);\r
10051 \r
10052   /* Prepare return value */\r
10053   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10054   cp->kind = CPReal;\r
10055   cp->hProcess = piProcInfo.hProcess;\r
10056   cp->pid = piProcInfo.dwProcessId;\r
10057   cp->hFrom = hChildStdoutRdDup;\r
10058   cp->hTo = hChildStdinWrDup;\r
10059 \r
10060   *pr = (void *) cp;\r
10061 \r
10062   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10063      2000 where engines sometimes don't see the initial command(s)\r
10064      from WinBoard and hang.  I don't understand how that can happen,\r
10065      but the Sleep is harmless, so I've put it in.  Others have also\r
10066      reported what may be the same problem, so hopefully this will fix\r
10067      it for them too.  */\r
10068   Sleep(500);\r
10069 \r
10070   return NO_ERROR;\r
10071 }\r
10072 \r
10073 \r
10074 void\r
10075 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10076 {\r
10077   ChildProc *cp; int result;\r
10078 \r
10079   cp = (ChildProc *) pr;\r
10080   if (cp == NULL) return;\r
10081 \r
10082   switch (cp->kind) {\r
10083   case CPReal:\r
10084     /* TerminateProcess is considered harmful, so... */\r
10085     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10086     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10087     /* The following doesn't work because the chess program\r
10088        doesn't "have the same console" as WinBoard.  Maybe\r
10089        we could arrange for this even though neither WinBoard\r
10090        nor the chess program uses a console for stdio? */\r
10091     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10092 \r
10093     /* [AS] Special termination modes for misbehaving programs... */\r
10094     if( signal == 9 ) { \r
10095         result = TerminateProcess( cp->hProcess, 0 );\r
10096 \r
10097         if ( appData.debugMode) {\r
10098             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10099         }\r
10100     }\r
10101     else if( signal == 10 ) {\r
10102         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10103 \r
10104         if( dw != WAIT_OBJECT_0 ) {\r
10105             result = TerminateProcess( cp->hProcess, 0 );\r
10106 \r
10107             if ( appData.debugMode) {\r
10108                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10109             }\r
10110 \r
10111         }\r
10112     }\r
10113 \r
10114     CloseHandle(cp->hProcess);\r
10115     break;\r
10116 \r
10117   case CPComm:\r
10118     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10119     break;\r
10120 \r
10121   case CPSock:\r
10122     closesocket(cp->sock);\r
10123     WSACleanup();\r
10124     break;\r
10125 \r
10126   case CPRcmd:\r
10127     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10128     closesocket(cp->sock);\r
10129     closesocket(cp->sock2);\r
10130     WSACleanup();\r
10131     break;\r
10132   }\r
10133   free(cp);\r
10134 }\r
10135 \r
10136 void\r
10137 InterruptChildProcess(ProcRef pr)\r
10138 {\r
10139   ChildProc *cp;\r
10140 \r
10141   cp = (ChildProc *) pr;\r
10142   if (cp == NULL) return;\r
10143   switch (cp->kind) {\r
10144   case CPReal:\r
10145     /* The following doesn't work because the chess program\r
10146        doesn't "have the same console" as WinBoard.  Maybe\r
10147        we could arrange for this even though neither WinBoard\r
10148        nor the chess program uses a console for stdio */\r
10149     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10150     break;\r
10151 \r
10152   case CPComm:\r
10153   case CPSock:\r
10154     /* Can't interrupt */\r
10155     break;\r
10156 \r
10157   case CPRcmd:\r
10158     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10159     break;\r
10160   }\r
10161 }\r
10162 \r
10163 \r
10164 int\r
10165 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10166 {\r
10167   char cmdLine[MSG_SIZ];\r
10168 \r
10169   if (port[0] == NULLCHAR) {\r
10170     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10171   } else {\r
10172     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10173   }\r
10174   return StartChildProcess(cmdLine, "", pr);\r
10175 }\r
10176 \r
10177 \r
10178 /* Code to open TCP sockets */\r
10179 \r
10180 int\r
10181 OpenTCP(char *host, char *port, ProcRef *pr)\r
10182 {\r
10183   ChildProc *cp;\r
10184   int err;\r
10185   SOCKET s;\r
10186   struct sockaddr_in sa, mysa;\r
10187   struct hostent FAR *hp;\r
10188   unsigned short uport;\r
10189   WORD wVersionRequested;\r
10190   WSADATA wsaData;\r
10191 \r
10192   /* Initialize socket DLL */\r
10193   wVersionRequested = MAKEWORD(1, 1);\r
10194   err = WSAStartup(wVersionRequested, &wsaData);\r
10195   if (err != 0) return err;\r
10196 \r
10197   /* Make socket */\r
10198   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10199     err = WSAGetLastError();\r
10200     WSACleanup();\r
10201     return err;\r
10202   }\r
10203 \r
10204   /* Bind local address using (mostly) don't-care values.\r
10205    */\r
10206   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10207   mysa.sin_family = AF_INET;\r
10208   mysa.sin_addr.s_addr = INADDR_ANY;\r
10209   uport = (unsigned short) 0;\r
10210   mysa.sin_port = htons(uport);\r
10211   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10212       == SOCKET_ERROR) {\r
10213     err = WSAGetLastError();\r
10214     WSACleanup();\r
10215     return err;\r
10216   }\r
10217 \r
10218   /* Resolve remote host name */\r
10219   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10220   if (!(hp = gethostbyname(host))) {\r
10221     unsigned int b0, b1, b2, b3;\r
10222 \r
10223     err = WSAGetLastError();\r
10224 \r
10225     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10226       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10227       hp->h_addrtype = AF_INET;\r
10228       hp->h_length = 4;\r
10229       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10230       hp->h_addr_list[0] = (char *) malloc(4);\r
10231       hp->h_addr_list[0][0] = (char) b0;\r
10232       hp->h_addr_list[0][1] = (char) b1;\r
10233       hp->h_addr_list[0][2] = (char) b2;\r
10234       hp->h_addr_list[0][3] = (char) b3;\r
10235     } else {\r
10236       WSACleanup();\r
10237       return err;\r
10238     }\r
10239   }\r
10240   sa.sin_family = hp->h_addrtype;\r
10241   uport = (unsigned short) atoi(port);\r
10242   sa.sin_port = htons(uport);\r
10243   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10244 \r
10245   /* Make connection */\r
10246   if (connect(s, (struct sockaddr *) &sa,\r
10247               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10248     err = WSAGetLastError();\r
10249     WSACleanup();\r
10250     return err;\r
10251   }\r
10252 \r
10253   /* Prepare return value */\r
10254   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10255   cp->kind = CPSock;\r
10256   cp->sock = s;\r
10257   *pr = (ProcRef *) cp;\r
10258 \r
10259   return NO_ERROR;\r
10260 }\r
10261 \r
10262 int\r
10263 OpenCommPort(char *name, ProcRef *pr)\r
10264 {\r
10265   HANDLE h;\r
10266   COMMTIMEOUTS ct;\r
10267   ChildProc *cp;\r
10268   char fullname[MSG_SIZ];\r
10269 \r
10270   if (*name != '\\')\r
10271     sprintf(fullname, "\\\\.\\%s", name);\r
10272   else\r
10273     strcpy(fullname, name);\r
10274 \r
10275   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10276                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10277   if (h == (HANDLE) -1) {\r
10278     return GetLastError();\r
10279   }\r
10280   hCommPort = h;\r
10281 \r
10282   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10283 \r
10284   /* Accumulate characters until a 100ms pause, then parse */\r
10285   ct.ReadIntervalTimeout = 100;\r
10286   ct.ReadTotalTimeoutMultiplier = 0;\r
10287   ct.ReadTotalTimeoutConstant = 0;\r
10288   ct.WriteTotalTimeoutMultiplier = 0;\r
10289   ct.WriteTotalTimeoutConstant = 0;\r
10290   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10291 \r
10292   /* Prepare return value */\r
10293   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10294   cp->kind = CPComm;\r
10295   cp->hFrom = h;\r
10296   cp->hTo = h;\r
10297   *pr = (ProcRef *) cp;\r
10298 \r
10299   return NO_ERROR;\r
10300 }\r
10301 \r
10302 int\r
10303 OpenLoopback(ProcRef *pr)\r
10304 {\r
10305   DisplayFatalError("Not implemented", 0, 1);\r
10306   return NO_ERROR;\r
10307 }\r
10308 \r
10309 \r
10310 int\r
10311 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10312 {\r
10313   ChildProc *cp;\r
10314   int err;\r
10315   SOCKET s, s2, s3;\r
10316   struct sockaddr_in sa, mysa;\r
10317   struct hostent FAR *hp;\r
10318   unsigned short uport;\r
10319   WORD wVersionRequested;\r
10320   WSADATA wsaData;\r
10321   int fromPort;\r
10322   char stderrPortStr[MSG_SIZ];\r
10323 \r
10324   /* Initialize socket DLL */\r
10325   wVersionRequested = MAKEWORD(1, 1);\r
10326   err = WSAStartup(wVersionRequested, &wsaData);\r
10327   if (err != 0) return err;\r
10328 \r
10329   /* Resolve remote host name */\r
10330   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10331   if (!(hp = gethostbyname(host))) {\r
10332     unsigned int b0, b1, b2, b3;\r
10333 \r
10334     err = WSAGetLastError();\r
10335 \r
10336     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10337       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10338       hp->h_addrtype = AF_INET;\r
10339       hp->h_length = 4;\r
10340       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10341       hp->h_addr_list[0] = (char *) malloc(4);\r
10342       hp->h_addr_list[0][0] = (char) b0;\r
10343       hp->h_addr_list[0][1] = (char) b1;\r
10344       hp->h_addr_list[0][2] = (char) b2;\r
10345       hp->h_addr_list[0][3] = (char) b3;\r
10346     } else {\r
10347       WSACleanup();\r
10348       return err;\r
10349     }\r
10350   }\r
10351   sa.sin_family = hp->h_addrtype;\r
10352   uport = (unsigned short) 514;\r
10353   sa.sin_port = htons(uport);\r
10354   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10355 \r
10356   /* Bind local socket to unused "privileged" port address\r
10357    */\r
10358   s = INVALID_SOCKET;\r
10359   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10360   mysa.sin_family = AF_INET;\r
10361   mysa.sin_addr.s_addr = INADDR_ANY;\r
10362   for (fromPort = 1023;; fromPort--) {\r
10363     if (fromPort < 0) {\r
10364       WSACleanup();\r
10365       return WSAEADDRINUSE;\r
10366     }\r
10367     if (s == INVALID_SOCKET) {\r
10368       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10369         err = WSAGetLastError();\r
10370         WSACleanup();\r
10371         return err;\r
10372       }\r
10373     }\r
10374     uport = (unsigned short) fromPort;\r
10375     mysa.sin_port = htons(uport);\r
10376     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10377         == SOCKET_ERROR) {\r
10378       err = WSAGetLastError();\r
10379       if (err == WSAEADDRINUSE) continue;\r
10380       WSACleanup();\r
10381       return err;\r
10382     }\r
10383     if (connect(s, (struct sockaddr *) &sa,\r
10384       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10385       err = WSAGetLastError();\r
10386       if (err == WSAEADDRINUSE) {\r
10387         closesocket(s);\r
10388         s = -1;\r
10389         continue;\r
10390       }\r
10391       WSACleanup();\r
10392       return err;\r
10393     }\r
10394     break;\r
10395   }\r
10396 \r
10397   /* Bind stderr local socket to unused "privileged" port address\r
10398    */\r
10399   s2 = INVALID_SOCKET;\r
10400   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10401   mysa.sin_family = AF_INET;\r
10402   mysa.sin_addr.s_addr = INADDR_ANY;\r
10403   for (fromPort = 1023;; fromPort--) {\r
10404     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10405     if (fromPort < 0) {\r
10406       (void) closesocket(s);\r
10407       WSACleanup();\r
10408       return WSAEADDRINUSE;\r
10409     }\r
10410     if (s2 == INVALID_SOCKET) {\r
10411       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10412         err = WSAGetLastError();\r
10413         closesocket(s);\r
10414         WSACleanup();\r
10415         return err;\r
10416       }\r
10417     }\r
10418     uport = (unsigned short) fromPort;\r
10419     mysa.sin_port = htons(uport);\r
10420     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10421         == SOCKET_ERROR) {\r
10422       err = WSAGetLastError();\r
10423       if (err == WSAEADDRINUSE) continue;\r
10424       (void) closesocket(s);\r
10425       WSACleanup();\r
10426       return err;\r
10427     }\r
10428     if (listen(s2, 1) == SOCKET_ERROR) {\r
10429       err = WSAGetLastError();\r
10430       if (err == WSAEADDRINUSE) {\r
10431         closesocket(s2);\r
10432         s2 = INVALID_SOCKET;\r
10433         continue;\r
10434       }\r
10435       (void) closesocket(s);\r
10436       (void) closesocket(s2);\r
10437       WSACleanup();\r
10438       return err;\r
10439     }\r
10440     break;\r
10441   }\r
10442   prevStderrPort = fromPort; // remember port used\r
10443   sprintf(stderrPortStr, "%d", fromPort);\r
10444 \r
10445   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10446     err = WSAGetLastError();\r
10447     (void) closesocket(s);\r
10448     (void) closesocket(s2);\r
10449     WSACleanup();\r
10450     return err;\r
10451   }\r
10452 \r
10453   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10454     err = WSAGetLastError();\r
10455     (void) closesocket(s);\r
10456     (void) closesocket(s2);\r
10457     WSACleanup();\r
10458     return err;\r
10459   }\r
10460   if (*user == NULLCHAR) user = UserName();\r
10461   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10462     err = WSAGetLastError();\r
10463     (void) closesocket(s);\r
10464     (void) closesocket(s2);\r
10465     WSACleanup();\r
10466     return err;\r
10467   }\r
10468   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10469     err = WSAGetLastError();\r
10470     (void) closesocket(s);\r
10471     (void) closesocket(s2);\r
10472     WSACleanup();\r
10473     return err;\r
10474   }\r
10475 \r
10476   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10477     err = WSAGetLastError();\r
10478     (void) closesocket(s);\r
10479     (void) closesocket(s2);\r
10480     WSACleanup();\r
10481     return err;\r
10482   }\r
10483   (void) closesocket(s2);  /* Stop listening */\r
10484 \r
10485   /* Prepare return value */\r
10486   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10487   cp->kind = CPRcmd;\r
10488   cp->sock = s;\r
10489   cp->sock2 = s3;\r
10490   *pr = (ProcRef *) cp;\r
10491 \r
10492   return NO_ERROR;\r
10493 }\r
10494 \r
10495 \r
10496 InputSourceRef\r
10497 AddInputSource(ProcRef pr, int lineByLine,\r
10498                InputCallback func, VOIDSTAR closure)\r
10499 {\r
10500   InputSource *is, *is2 = NULL;\r
10501   ChildProc *cp = (ChildProc *) pr;\r
10502 \r
10503   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10504   is->lineByLine = lineByLine;\r
10505   is->func = func;\r
10506   is->closure = closure;\r
10507   is->second = NULL;\r
10508   is->next = is->buf;\r
10509   if (pr == NoProc) {\r
10510     is->kind = CPReal;\r
10511     consoleInputSource = is;\r
10512   } else {\r
10513     is->kind = cp->kind;\r
10514     /* \r
10515         [AS] Try to avoid a race condition if the thread is given control too early:\r
10516         we create all threads suspended so that the is->hThread variable can be\r
10517         safely assigned, then let the threads start with ResumeThread.\r
10518     */\r
10519     switch (cp->kind) {\r
10520     case CPReal:\r
10521       is->hFile = cp->hFrom;\r
10522       cp->hFrom = NULL; /* now owned by InputThread */\r
10523       is->hThread =\r
10524         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10525                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10526       break;\r
10527 \r
10528     case CPComm:\r
10529       is->hFile = cp->hFrom;\r
10530       cp->hFrom = NULL; /* now owned by InputThread */\r
10531       is->hThread =\r
10532         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10533                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10534       break;\r
10535 \r
10536     case CPSock:\r
10537       is->sock = cp->sock;\r
10538       is->hThread =\r
10539         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10540                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10541       break;\r
10542 \r
10543     case CPRcmd:\r
10544       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10545       *is2 = *is;\r
10546       is->sock = cp->sock;\r
10547       is->second = is2;\r
10548       is2->sock = cp->sock2;\r
10549       is2->second = is2;\r
10550       is->hThread =\r
10551         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10552                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10553       is2->hThread =\r
10554         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10555                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10556       break;\r
10557     }\r
10558 \r
10559     if( is->hThread != NULL ) {\r
10560         ResumeThread( is->hThread );\r
10561     }\r
10562 \r
10563     if( is2 != NULL && is2->hThread != NULL ) {\r
10564         ResumeThread( is2->hThread );\r
10565     }\r
10566   }\r
10567 \r
10568   return (InputSourceRef) is;\r
10569 }\r
10570 \r
10571 void\r
10572 RemoveInputSource(InputSourceRef isr)\r
10573 {\r
10574   InputSource *is;\r
10575 \r
10576   is = (InputSource *) isr;\r
10577   is->hThread = NULL;  /* tell thread to stop */\r
10578   CloseHandle(is->hThread);\r
10579   if (is->second != NULL) {\r
10580     is->second->hThread = NULL;\r
10581     CloseHandle(is->second->hThread);\r
10582   }\r
10583 }\r
10584 \r
10585 \r
10586 int\r
10587 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10588 {\r
10589   DWORD dOutCount;\r
10590   int outCount = SOCKET_ERROR;\r
10591   ChildProc *cp = (ChildProc *) pr;\r
10592   static OVERLAPPED ovl;\r
10593 \r
10594   if (pr == NoProc) {\r
10595     ConsoleOutput(message, count, FALSE);\r
10596     return count;\r
10597   } \r
10598 \r
10599   if (ovl.hEvent == NULL) {\r
10600     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10601   }\r
10602   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10603 \r
10604   switch (cp->kind) {\r
10605   case CPSock:\r
10606   case CPRcmd:\r
10607     outCount = send(cp->sock, message, count, 0);\r
10608     if (outCount == SOCKET_ERROR) {\r
10609       *outError = WSAGetLastError();\r
10610     } else {\r
10611       *outError = NO_ERROR;\r
10612     }\r
10613     break;\r
10614 \r
10615   case CPReal:\r
10616     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10617                   &dOutCount, NULL)) {\r
10618       *outError = NO_ERROR;\r
10619       outCount = (int) dOutCount;\r
10620     } else {\r
10621       *outError = GetLastError();\r
10622     }\r
10623     break;\r
10624 \r
10625   case CPComm:\r
10626     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10627                             &dOutCount, &ovl);\r
10628     if (*outError == NO_ERROR) {\r
10629       outCount = (int) dOutCount;\r
10630     }\r
10631     break;\r
10632   }\r
10633   return outCount;\r
10634 }\r
10635 \r
10636 int\r
10637 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10638                        long msdelay)\r
10639 {\r
10640   /* Ignore delay, not implemented for WinBoard */\r
10641   return OutputToProcess(pr, message, count, outError);\r
10642 }\r
10643 \r
10644 \r
10645 void\r
10646 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10647                         char *buf, int count, int error)\r
10648 {\r
10649   DisplayFatalError("Not implemented", 0, 1);\r
10650 }\r
10651 \r
10652 /* see wgamelist.c for Game List functions */\r
10653 /* see wedittags.c for Edit Tags functions */\r
10654 \r
10655 \r
10656 VOID\r
10657 ICSInitScript()\r
10658 {\r
10659   FILE *f;\r
10660   char buf[MSG_SIZ];\r
10661   char *dummy;\r
10662 \r
10663   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10664     f = fopen(buf, "r");\r
10665     if (f != NULL) {\r
10666       ProcessICSInitScript(f);\r
10667       fclose(f);\r
10668     }\r
10669   }\r
10670 }\r
10671 \r
10672 \r
10673 VOID\r
10674 StartAnalysisClock()\r
10675 {\r
10676   if (analysisTimerEvent) return;\r
10677   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10678                                         (UINT) 2000, NULL);\r
10679 }\r
10680 \r
10681 LRESULT CALLBACK\r
10682 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10683 {\r
10684   static HANDLE hwndText;\r
10685   RECT rect;\r
10686   static int sizeX, sizeY;\r
10687   int newSizeX, newSizeY, flags;\r
10688   MINMAXINFO *mmi;\r
10689 \r
10690   switch (message) {\r
10691   case WM_INITDIALOG: /* message: initialize dialog box */\r
10692     /* Initialize the dialog items */\r
10693     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10694     SetWindowText(hDlg, analysisTitle);\r
10695     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10696     /* Size and position the dialog */\r
10697     if (!analysisDialog) {\r
10698       analysisDialog = hDlg;\r
10699       flags = SWP_NOZORDER;\r
10700       GetClientRect(hDlg, &rect);\r
10701       sizeX = rect.right;\r
10702       sizeY = rect.bottom;\r
10703       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10704           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10705         WINDOWPLACEMENT wp;\r
10706         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10707         wp.length = sizeof(WINDOWPLACEMENT);\r
10708         wp.flags = 0;\r
10709         wp.showCmd = SW_SHOW;\r
10710         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10711         wp.rcNormalPosition.left = analysisX;\r
10712         wp.rcNormalPosition.right = analysisX + analysisW;\r
10713         wp.rcNormalPosition.top = analysisY;\r
10714         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10715         SetWindowPlacement(hDlg, &wp);\r
10716 \r
10717         GetClientRect(hDlg, &rect);\r
10718         newSizeX = rect.right;\r
10719         newSizeY = rect.bottom;\r
10720         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10721                               newSizeX, newSizeY);\r
10722         sizeX = newSizeX;\r
10723         sizeY = newSizeY;\r
10724       }\r
10725     }\r
10726     return FALSE;\r
10727 \r
10728   case WM_COMMAND: /* message: received a command */\r
10729     switch (LOWORD(wParam)) {\r
10730     case IDCANCEL:\r
10731       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10732           ExitAnalyzeMode();\r
10733           ModeHighlight();\r
10734           return TRUE;\r
10735       }\r
10736       EditGameEvent();\r
10737       return TRUE;\r
10738     default:\r
10739       break;\r
10740     }\r
10741     break;\r
10742 \r
10743   case WM_SIZE:\r
10744     newSizeX = LOWORD(lParam);\r
10745     newSizeY = HIWORD(lParam);\r
10746     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10747     sizeX = newSizeX;\r
10748     sizeY = newSizeY;\r
10749     break;\r
10750 \r
10751   case WM_GETMINMAXINFO:\r
10752     /* Prevent resizing window too small */\r
10753     mmi = (MINMAXINFO *) lParam;\r
10754     mmi->ptMinTrackSize.x = 100;\r
10755     mmi->ptMinTrackSize.y = 100;\r
10756     break;\r
10757   }\r
10758   return FALSE;\r
10759 }\r
10760 \r
10761 VOID\r
10762 AnalysisPopUp(char* title, char* str)\r
10763 {\r
10764   FARPROC lpProc;\r
10765   char *p, *q;\r
10766 \r
10767   /* [AS] */\r
10768   EngineOutputPopUp();\r
10769   return;\r
10770 \r
10771   if (str == NULL) str = "";\r
10772   p = (char *) malloc(2 * strlen(str) + 2);\r
10773   q = p;\r
10774   while (*str) {\r
10775     if (*str == '\n') *q++ = '\r';\r
10776     *q++ = *str++;\r
10777   }\r
10778   *q = NULLCHAR;\r
10779   if (analysisText != NULL) free(analysisText);\r
10780   analysisText = p;\r
10781 \r
10782   if (analysisDialog) {\r
10783     SetWindowText(analysisDialog, title);\r
10784     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10785     ShowWindow(analysisDialog, SW_SHOW);\r
10786   } else {\r
10787     analysisTitle = title;\r
10788     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10789     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10790                  hwndMain, (DLGPROC)lpProc);\r
10791     FreeProcInstance(lpProc);\r
10792   }\r
10793   analysisDialogUp = TRUE;  \r
10794 }\r
10795 \r
10796 VOID\r
10797 AnalysisPopDown()\r
10798 {\r
10799   if (analysisDialog) {\r
10800     ShowWindow(analysisDialog, SW_HIDE);\r
10801   }\r
10802   analysisDialogUp = FALSE;  \r
10803 }\r
10804 \r
10805 \r
10806 VOID\r
10807 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10808 {\r
10809   highlightInfo.sq[0].x = fromX;\r
10810   highlightInfo.sq[0].y = fromY;\r
10811   highlightInfo.sq[1].x = toX;\r
10812   highlightInfo.sq[1].y = toY;\r
10813 }\r
10814 \r
10815 VOID\r
10816 ClearHighlights()\r
10817 {\r
10818   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10819     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10820 }\r
10821 \r
10822 VOID\r
10823 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10824 {\r
10825   premoveHighlightInfo.sq[0].x = fromX;\r
10826   premoveHighlightInfo.sq[0].y = fromY;\r
10827   premoveHighlightInfo.sq[1].x = toX;\r
10828   premoveHighlightInfo.sq[1].y = toY;\r
10829 }\r
10830 \r
10831 VOID\r
10832 ClearPremoveHighlights()\r
10833 {\r
10834   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10835     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10836 }\r
10837 \r
10838 VOID\r
10839 ShutDownFrontEnd()\r
10840 {\r
10841   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10842   DeleteClipboardTempFiles();\r
10843 }\r
10844 \r
10845 void\r
10846 BoardToTop()\r
10847 {\r
10848     if (IsIconic(hwndMain))\r
10849       ShowWindow(hwndMain, SW_RESTORE);\r
10850 \r
10851     SetActiveWindow(hwndMain);\r
10852 }\r
10853 \r
10854 /*\r
10855  * Prototypes for animation support routines\r
10856  */\r
10857 static void ScreenSquare(int column, int row, POINT * pt);\r
10858 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10859      POINT frames[], int * nFrames);\r
10860 \r
10861 \r
10862 void\r
10863 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10864 {       // [HGM] atomic: animate blast wave\r
10865         int i;\r
10866 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10867         explodeInfo.fromX = fromX;\r
10868         explodeInfo.fromY = fromY;\r
10869         explodeInfo.toX = toX;\r
10870         explodeInfo.toY = toY;\r
10871         for(i=1; i<nFrames; i++) {\r
10872             explodeInfo.radius = (i*180)/(nFrames-1);\r
10873             DrawPosition(FALSE, NULL);\r
10874             Sleep(appData.animSpeed);\r
10875         }\r
10876         explodeInfo.radius = 0;\r
10877         DrawPosition(TRUE, NULL);\r
10878 }\r
10879 \r
10880 #define kFactor 4\r
10881 \r
10882 void\r
10883 AnimateMove(board, fromX, fromY, toX, toY)\r
10884      Board board;\r
10885      int fromX;\r
10886      int fromY;\r
10887      int toX;\r
10888      int toY;\r
10889 {\r
10890   ChessSquare piece;\r
10891   POINT start, finish, mid;\r
10892   POINT frames[kFactor * 2 + 1];\r
10893   int nFrames, n;\r
10894 \r
10895   if (!appData.animate) return;\r
10896   if (doingSizing) return;\r
10897   if (fromY < 0 || fromX < 0) return;\r
10898   piece = board[fromY][fromX];\r
10899   if (piece >= EmptySquare) return;\r
10900 \r
10901   ScreenSquare(fromX, fromY, &start);\r
10902   ScreenSquare(toX, toY, &finish);\r
10903 \r
10904   /* All pieces except knights move in straight line */\r
10905   if (piece != WhiteKnight && piece != BlackKnight) {\r
10906     mid.x = start.x + (finish.x - start.x) / 2;\r
10907     mid.y = start.y + (finish.y - start.y) / 2;\r
10908   } else {\r
10909     /* Knight: make diagonal movement then straight */\r
10910     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10911        mid.x = start.x + (finish.x - start.x) / 2;\r
10912        mid.y = finish.y;\r
10913      } else {\r
10914        mid.x = finish.x;\r
10915        mid.y = start.y + (finish.y - start.y) / 2;\r
10916      }\r
10917   }\r
10918   \r
10919   /* Don't use as many frames for very short moves */\r
10920   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10921     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10922   else\r
10923     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10924 \r
10925   animInfo.from.x = fromX;\r
10926   animInfo.from.y = fromY;\r
10927   animInfo.to.x = toX;\r
10928   animInfo.to.y = toY;\r
10929   animInfo.lastpos = start;\r
10930   animInfo.piece = piece;\r
10931   for (n = 0; n < nFrames; n++) {\r
10932     animInfo.pos = frames[n];\r
10933     DrawPosition(FALSE, NULL);\r
10934     animInfo.lastpos = animInfo.pos;\r
10935     Sleep(appData.animSpeed);\r
10936   }\r
10937   animInfo.pos = finish;\r
10938   DrawPosition(FALSE, NULL);\r
10939   animInfo.piece = EmptySquare;\r
10940   if(gameInfo.variant == VariantAtomic && \r
10941      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10942         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10943 }\r
10944 \r
10945 /*      Convert board position to corner of screen rect and color       */\r
10946 \r
10947 static void\r
10948 ScreenSquare(column, row, pt)\r
10949      int column; int row; POINT * pt;\r
10950 {\r
10951   if (flipView) {\r
10952     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10953     pt->y = lineGap + row * (squareSize + lineGap);\r
10954   } else {\r
10955     pt->x = lineGap + column * (squareSize + lineGap);\r
10956     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10957   }\r
10958 }\r
10959 \r
10960 /*      Generate a series of frame coords from start->mid->finish.\r
10961         The movement rate doubles until the half way point is\r
10962         reached, then halves back down to the final destination,\r
10963         which gives a nice slow in/out effect. The algorithmn\r
10964         may seem to generate too many intermediates for short\r
10965         moves, but remember that the purpose is to attract the\r
10966         viewers attention to the piece about to be moved and\r
10967         then to where it ends up. Too few frames would be less\r
10968         noticeable.                                             */\r
10969 \r
10970 static void\r
10971 Tween(start, mid, finish, factor, frames, nFrames)\r
10972      POINT * start; POINT * mid;\r
10973      POINT * finish; int factor;\r
10974      POINT frames[]; int * nFrames;\r
10975 {\r
10976   int n, fraction = 1, count = 0;\r
10977 \r
10978   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10979   for (n = 0; n < factor; n++)\r
10980     fraction *= 2;\r
10981   for (n = 0; n < factor; n++) {\r
10982     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10983     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10984     count ++;\r
10985     fraction = fraction / 2;\r
10986   }\r
10987   \r
10988   /* Midpoint */\r
10989   frames[count] = *mid;\r
10990   count ++;\r
10991   \r
10992   /* Slow out, stepping 1/2, then 1/4, ... */\r
10993   fraction = 2;\r
10994   for (n = 0; n < factor; n++) {\r
10995     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10996     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10997     count ++;\r
10998     fraction = fraction * 2;\r
10999   }\r
11000   *nFrames = count;\r
11001 }\r
11002 \r
11003 void\r
11004 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
11005 {\r
11006 #if 0\r
11007     char buf[256];\r
11008 \r
11009     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
11010         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
11011 \r
11012     OutputDebugString( buf );\r
11013 #endif\r
11014 \r
11015     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
11016 \r
11017     EvalGraphSet( first, last, current, pvInfoList );\r
11018 }\r
11019 \r
11020 void SetProgramStats( FrontEndProgramStats * stats )\r
11021 {\r
11022 #if 0\r
11023     char buf[1024];\r
11024 \r
11025     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
11026         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
11027 \r
11028     OutputDebugString( buf );\r
11029 #endif\r
11030 \r
11031     EngineOutputUpdate( stats );\r
11032 }\r