3b2dd29b2760038593b446b753f10e032ee644e7
[xboard.git] / winboard / winboard.c
1 /*\r
2  * WinBoard.c -- Windows NT front end to XBoard\r
3  *\r
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,\r
5  * Massachusetts. \r
6  *\r
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
8  * 2007, 2008, 2009 Free Software Foundation, Inc.\r
9  *\r
10  * Enhancements Copyright 2005 Alessandro Scotti\r
11  *\r
12  * XBoard borrows its colors and the bitmaps.xchess bitmap set from XChess,\r
13  * which was written and is copyrighted by Wayne Christopher.\r
14  *\r
15  * The following terms apply to Digital Equipment Corporation's copyright\r
16  * interest in XBoard:\r
17  * ------------------------------------------------------------------------\r
18  * All Rights Reserved\r
19  *\r
20  * Permission to use, copy, modify, and distribute this software and its\r
21  * documentation for any purpose and without fee is hereby granted,\r
22  * provided that the above copyright notice appear in all copies and that\r
23  * both that copyright notice and this permission notice appear in\r
24  * supporting documentation, and that the name of Digital not be\r
25  * used in advertising or publicity pertaining to distribution of the\r
26  * software without specific, written prior permission.\r
27  *\r
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING\r
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL\r
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR\r
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\r
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,\r
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS\r
34  * SOFTWARE.\r
35  * ------------------------------------------------------------------------\r
36  *\r
37  * The following terms apply to the enhanced version of XBoard\r
38  * distributed by the Free Software Foundation:\r
39  * ------------------------------------------------------------------------\r
40  *\r
41  * GNU XBoard is free software: you can redistribute it and/or modify\r
42  * it under the terms of the GNU General Public License as published by\r
43  * the Free Software Foundation, either version 3 of the License, or (at\r
44  * your option) any later version.\r
45  *\r
46  * GNU XBoard is distributed in the hope that it will be useful, but\r
47  * WITHOUT ANY WARRANTY; without even the implied warranty of\r
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
49  * General Public License for more details.\r
50  *\r
51  * You should have received a copy of the GNU General Public License\r
52  * along with this program. If not, see http://www.gnu.org/licenses/.  *\r
53  *\r
54  *------------------------------------------------------------------------\r
55  ** See the file ChangeLog for a revision history.  */\r
56 \r
57 #include "config.h"\r
58 \r
59 #include <windows.h>\r
60 #include <winuser.h>\r
61 #include <winsock.h>\r
62 #include <commctrl.h>\r
63 \r
64 #include <stdio.h>\r
65 #include <stdlib.h>\r
66 #include <time.h>\r
67 #include <malloc.h>\r
68 #include <sys/stat.h>\r
69 #include <fcntl.h>\r
70 #include <math.h>\r
71 #include <commdlg.h>\r
72 #include <dlgs.h>\r
73 #include <richedit.h>\r
74 #include <mmsystem.h>\r
75 #include <ctype.h>\r
76 \r
77 #if __GNUC__\r
78 #include <errno.h>\r
79 #include <string.h>\r
80 #endif\r
81 \r
82 #include "common.h"\r
83 #include "winboard.h"\r
84 #include "frontend.h"\r
85 #include "backend.h"\r
86 #include "moves.h"\r
87 #include "wclipbrd.h"\r
88 #include "wgamelist.h"\r
89 #include "wedittags.h"\r
90 #include "woptions.h"\r
91 #include "wsockerr.h"\r
92 #include "defaults.h"\r
93 #include "help.h"\r
94 #include "wsnap.h"\r
95 \r
96 //void InitEngineUCI( const char * iniDir, ChessProgramState * cps );\r
97 \r
98   int myrandom(void);\r
99   void mysrandom(unsigned int seed);\r
100 \r
101 extern int whiteFlag, blackFlag;\r
102 Boolean flipClock = FALSE;\r
103 extern HANDLE chatHandle[];\r
104 extern int ics_type;\r
105 \r
106 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
107 VOID NewVariantPopup(HWND hwnd);\r
108 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
109                    /*char*/int promoChar));\r
110 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
111 void DisplayMove P((int moveNumber));\r
112 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
113 void ChatPopUp P(());\r
114 typedef struct {\r
115   ChessSquare piece;  \r
116   POINT pos;      /* window coordinates of current pos */\r
117   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
118   POINT from;     /* board coordinates of the piece's orig pos */\r
119   POINT to;       /* board coordinates of the piece's new pos */\r
120 } AnimInfo;\r
121 \r
122 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
123 \r
124 typedef struct {\r
125   POINT start;    /* window coordinates of start pos */\r
126   POINT pos;      /* window coordinates of current pos */\r
127   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
128   POINT from;     /* board coordinates of the piece's orig pos */\r
129 } DragInfo;\r
130 \r
131 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
132 \r
133 typedef struct {\r
134   POINT sq[2];    /* board coordinates of from, to squares */\r
135 } HighlightInfo;\r
136 \r
137 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
138 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
139 \r
140 typedef struct { // [HGM] atomic\r
141   int fromX, fromY, toX, toY, radius;\r
142 } ExplodeInfo;\r
143 \r
144 static ExplodeInfo explodeInfo;\r
145 \r
146 /* Window class names */\r
147 char szAppName[] = "WinBoard";\r
148 char szConsoleName[] = "WBConsole";\r
149 \r
150 /* Title bar text */\r
151 char szTitle[] = "WinBoard";\r
152 char szConsoleTitle[] = "I C S Interaction";\r
153 \r
154 char *programName;\r
155 char *settingsFileName;\r
156 BOOLEAN saveSettingsOnExit;\r
157 char installDir[MSG_SIZ];\r
158 \r
159 BoardSize boardSize;\r
160 BOOLEAN chessProgram;\r
161 static int boardX, boardY;\r
162 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
163 static int squareSize, lineGap, minorSize;\r
164 static int winWidth, winHeight, winW, winH;\r
165 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
166 static int logoHeight = 0;\r
167 static char messageText[MESSAGE_TEXT_MAX];\r
168 static int clockTimerEvent = 0;\r
169 static int loadGameTimerEvent = 0;\r
170 static int analysisTimerEvent = 0;\r
171 static DelayedEventCallback delayedTimerCallback;\r
172 static int delayedTimerEvent = 0;\r
173 static int buttonCount = 2;\r
174 char *icsTextMenuString;\r
175 char *icsNames;\r
176 char *firstChessProgramNames;\r
177 char *secondChessProgramNames;\r
178 \r
179 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
180 \r
181 #define PALETTESIZE 256\r
182 \r
183 HINSTANCE hInst;          /* current instance */\r
184 HWND hwndMain = NULL;        /* root window*/\r
185 HWND hwndConsole = NULL;\r
186 BOOLEAN alwaysOnTop = FALSE;\r
187 RECT boardRect;\r
188 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
189   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
190 HPALETTE hPal;\r
191 ColorClass currentColorClass;\r
192 \r
193 HWND hCommPort = NULL;    /* currently open comm port */\r
194 static HWND hwndPause;    /* pause button */\r
195 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
196 static HBRUSH lightSquareBrush, darkSquareBrush,\r
197   blackSquareBrush, /* [HGM] for band between board and holdings */\r
198   explodeBrush,     /* [HGM] atomic */\r
199   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
200 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
201 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
202 static HPEN gridPen = NULL;\r
203 static HPEN highlightPen = NULL;\r
204 static HPEN premovePen = NULL;\r
205 static NPLOGPALETTE pLogPal;\r
206 static BOOL paletteChanged = FALSE;\r
207 static HICON iconWhite, iconBlack, iconCurrent;\r
208 static int doingSizing = FALSE;\r
209 static int lastSizing = 0;\r
210 static int prevStderrPort;\r
211 static HBITMAP userLogo;\r
212 \r
213 /* [AS] Support for background textures */\r
214 #define BACK_TEXTURE_MODE_DISABLED      0\r
215 #define BACK_TEXTURE_MODE_PLAIN         1\r
216 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
217 \r
218 static HBITMAP liteBackTexture = NULL;\r
219 static HBITMAP darkBackTexture = NULL;\r
220 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
221 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
222 static int backTextureSquareSize = 0;\r
223 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
224 \r
225 #if __GNUC__ && !defined(_winmajor)\r
226 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
227 #else\r
228 #if defined(_winmajor)\r
229 #define oldDialog (_winmajor < 4)\r
230 #else\r
231 #define oldDialog 0\r
232 #endif\r
233 #endif\r
234 \r
235 char *defaultTextAttribs[] = \r
236 {\r
237   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
238   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
239   COLOR_NONE\r
240 };\r
241 \r
242 typedef struct {\r
243   char *name;\r
244   int squareSize;\r
245   int lineGap;\r
246   int smallLayout;\r
247   int tinyLayout;\r
248   int cliWidth, cliHeight;\r
249 } SizeInfo;\r
250 \r
251 SizeInfo sizeInfo[] = \r
252 {\r
253   { "tiny",     21, 0, 1, 1, 0, 0 },\r
254   { "teeny",    25, 1, 1, 1, 0, 0 },\r
255   { "dinky",    29, 1, 1, 1, 0, 0 },\r
256   { "petite",   33, 1, 1, 1, 0, 0 },\r
257   { "slim",     37, 2, 1, 0, 0, 0 },\r
258   { "small",    40, 2, 1, 0, 0, 0 },\r
259   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
260   { "middling", 49, 2, 0, 0, 0, 0 },\r
261   { "average",  54, 2, 0, 0, 0, 0 },\r
262   { "moderate", 58, 3, 0, 0, 0, 0 },\r
263   { "medium",   64, 3, 0, 0, 0, 0 },\r
264   { "bulky",    72, 3, 0, 0, 0, 0 },\r
265   { "large",    80, 3, 0, 0, 0, 0 },\r
266   { "big",      87, 3, 0, 0, 0, 0 },\r
267   { "huge",     95, 3, 0, 0, 0, 0 },\r
268   { "giant",    108, 3, 0, 0, 0, 0 },\r
269   { "colossal", 116, 4, 0, 0, 0, 0 },\r
270   { "titanic",  129, 4, 0, 0, 0, 0 },\r
271   { NULL, 0, 0, 0, 0, 0, 0 }\r
272 };\r
273 \r
274 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
275 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
276 {\r
277   { MF(CLOCK_FONT_TINY), MF(MESSAGE_FONT_TINY), MF(COORD_FONT_TINY), MF(CONSOLE_FONT_TINY), MF(COMMENT_FONT_TINY), MF(EDITTAGS_FONT_TINY), MF(MOVEHISTORY_FONT_ALL) },\r
278   { MF(CLOCK_FONT_TEENY), MF(MESSAGE_FONT_TEENY), MF(COORD_FONT_TEENY), MF(CONSOLE_FONT_TEENY), MF(COMMENT_FONT_TEENY), MF(EDITTAGS_FONT_TEENY), MF(MOVEHISTORY_FONT_ALL) },\r
279   { MF(CLOCK_FONT_DINKY), MF(MESSAGE_FONT_DINKY), MF(COORD_FONT_DINKY), MF(CONSOLE_FONT_DINKY), MF(COMMENT_FONT_DINKY), MF(EDITTAGS_FONT_DINKY), MF(MOVEHISTORY_FONT_ALL) },\r
280   { MF(CLOCK_FONT_PETITE), MF(MESSAGE_FONT_PETITE), MF(COORD_FONT_PETITE), MF(CONSOLE_FONT_PETITE), MF(COMMENT_FONT_PETITE), MF(EDITTAGS_FONT_PETITE), MF(MOVEHISTORY_FONT_ALL) },\r
281   { MF(CLOCK_FONT_SLIM), MF(MESSAGE_FONT_SLIM), MF(COORD_FONT_SLIM), MF(CONSOLE_FONT_SLIM), MF(COMMENT_FONT_SLIM), MF(EDITTAGS_FONT_SLIM), MF(MOVEHISTORY_FONT_ALL) },\r
282   { MF(CLOCK_FONT_SMALL), MF(MESSAGE_FONT_SMALL), MF(COORD_FONT_SMALL), MF(CONSOLE_FONT_SMALL), MF(COMMENT_FONT_SMALL), MF(EDITTAGS_FONT_SMALL), MF(MOVEHISTORY_FONT_ALL) },\r
283   { MF(CLOCK_FONT_MEDIOCRE), MF(MESSAGE_FONT_MEDIOCRE), MF(COORD_FONT_MEDIOCRE), MF(CONSOLE_FONT_MEDIOCRE), MF(COMMENT_FONT_MEDIOCRE), MF(EDITTAGS_FONT_MEDIOCRE), MF(MOVEHISTORY_FONT_ALL) },\r
284   { MF(CLOCK_FONT_MIDDLING), MF(MESSAGE_FONT_MIDDLING), MF(COORD_FONT_MIDDLING), MF(CONSOLE_FONT_MIDDLING), MF(COMMENT_FONT_MIDDLING), MF(EDITTAGS_FONT_MIDDLING), MF(MOVEHISTORY_FONT_ALL) },\r
285   { MF(CLOCK_FONT_AVERAGE), MF(MESSAGE_FONT_AVERAGE), MF(COORD_FONT_AVERAGE), MF(CONSOLE_FONT_AVERAGE), MF(COMMENT_FONT_AVERAGE), MF(EDITTAGS_FONT_AVERAGE), MF(MOVEHISTORY_FONT_ALL) },\r
286   { MF(CLOCK_FONT_MODERATE), MF(MESSAGE_FONT_MODERATE), MF(COORD_FONT_MODERATE), MF(CONSOLE_FONT_MODERATE), MF(COMMENT_FONT_MODERATE), MF(EDITTAGS_FONT_MODERATE), MF(MOVEHISTORY_FONT_ALL) },\r
287   { MF(CLOCK_FONT_MEDIUM), MF(MESSAGE_FONT_MEDIUM), MF(COORD_FONT_MEDIUM), MF(CONSOLE_FONT_MEDIUM), MF(COMMENT_FONT_MEDIUM), MF(EDITTAGS_FONT_MEDIUM), MF(MOVEHISTORY_FONT_ALL) },\r
288   { MF(CLOCK_FONT_BULKY), MF(MESSAGE_FONT_BULKY), MF(COORD_FONT_BULKY), MF(CONSOLE_FONT_BULKY), MF(COMMENT_FONT_BULKY), MF(EDITTAGS_FONT_BULKY), MF(MOVEHISTORY_FONT_ALL) },\r
289   { MF(CLOCK_FONT_LARGE), MF(MESSAGE_FONT_LARGE), MF(COORD_FONT_LARGE), MF(CONSOLE_FONT_LARGE), MF(COMMENT_FONT_LARGE), MF(EDITTAGS_FONT_LARGE), MF(MOVEHISTORY_FONT_ALL) },\r
290   { MF(CLOCK_FONT_BIG), MF(MESSAGE_FONT_BIG), MF(COORD_FONT_BIG), MF(CONSOLE_FONT_BIG), MF(COMMENT_FONT_BIG), MF(EDITTAGS_FONT_BIG), MF(MOVEHISTORY_FONT_ALL) },\r
291   { MF(CLOCK_FONT_HUGE), MF(MESSAGE_FONT_HUGE), MF(COORD_FONT_HUGE), MF(CONSOLE_FONT_HUGE), MF(COMMENT_FONT_HUGE), MF(EDITTAGS_FONT_HUGE), MF(MOVEHISTORY_FONT_ALL) },\r
292   { MF(CLOCK_FONT_GIANT), MF(MESSAGE_FONT_GIANT), MF(COORD_FONT_GIANT), MF(CONSOLE_FONT_GIANT), MF(COMMENT_FONT_GIANT), MF(EDITTAGS_FONT_GIANT), MF(MOVEHISTORY_FONT_ALL) },\r
293   { MF(CLOCK_FONT_COLOSSAL), MF(MESSAGE_FONT_COLOSSAL), MF(COORD_FONT_COLOSSAL), MF(CONSOLE_FONT_COLOSSAL), MF(COMMENT_FONT_COLOSSAL), MF(EDITTAGS_FONT_COLOSSAL), MF(MOVEHISTORY_FONT_ALL) },\r
294   { MF(CLOCK_FONT_TITANIC), MF(MESSAGE_FONT_TITANIC), MF(COORD_FONT_TITANIC), MF(CONSOLE_FONT_TITANIC), MF(COMMENT_FONT_TITANIC), MF(EDITTAGS_FONT_TITANIC), MF(MOVEHISTORY_FONT_ALL) },\r
295 };\r
296 \r
297 MyFont *font[NUM_SIZES][NUM_FONTS];\r
298 \r
299 typedef struct {\r
300   char *label;\r
301   int id;\r
302   HWND hwnd;\r
303   WNDPROC wndproc;\r
304 } MyButtonDesc;\r
305 \r
306 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
307 #define N_BUTTONS 5\r
308 \r
309 MyButtonDesc buttonDesc[N_BUTTONS] =\r
310 {\r
311   {"<<", IDM_ToStart, NULL, NULL},\r
312   {"<", IDM_Backward, NULL, NULL},\r
313   {"P", IDM_Pause, NULL, NULL},\r
314   {">", IDM_Forward, NULL, NULL},\r
315   {">>", IDM_ToEnd, NULL, NULL},\r
316 };\r
317 \r
318 int tinyLayout = 0, smallLayout = 0;\r
319 #define MENU_BAR_ITEMS 7\r
320 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
321   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
322   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
323 };\r
324 \r
325 \r
326 MySound sounds[(int)NSoundClasses];\r
327 MyTextAttribs textAttribs[(int)NColorClasses];\r
328 \r
329 MyColorizeAttribs colorizeAttribs[] = {\r
330   { (COLORREF)0, 0, "Shout Text" },\r
331   { (COLORREF)0, 0, "SShout/CShout" },\r
332   { (COLORREF)0, 0, "Channel 1 Text" },\r
333   { (COLORREF)0, 0, "Channel Text" },\r
334   { (COLORREF)0, 0, "Kibitz Text" },\r
335   { (COLORREF)0, 0, "Tell Text" },\r
336   { (COLORREF)0, 0, "Challenge Text" },\r
337   { (COLORREF)0, 0, "Request Text" },\r
338   { (COLORREF)0, 0, "Seek Text" },\r
339   { (COLORREF)0, 0, "Normal Text" },\r
340   { (COLORREF)0, 0, "None" }\r
341 };\r
342 \r
343 \r
344 \r
345 static char *commentTitle;\r
346 static char *commentText;\r
347 static int commentIndex;\r
348 static Boolean editComment = FALSE;\r
349 HWND commentDialog = NULL;\r
350 BOOLEAN commentDialogUp = FALSE;\r
351 static int commentX, commentY, commentH, commentW;\r
352 \r
353 static char *analysisTitle;\r
354 static char *analysisText;\r
355 HWND analysisDialog = NULL;\r
356 BOOLEAN analysisDialogUp = FALSE;\r
357 static int analysisX, analysisY, analysisH, analysisW;\r
358 \r
359 char errorTitle[MSG_SIZ];\r
360 char errorMessage[2*MSG_SIZ];\r
361 HWND errorDialog = NULL;\r
362 BOOLEAN moveErrorMessageUp = FALSE;\r
363 BOOLEAN consoleEcho = TRUE;\r
364 CHARFORMAT consoleCF;\r
365 COLORREF consoleBackgroundColor;\r
366 \r
367 char *programVersion;\r
368 \r
369 #define CPReal 1\r
370 #define CPComm 2\r
371 #define CPSock 3\r
372 #define CPRcmd 4\r
373 typedef int CPKind;\r
374 \r
375 typedef struct {\r
376   CPKind kind;\r
377   HANDLE hProcess;\r
378   DWORD pid;\r
379   HANDLE hTo;\r
380   HANDLE hFrom;\r
381   SOCKET sock;\r
382   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
383 } ChildProc;\r
384 \r
385 #define INPUT_SOURCE_BUF_SIZE 4096\r
386 \r
387 typedef struct _InputSource {\r
388   CPKind kind;\r
389   HANDLE hFile;\r
390   SOCKET sock;\r
391   int lineByLine;\r
392   HANDLE hThread;\r
393   DWORD id;\r
394   char buf[INPUT_SOURCE_BUF_SIZE];\r
395   char *next;\r
396   DWORD count;\r
397   int error;\r
398   InputCallback func;\r
399   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
400   VOIDSTAR closure;\r
401 } InputSource;\r
402 \r
403 InputSource *consoleInputSource;\r
404 \r
405 DCB dcb;\r
406 \r
407 /* forward */\r
408 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
409 VOID ConsoleCreate();\r
410 LRESULT CALLBACK\r
411   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
412 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
413 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
414 VOID ParseCommSettings(char *arg, DCB *dcb);\r
415 LRESULT CALLBACK\r
416   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
417 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
418 void ParseIcsTextMenu(char *icsTextMenuString);\r
419 VOID PopUpMoveDialog(char firstchar);\r
420 VOID PopUpNameDialog(char firstchar);\r
421 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
422 \r
423 /* [AS] */\r
424 int NewGameFRC();\r
425 int GameListOptions();\r
426 \r
427 HWND moveHistoryDialog = NULL;\r
428 BOOLEAN moveHistoryDialogUp = FALSE;\r
429 \r
430 WindowPlacement wpMoveHistory;\r
431 \r
432 HWND evalGraphDialog = NULL;\r
433 BOOLEAN evalGraphDialogUp = FALSE;\r
434 \r
435 WindowPlacement wpEvalGraph;\r
436 \r
437 HWND engineOutputDialog = NULL;\r
438 BOOLEAN engineOutputDialogUp = FALSE;\r
439 \r
440 WindowPlacement wpEngineOutput;\r
441 WindowPlacement wpGameList;\r
442 WindowPlacement wpConsole;\r
443 \r
444 VOID MoveHistoryPopUp();\r
445 VOID MoveHistoryPopDown();\r
446 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
447 BOOL MoveHistoryIsUp();\r
448 \r
449 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
450 VOID EvalGraphPopUp();\r
451 VOID EvalGraphPopDown();\r
452 BOOL EvalGraphIsUp();\r
453 \r
454 VOID EngineOutputPopUp();\r
455 VOID EngineOutputPopDown();\r
456 BOOL EngineOutputIsUp();\r
457 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
458 \r
459 VOID EngineOptionsPopup(); // [HGM] settings\r
460 \r
461 VOID GothicPopUp(char *title, VariantClass variant);\r
462 /*\r
463  * Setting "frozen" should disable all user input other than deleting\r
464  * the window.  We do this while engines are initializing themselves.\r
465  */\r
466 static int frozen = 0;\r
467 static int oldMenuItemState[MENU_BAR_ITEMS];\r
468 void FreezeUI()\r
469 {\r
470   HMENU hmenu;\r
471   int i;\r
472 \r
473   if (frozen) return;\r
474   frozen = 1;\r
475   hmenu = GetMenu(hwndMain);\r
476   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
477     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
478   }\r
479   DrawMenuBar(hwndMain);\r
480 }\r
481 \r
482 /* Undo a FreezeUI */\r
483 void ThawUI()\r
484 {\r
485   HMENU hmenu;\r
486   int i;\r
487 \r
488   if (!frozen) return;\r
489   frozen = 0;\r
490   hmenu = GetMenu(hwndMain);\r
491   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
492     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
493   }\r
494   DrawMenuBar(hwndMain);\r
495 }\r
496 \r
497 static int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
498 \r
499 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
500 #ifdef JAWS\r
501 #include "jaws.c"\r
502 #else\r
503 #define JAWS_INIT\r
504 #define JAWS_ARGS\r
505 #define JAWS_ALT_INTERCEPT\r
506 #define JAWS_KB_NAVIGATION\r
507 #define JAWS_MENU_ITEMS\r
508 #define JAWS_SILENCE\r
509 #define JAWS_REPLAY\r
510 #define JAWS_ACCEL\r
511 #define JAWS_COPYRIGHT\r
512 #define JAWS_DELETE(X) X\r
513 #define SAYMACHINEMOVE()\r
514 #define SAY(X)\r
515 #endif\r
516 \r
517 /*---------------------------------------------------------------------------*\\r
518  *\r
519  * WinMain\r
520  *\r
521 \*---------------------------------------------------------------------------*/\r
522 \r
523 int APIENTRY\r
524 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
525         LPSTR lpCmdLine, int nCmdShow)\r
526 {\r
527   MSG msg;\r
528   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
529 //  INITCOMMONCONTROLSEX ex;\r
530 \r
531   debugFP = stderr;\r
532 \r
533   LoadLibrary("RICHED32.DLL");\r
534   consoleCF.cbSize = sizeof(CHARFORMAT);\r
535 \r
536   if (!InitApplication(hInstance)) {\r
537     return (FALSE);\r
538   }\r
539   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
540     return (FALSE);\r
541   }\r
542 \r
543   JAWS_INIT\r
544 \r
545 //  InitCommonControlsEx(&ex);\r
546   InitCommonControls();\r
547 \r
548   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
549   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
550   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
551 \r
552   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
553 \r
554   while (GetMessage(&msg, /* message structure */\r
555                     NULL, /* handle of window receiving the message */\r
556                     0,    /* lowest message to examine */\r
557                     0))   /* highest message to examine */\r
558     {\r
559 \r
560       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
561         // [HGM] navigate: switch between all windows with tab\r
562         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
563         int i, currentElement = 0;\r
564 \r
565         // first determine what element of the chain we come from (if any)\r
566         if(appData.icsActive) {\r
567             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
568             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
569         }\r
570         if(engineOutputDialog && EngineOutputIsUp()) {\r
571             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
572             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
573         }\r
574         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
575             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
576         }\r
577         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
578         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
579         if(msg.hwnd == e1)                 currentElement = 2; else\r
580         if(msg.hwnd == e2)                 currentElement = 3; else\r
581         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
582         if(msg.hwnd == mh)                currentElement = 4; else\r
583         if(msg.hwnd == evalGraphDialog)    currentElement = 6; else\r
584         if(msg.hwnd == hText)  currentElement = 5; else\r
585         if(msg.hwnd == hInput) currentElement = 6; else\r
586         for (i = 0; i < N_BUTTONS; i++) {\r
587             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
588         }\r
589 \r
590         // determine where to go to\r
591         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
592           do {\r
593             currentElement = (currentElement + direction) % 7;\r
594             switch(currentElement) {\r
595                 case 0:\r
596                   h = hwndMain; break; // passing this case always makes the loop exit\r
597                 case 1:\r
598                   h = buttonDesc[0].hwnd; break; // could be NULL\r
599                 case 2:\r
600                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
601                   h = e1; break;\r
602                 case 3:\r
603                   if(!EngineOutputIsUp()) continue;\r
604                   h = e2; break;\r
605                 case 4:\r
606                   if(!MoveHistoryIsUp()) continue;\r
607                   h = mh; break;\r
608 //              case 6: // input to eval graph does not seem to get here!\r
609 //                if(!EvalGraphIsUp()) continue;\r
610 //                h = evalGraphDialog; break;\r
611                 case 5:\r
612                   if(!appData.icsActive) continue;\r
613                   SAY("display");\r
614                   h = hText; break;\r
615                 case 6:\r
616                   if(!appData.icsActive) continue;\r
617                   SAY("input");\r
618                   h = hInput; break;\r
619             }\r
620           } while(h == 0);\r
621 \r
622           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
623           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
624           SetFocus(h);\r
625 \r
626           continue; // this message now has been processed\r
627         }\r
628       }\r
629 \r
630       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
631           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
632           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
633           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
634           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
635           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
636           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
637           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
638           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
639           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
640         int done = 0, i; // [HGM] chat: dispatch cat-box messages\r
641         for(i=0; i<MAX_CHAT; i++) \r
642             if(chatHandle[i] && IsDialogMessage(chatHandle[i], &msg)) {\r
643                 done = 1; break;\r
644         }\r
645         if(done) continue; // [HGM] chat: end patch\r
646         TranslateMessage(&msg); /* Translates virtual key codes */\r
647         DispatchMessage(&msg);  /* Dispatches message to window */\r
648       }\r
649     }\r
650 \r
651 \r
652   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
653 }\r
654 \r
655 /*---------------------------------------------------------------------------*\\r
656  *\r
657  * Initialization functions\r
658  *\r
659 \*---------------------------------------------------------------------------*/\r
660 \r
661 void\r
662 SetUserLogo()\r
663 {   // update user logo if necessary\r
664     static char oldUserName[MSG_SIZ], *curName;\r
665 \r
666     if(appData.autoLogo) {\r
667           curName = UserName();\r
668           if(strcmp(curName, oldUserName)) {\r
669                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
670                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
671                 strcpy(oldUserName, curName);\r
672           }\r
673     }\r
674 }\r
675 \r
676 BOOL\r
677 InitApplication(HINSTANCE hInstance)\r
678 {\r
679   WNDCLASS wc;\r
680 \r
681   /* Fill in window class structure with parameters that describe the */\r
682   /* main window. */\r
683 \r
684   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
685   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
686   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
687   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
688   wc.hInstance     = hInstance;         /* Owner of this class */\r
689   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
690   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
691   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
692   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
693   wc.lpszClassName = szAppName;                 /* Name to register as */\r
694 \r
695   /* Register the window class and return success/failure code. */\r
696   if (!RegisterClass(&wc)) return FALSE;\r
697 \r
698   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
699   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
700   wc.cbClsExtra    = 0;\r
701   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
702   wc.hInstance     = hInstance;\r
703   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
704   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
705   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
706   wc.lpszMenuName  = NULL;\r
707   wc.lpszClassName = szConsoleName;\r
708 \r
709   if (!RegisterClass(&wc)) return FALSE;\r
710   return TRUE;\r
711 }\r
712 \r
713 \r
714 /* Set by InitInstance, used by EnsureOnScreen */\r
715 int screenHeight, screenWidth;\r
716 \r
717 void\r
718 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
719 {\r
720 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
721   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
722   if (*x > screenWidth - 32) *x = 0;\r
723   if (*y > screenHeight - 32) *y = 0;\r
724   if (*x < minX) *x = minX;\r
725   if (*y < minY) *y = minY;\r
726 }\r
727 \r
728 BOOL\r
729 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
730 {\r
731   HWND hwnd; /* Main window handle. */\r
732   int ibs;\r
733   WINDOWPLACEMENT wp;\r
734   char *filepart;\r
735 \r
736   hInst = hInstance;    /* Store instance handle in our global variable */\r
737 \r
738   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
739     *filepart = NULLCHAR;\r
740   } else {\r
741     GetCurrentDirectory(MSG_SIZ, installDir);\r
742   }\r
743   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
744   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
745   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
746   if (appData.debugMode) {\r
747     debugFP = fopen(appData.nameOfDebugFile, "w");\r
748     setbuf(debugFP, NULL);\r
749   }\r
750 \r
751   InitBackEnd1();\r
752 \r
753 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
754 //  InitEngineUCI( installDir, &second );\r
755 \r
756   /* Create a main window for this application instance. */\r
757   hwnd = CreateWindow(szAppName, szTitle,\r
758                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
759                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
760                       NULL, NULL, hInstance, NULL);\r
761   hwndMain = hwnd;\r
762 \r
763   /* If window could not be created, return "failure" */\r
764   if (!hwnd) {\r
765     return (FALSE);\r
766   }\r
767 \r
768   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
769   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
770       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
771 \r
772       if (first.programLogo == NULL && appData.debugMode) {\r
773           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
774       }\r
775   } else if(appData.autoLogo) {\r
776       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
777         char buf[MSG_SIZ];\r
778         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
779         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
780       }\r
781   }\r
782 \r
783   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
784       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
785 \r
786       if (second.programLogo == NULL && appData.debugMode) {\r
787           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
788       }\r
789   } else if(appData.autoLogo) {\r
790       char buf[MSG_SIZ];\r
791       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
792         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
793         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
794       } else\r
795       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
796         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
797         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
798       }\r
799   }\r
800 \r
801   SetUserLogo();\r
802 \r
803   iconWhite = LoadIcon(hInstance, "icon_white");\r
804   iconBlack = LoadIcon(hInstance, "icon_black");\r
805   iconCurrent = iconWhite;\r
806   InitDrawingColors();\r
807   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
808   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
809   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
810     /* Compute window size for each board size, and use the largest\r
811        size that fits on this screen as the default. */\r
812     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
813     if (boardSize == (BoardSize)-1 &&\r
814         winH <= screenHeight\r
815            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
816         && winW <= screenWidth) {\r
817       boardSize = (BoardSize)ibs;\r
818     }\r
819   }\r
820 \r
821   InitDrawingSizes(boardSize, 0);\r
822   InitMenuChecks();\r
823   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
824 \r
825   /* [AS] Load textures if specified */\r
826   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
827   \r
828   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
829       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
830       liteBackTextureMode = appData.liteBackTextureMode;\r
831 \r
832       if (liteBackTexture == NULL && appData.debugMode) {\r
833           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
834       }\r
835   }\r
836   \r
837   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
838       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
839       darkBackTextureMode = appData.darkBackTextureMode;\r
840 \r
841       if (darkBackTexture == NULL && appData.debugMode) {\r
842           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
843       }\r
844   }\r
845 \r
846   mysrandom( (unsigned) time(NULL) );\r
847 \r
848   /* [AS] Restore layout */\r
849   if( wpMoveHistory.visible ) {\r
850       MoveHistoryPopUp();\r
851   }\r
852 \r
853   if( wpEvalGraph.visible ) {\r
854       EvalGraphPopUp();\r
855   }\r
856 \r
857   if( wpEngineOutput.visible ) {\r
858       EngineOutputPopUp();\r
859   }\r
860 \r
861   InitBackEnd2();\r
862 \r
863   /* Make the window visible; update its client area; and return "success" */\r
864   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
865   wp.length = sizeof(WINDOWPLACEMENT);\r
866   wp.flags = 0;\r
867   wp.showCmd = nCmdShow;\r
868   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
869   wp.rcNormalPosition.left = boardX;\r
870   wp.rcNormalPosition.right = boardX + winWidth;\r
871   wp.rcNormalPosition.top = boardY;\r
872   wp.rcNormalPosition.bottom = boardY + winHeight;\r
873   SetWindowPlacement(hwndMain, &wp);\r
874 \r
875   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
876                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
877 \r
878   if (hwndConsole) {\r
879 #if AOT_CONSOLE\r
880     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
881                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
882 #endif\r
883     ShowWindow(hwndConsole, nCmdShow);\r
884   }\r
885   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
886   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
887 \r
888   return TRUE;\r
889 \r
890 }\r
891 \r
892 \r
893 typedef enum {\r
894   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
895   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
896   ArgSettingsFilename,\r
897   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
898 } ArgType;\r
899 \r
900 typedef struct {\r
901   char *argName;\r
902   ArgType argType;\r
903   /***\r
904   union {\r
905     String *pString;       // ArgString\r
906     int *pInt;             // ArgInt\r
907     float *pFloat;         // ArgFloat\r
908     Boolean *pBoolean;     // ArgBoolean\r
909     COLORREF *pColor;      // ArgColor\r
910     ColorClass cc;         // ArgAttribs\r
911     String *pFilename;     // ArgFilename\r
912     BoardSize *pBoardSize; // ArgBoardSize\r
913     int whichFont;         // ArgFont\r
914     DCB *pDCB;             // ArgCommSettings\r
915     String *pFilename;     // ArgSettingsFilename\r
916   } argLoc;\r
917   ***/\r
918   LPVOID argLoc;\r
919   BOOL save;\r
920 } ArgDescriptor;\r
921 \r
922 int junk;\r
923 ArgDescriptor argDescriptors[] = {\r
924   /* positional arguments */\r
925   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
926   { "", ArgNone, NULL },\r
927   /* keyword arguments */\r
928   JAWS_ARGS\r
929   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
930   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
931   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
932   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
933   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
934   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
935   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
936   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
937   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
938   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
939   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
940   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
941   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
942   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
943   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
944   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
945   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
946   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
947     FALSE },\r
948   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
949     FALSE },\r
950   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
951     FALSE },\r
952   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
953   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
954     FALSE },\r
955   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
956   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
957   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
958   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
959   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
960   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
961   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
962   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
963   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
964   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
965   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
966   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
967   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
968   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
969   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
970   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
971   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
972   /*!!bitmapDirectory?*/\r
973   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
974   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
975   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
976   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
977   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
978   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
979   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
980   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
981   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
982   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
983   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
984   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
985   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
986   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
987   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
988   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
989   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
990   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
991   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
992   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
993   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
994   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
995   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
996   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
997   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
998   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
999   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
1000   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
1001   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
1002   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
1003   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
1004   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
1005   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
1006   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1007   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
1008   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
1009   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
1010   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
1011   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
1012   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1013   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
1014   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1015   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1016   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1017   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1018   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1019   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1020   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
1021   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1022   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1023   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1024   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1025   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1026   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1027   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1028   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1029   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1030   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1031   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1032   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1033   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1034   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1035   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1036   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1037   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1038   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1039   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1040   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1041   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1042   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1043   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1044   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1045   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1046   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1047   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1048   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1049   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1050   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1051   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1052   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1053   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1054   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1055   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1056   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1057   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1058   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1059   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1060   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1061   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1062   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1063   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1064   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1065   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1066   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1067     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1068   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1069   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1070   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1071   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1072   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1073   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1074   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1075   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1076     TRUE }, /* must come after all fonts */\r
1077   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1078   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1079     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1080   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1081   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1082   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1083   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1084   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1085   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1086   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1087   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1088   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1089   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1090   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1091   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1092   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1093   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1094   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1095   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1096   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1097   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1098   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1099   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1100   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1101   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1102   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1103   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1104   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1105   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1106   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1107   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1108 #if 0\r
1109   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1110   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1111 #endif\r
1112   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1113   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1114   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1115   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1116   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1117   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1118   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1119   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1120   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1121   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1122   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1123   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1124   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1125   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1126   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1127   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1128   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1129   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1130   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1131   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1132   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1133   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1134   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1135   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1136   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1137   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1138   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1139   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1140   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1141   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1142   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1143   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1144   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1145   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1146   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1147   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1148   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1149   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1150   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1151   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1152   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1153   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1154   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1155   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1156   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1157   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1158   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1159   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1160   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1161   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1162   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1163   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1164   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1165   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1166   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1167   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1168   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1169   { "highlightLastMove", ArgBoolean,\r
1170     (LPVOID) &appData.highlightLastMove, TRUE },\r
1171   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1172   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1173   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1174   { "highlightDragging", ArgBoolean,\r
1175     (LPVOID) &appData.highlightDragging, TRUE },\r
1176   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1177   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1178   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1179   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1180   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1181   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1182   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1183   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1184   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1185   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1186   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1187   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1188   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1189   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1190   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1191   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1192   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1193   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1194   { "soundShout", ArgFilename,\r
1195     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1196   { "soundSShout", ArgFilename,\r
1197     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1198   { "soundChannel1", ArgFilename,\r
1199     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1200   { "soundChannel", ArgFilename,\r
1201     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1202   { "soundKibitz", ArgFilename,\r
1203     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1204   { "soundTell", ArgFilename,\r
1205     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1206   { "soundChallenge", ArgFilename,\r
1207     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1208   { "soundRequest", ArgFilename,\r
1209     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1210   { "soundSeek", ArgFilename,\r
1211     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1212   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1213   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1214   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1215   { "soundIcsLoss", ArgFilename, \r
1216     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1217   { "soundIcsDraw", ArgFilename, \r
1218     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1219   { "soundIcsUnfinished", ArgFilename, \r
1220     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1221   { "soundIcsAlarm", ArgFilename, \r
1222     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1223   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1224   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1225   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1226   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1227   { "reuseChessPrograms", ArgBoolean,\r
1228     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1229   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1230   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1231   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1232   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1233   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1234   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1235   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1236   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1237   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1238   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1239   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1240   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1241   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1242   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1243   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1244     TRUE },\r
1245   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1246     TRUE },\r
1247   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1248   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1249   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1250   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1251   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1252   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1253   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1254   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1255   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1256   /* [AS] New features */\r
1257   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1258   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1259   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1260   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1261   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1262   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1263   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1264   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1265   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1266   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1267   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1268   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1269   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1270   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1271   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1272   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1273   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1274   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1275   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1276   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1277   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1278   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1279   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1280   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1281   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1282   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1283   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1284   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1285   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1286   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1287   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1288   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1289   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1290   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1291   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1292   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1293   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1294   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1295   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1296   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1297   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1298   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1299   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1300   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1301   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1302   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1303   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1304   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1305   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1306   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1307 \r
1308   /* [HGM] board-size, adjudication and misc. options */\r
1309   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1310   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1311   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1312   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1313   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1314   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1315   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1316   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1317   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1318   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1319   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1320   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1321   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1322   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1323   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1324   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1325   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1326   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1327   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1328   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1329   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1330   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1331   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1332   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1333   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1334   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1335   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1336   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1337   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1338   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1339   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1340   { "keepAlive", ArgInt, (LPVOID) &appData.keepAlive, FALSE },\r
1341   { "icstype", ArgInt, (LPVOID) &ics_type, FALSE },\r
1342 \r
1343 #ifdef ZIPPY\r
1344   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1345   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1346   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1347   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1348   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1349   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1350   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1351   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1352   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1353   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1354   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1355   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1356   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1357     FALSE },\r
1358   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1359   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1360   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1361   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1362   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1363   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1364   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1365     FALSE },\r
1366   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1367   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1368   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1369   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1370   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1371   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1372   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1373   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1374   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1375   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1376   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1377   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1378   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1379   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1380   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1381   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1382   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1383   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1384   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1385 #endif\r
1386   /* [HGM] options for broadcasting and time odds */\r
1387   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1388   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1389   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1390   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1391   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1392   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1393   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1394   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1395   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1396   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1397   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1398 \r
1399   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1400   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1401   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1402   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1403   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1404   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1405   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1406   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1407   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1408   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1409   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1410   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1411   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1412   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1413   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1414   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1415   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1416   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1417   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1418   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1419   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1420   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1421   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1422   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1423   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1424   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1425   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1426   /* [AS] Layout stuff */\r
1427   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1428   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1429   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1430   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1431   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1432 \r
1433   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1434   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1435   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1436   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1437   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1438 \r
1439   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1440   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1441   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1442   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1443   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1444 \r
1445   { NULL, ArgNone, NULL, FALSE }\r
1446 };\r
1447 \r
1448 \r
1449 /* Kludge for indirection files on command line */\r
1450 char* lastIndirectionFilename;\r
1451 ArgDescriptor argDescriptorIndirection =\r
1452 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1453 \r
1454 \r
1455 VOID\r
1456 ExitArgError(char *msg, char *badArg)\r
1457 {\r
1458   char buf[MSG_SIZ];\r
1459 \r
1460   sprintf(buf, "%s %s", msg, badArg);\r
1461   DisplayFatalError(buf, 0, 2);\r
1462   exit(2);\r
1463 }\r
1464 \r
1465 /* Command line font name parser.  NULL name means do nothing.\r
1466    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1467    For backward compatibility, syntax without the colon is also\r
1468    accepted, but font names with digits in them won't work in that case.\r
1469 */\r
1470 VOID\r
1471 ParseFontName(char *name, MyFontParams *mfp)\r
1472 {\r
1473   char *p, *q;\r
1474   if (name == NULL) return;\r
1475   p = name;\r
1476   q = strchr(p, ':');\r
1477   if (q) {\r
1478     if (q - p >= sizeof(mfp->faceName))\r
1479       ExitArgError("Font name too long:", name);\r
1480     memcpy(mfp->faceName, p, q - p);\r
1481     mfp->faceName[q - p] = NULLCHAR;\r
1482     p = q + 1;\r
1483   } else {\r
1484     q = mfp->faceName;\r
1485     while (*p && !isdigit(*p)) {\r
1486       *q++ = *p++;\r
1487       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1488         ExitArgError("Font name too long:", name);\r
1489     }\r
1490     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1491     *q = NULLCHAR;\r
1492   }\r
1493   if (!*p) ExitArgError("Font point size missing:", name);\r
1494   mfp->pointSize = (float) atof(p);\r
1495   mfp->bold = (strchr(p, 'b') != NULL);\r
1496   mfp->italic = (strchr(p, 'i') != NULL);\r
1497   mfp->underline = (strchr(p, 'u') != NULL);\r
1498   mfp->strikeout = (strchr(p, 's') != NULL);\r
1499 }\r
1500 \r
1501 /* Color name parser.\r
1502    X version accepts X color names, but this one\r
1503    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1504 COLORREF\r
1505 ParseColorName(char *name)\r
1506 {\r
1507   int red, green, blue, count;\r
1508   char buf[MSG_SIZ];\r
1509 \r
1510   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1511   if (count != 3) {\r
1512     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1513       &red, &green, &blue);\r
1514   }\r
1515   if (count != 3) {\r
1516     sprintf(buf, "Can't parse color name %s", name);\r
1517     DisplayError(buf, 0);\r
1518     return RGB(0, 0, 0);\r
1519   }\r
1520   return PALETTERGB(red, green, blue);\r
1521 }\r
1522 \r
1523 \r
1524 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1525 {\r
1526   char *e = argValue;\r
1527   int eff = 0;\r
1528 \r
1529   while (*e) {\r
1530     if (*e == 'b')      eff |= CFE_BOLD;\r
1531     else if (*e == 'i') eff |= CFE_ITALIC;\r
1532     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1533     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1534     else if (*e == '#' || isdigit(*e)) break;\r
1535     e++;\r
1536   }\r
1537   *effects = eff;\r
1538   *color   = ParseColorName(e);\r
1539 }\r
1540 \r
1541 \r
1542 BoardSize\r
1543 ParseBoardSize(char *name)\r
1544 {\r
1545   BoardSize bs = SizeTiny;\r
1546   while (sizeInfo[bs].name != NULL) {\r
1547     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1548     bs++;\r
1549   }\r
1550   ExitArgError("Unrecognized board size value", name);\r
1551   return bs; /* not reached */\r
1552 }\r
1553 \r
1554 \r
1555 char\r
1556 StringGet(void *getClosure)\r
1557 {\r
1558   char **p = (char **) getClosure;\r
1559   return *((*p)++);\r
1560 }\r
1561 \r
1562 char\r
1563 FileGet(void *getClosure)\r
1564 {\r
1565   int c;\r
1566   FILE* f = (FILE*) getClosure;\r
1567 \r
1568   c = getc(f);\r
1569   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1570   if (c == EOF)\r
1571     return NULLCHAR;\r
1572   else\r
1573     return (char) c;\r
1574 }\r
1575 \r
1576 /* Parse settings file named "name". If file found, return the\r
1577    full name in fullname and return TRUE; else return FALSE */\r
1578 BOOLEAN\r
1579 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1580 {\r
1581   char *dummy;\r
1582   FILE *f;\r
1583   int ok; char buf[MSG_SIZ];\r
1584 \r
1585   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1586   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1587     sprintf(buf, "%s.ini", name);\r
1588     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1589   }\r
1590   if (ok) {\r
1591     f = fopen(fullname, "r");\r
1592     if (f != NULL) {\r
1593       ParseArgs(FileGet, f);\r
1594       fclose(f);\r
1595       return TRUE;\r
1596     }\r
1597   }\r
1598   return FALSE;\r
1599 }\r
1600 \r
1601 VOID\r
1602 ParseArgs(GetFunc get, void *cl)\r
1603 {\r
1604   char argName[ARG_MAX];\r
1605   char argValue[ARG_MAX];\r
1606   ArgDescriptor *ad;\r
1607   char start;\r
1608   char *q;\r
1609   int i, octval;\r
1610   char ch;\r
1611   int posarg = 0;\r
1612 \r
1613   ch = get(cl);\r
1614   for (;;) {\r
1615     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1616     if (ch == NULLCHAR) break;\r
1617     if (ch == ';') {\r
1618       /* Comment to end of line */\r
1619       ch = get(cl);\r
1620       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1621       continue;\r
1622     } else if (ch == '/' || ch == '-') {\r
1623       /* Switch */\r
1624       q = argName;\r
1625       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1626              ch != '\n' && ch != '\t') {\r
1627         *q++ = ch;\r
1628         ch = get(cl);\r
1629       }\r
1630       *q = NULLCHAR;\r
1631 \r
1632       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1633         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1634 \r
1635       if (ad->argName == NULL)\r
1636         ExitArgError("Unrecognized argument", argName);\r
1637 \r
1638     } else if (ch == '@') {\r
1639       /* Indirection file */\r
1640       ad = &argDescriptorIndirection;\r
1641       ch = get(cl);\r
1642     } else {\r
1643       /* Positional argument */\r
1644       ad = &argDescriptors[posarg++];\r
1645       strcpy(argName, ad->argName);\r
1646     }\r
1647 \r
1648     if (ad->argType == ArgTrue) {\r
1649       *(Boolean *) ad->argLoc = TRUE;\r
1650       continue;\r
1651     }\r
1652     if (ad->argType == ArgFalse) {\r
1653       *(Boolean *) ad->argLoc = FALSE;\r
1654       continue;\r
1655     }\r
1656 \r
1657     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1658     if (ch == NULLCHAR || ch == '\n') {\r
1659       ExitArgError("No value provided for argument", argName);\r
1660     }\r
1661     q = argValue;\r
1662     if (ch == '{') {\r
1663       // Quoting with { }.  No characters have to (or can) be escaped.\r
1664       // Thus the string cannot contain a '}' character.\r
1665       start = ch;\r
1666       ch = get(cl);\r
1667       while (start) {\r
1668         switch (ch) {\r
1669         case NULLCHAR:\r
1670           start = NULLCHAR;\r
1671           break;\r
1672           \r
1673         case '}':\r
1674           ch = get(cl);\r
1675           start = NULLCHAR;\r
1676           break;\r
1677 \r
1678         default:\r
1679           *q++ = ch;\r
1680           ch = get(cl);\r
1681           break;\r
1682         }\r
1683       }   \r
1684     } else if (ch == '\'' || ch == '"') {\r
1685       // Quoting with ' ' or " ", with \ as escape character.\r
1686       // Inconvenient for long strings that may contain Windows filenames.\r
1687       start = ch;\r
1688       ch = get(cl);\r
1689       while (start) {\r
1690         switch (ch) {\r
1691         case NULLCHAR:\r
1692           start = NULLCHAR;\r
1693           break;\r
1694 \r
1695         default:\r
1696         not_special:\r
1697           *q++ = ch;\r
1698           ch = get(cl);\r
1699           break;\r
1700 \r
1701         case '\'':\r
1702         case '\"':\r
1703           if (ch == start) {\r
1704             ch = get(cl);\r
1705             start = NULLCHAR;\r
1706             break;\r
1707           } else {\r
1708             goto not_special;\r
1709           }\r
1710 \r
1711         case '\\':\r
1712           if (ad->argType == ArgFilename\r
1713               || ad->argType == ArgSettingsFilename) {\r
1714               goto not_special;\r
1715           }\r
1716           ch = get(cl);\r
1717           switch (ch) {\r
1718           case NULLCHAR:\r
1719             ExitArgError("Incomplete \\ escape in value for", argName);\r
1720             break;\r
1721           case 'n':\r
1722             *q++ = '\n';\r
1723             ch = get(cl);\r
1724             break;\r
1725           case 'r':\r
1726             *q++ = '\r';\r
1727             ch = get(cl);\r
1728             break;\r
1729           case 't':\r
1730             *q++ = '\t';\r
1731             ch = get(cl);\r
1732             break;\r
1733           case 'b':\r
1734             *q++ = '\b';\r
1735             ch = get(cl);\r
1736             break;\r
1737           case 'f':\r
1738             *q++ = '\f';\r
1739             ch = get(cl);\r
1740             break;\r
1741           default:\r
1742             octval = 0;\r
1743             for (i = 0; i < 3; i++) {\r
1744               if (ch >= '0' && ch <= '7') {\r
1745                 octval = octval*8 + (ch - '0');\r
1746                 ch = get(cl);\r
1747               } else {\r
1748                 break;\r
1749               }\r
1750             }\r
1751             if (i > 0) {\r
1752               *q++ = (char) octval;\r
1753             } else {\r
1754               *q++ = ch;\r
1755               ch = get(cl);\r
1756             }\r
1757             break;\r
1758           }\r
1759           break;\r
1760         }\r
1761       }\r
1762     } else {\r
1763       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1764         *q++ = ch;\r
1765         ch = get(cl);\r
1766       }\r
1767     }\r
1768     *q = NULLCHAR;\r
1769 \r
1770     switch (ad->argType) {\r
1771     case ArgInt:\r
1772       *(int *) ad->argLoc = atoi(argValue);\r
1773       break;\r
1774 \r
1775     case ArgX:\r
1776       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1777       break;\r
1778 \r
1779     case ArgY:\r
1780       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1781       break;\r
1782 \r
1783     case ArgZ:\r
1784       *(int *) ad->argLoc = atoi(argValue);\r
1785       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1786       break;\r
1787 \r
1788     case ArgFloat:\r
1789       *(float *) ad->argLoc = (float) atof(argValue);\r
1790       break;\r
1791 \r
1792     case ArgString:\r
1793     case ArgFilename:\r
1794       *(char **) ad->argLoc = strdup(argValue);\r
1795       break;\r
1796 \r
1797     case ArgSettingsFilename:\r
1798       {\r
1799         char fullname[MSG_SIZ];\r
1800         if (ParseSettingsFile(argValue, fullname)) {\r
1801           if (ad->argLoc != NULL) {\r
1802             *(char **) ad->argLoc = strdup(fullname);\r
1803           }\r
1804         } else {\r
1805           if (ad->argLoc != NULL) {\r
1806           } else {\r
1807             ExitArgError("Failed to open indirection file", argValue);\r
1808           }\r
1809         }\r
1810       }\r
1811       break;\r
1812 \r
1813     case ArgBoolean:\r
1814       switch (argValue[0]) {\r
1815       case 't':\r
1816       case 'T':\r
1817         *(Boolean *) ad->argLoc = TRUE;\r
1818         break;\r
1819       case 'f':\r
1820       case 'F':\r
1821         *(Boolean *) ad->argLoc = FALSE;\r
1822         break;\r
1823       default:\r
1824         ExitArgError("Unrecognized boolean argument value", argValue);\r
1825         break;\r
1826       }\r
1827       break;\r
1828 \r
1829     case ArgColor:\r
1830       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1831       break;\r
1832 \r
1833     case ArgAttribs: {\r
1834       ColorClass cc = (ColorClass)ad->argLoc;\r
1835       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1836       }\r
1837       break;\r
1838       \r
1839     case ArgBoardSize:\r
1840       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1841       break;\r
1842 \r
1843     case ArgFont:\r
1844       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1845       break;\r
1846 \r
1847     case ArgCommSettings:\r
1848       ParseCommSettings(argValue, &dcb);\r
1849       break;\r
1850 \r
1851     case ArgNone:\r
1852       ExitArgError("Unrecognized argument", argValue);\r
1853       break;\r
1854     case ArgTrue:\r
1855     case ArgFalse: ;\r
1856     }\r
1857   }\r
1858 }\r
1859 \r
1860 VOID\r
1861 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1862 {\r
1863   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1864   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1865   DeleteDC(hdc);\r
1866   lf->lfWidth = 0;\r
1867   lf->lfEscapement = 0;\r
1868   lf->lfOrientation = 0;\r
1869   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1870   lf->lfItalic = mfp->italic;\r
1871   lf->lfUnderline = mfp->underline;\r
1872   lf->lfStrikeOut = mfp->strikeout;\r
1873   lf->lfCharSet = DEFAULT_CHARSET;\r
1874   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1875   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1876   lf->lfQuality = DEFAULT_QUALITY;\r
1877   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1878   strcpy(lf->lfFaceName, mfp->faceName);\r
1879 }\r
1880 \r
1881 VOID\r
1882 CreateFontInMF(MyFont *mf)\r
1883 {\r
1884   LFfromMFP(&mf->lf, &mf->mfp);\r
1885   if (mf->hf) DeleteObject(mf->hf);\r
1886   mf->hf = CreateFontIndirect(&mf->lf);\r
1887 }\r
1888 \r
1889 VOID\r
1890 SetDefaultTextAttribs()\r
1891 {\r
1892   ColorClass cc;\r
1893   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1894     ParseAttribs(&textAttribs[cc].color, \r
1895                  &textAttribs[cc].effects, \r
1896                  defaultTextAttribs[cc]);\r
1897   }\r
1898 }\r
1899 \r
1900 VOID\r
1901 SetDefaultSounds()\r
1902 {\r
1903   ColorClass cc;\r
1904   SoundClass sc;\r
1905   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1906     textAttribs[cc].sound.name = strdup("");\r
1907     textAttribs[cc].sound.data = NULL;\r
1908   }\r
1909   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1910     sounds[sc].name = strdup("");\r
1911     sounds[sc].data = NULL;\r
1912   }\r
1913   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1914 }\r
1915 \r
1916 VOID\r
1917 LoadAllSounds()\r
1918 {\r
1919   ColorClass cc;\r
1920   SoundClass sc;\r
1921   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1922     MyLoadSound(&textAttribs[cc].sound);\r
1923   }\r
1924   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1925     MyLoadSound(&sounds[sc]);\r
1926   }\r
1927 }\r
1928 \r
1929 VOID\r
1930 InitAppData(LPSTR lpCmdLine)\r
1931 {\r
1932   int i, j;\r
1933   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1934   char *dummy, *p;\r
1935 \r
1936   programName = szAppName;\r
1937 \r
1938   /* Initialize to defaults */\r
1939   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1940   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1941   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1942   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1943   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1944   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1945   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1946   SetDefaultTextAttribs();\r
1947   SetDefaultSounds();\r
1948   appData.movesPerSession = MOVES_PER_SESSION;\r
1949   appData.initString = INIT_STRING;\r
1950   appData.secondInitString = INIT_STRING;\r
1951   appData.firstComputerString = COMPUTER_STRING;\r
1952   appData.secondComputerString = COMPUTER_STRING;\r
1953   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1954   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1955   appData.firstPlaysBlack = FALSE;\r
1956   appData.noChessProgram = FALSE;\r
1957   chessProgram = FALSE;\r
1958   appData.firstHost = FIRST_HOST;\r
1959   appData.secondHost = SECOND_HOST;\r
1960   appData.firstDirectory = FIRST_DIRECTORY;\r
1961   appData.secondDirectory = SECOND_DIRECTORY;\r
1962   appData.bitmapDirectory = "";\r
1963   appData.remoteShell = REMOTE_SHELL;\r
1964   appData.remoteUser = "";\r
1965   appData.timeDelay = TIME_DELAY;\r
1966   appData.timeControl = TIME_CONTROL;\r
1967   appData.timeIncrement = TIME_INCREMENT;\r
1968   appData.icsActive = FALSE;\r
1969   appData.icsHost = "";\r
1970   appData.icsPort = ICS_PORT;\r
1971   appData.icsCommPort = ICS_COMM_PORT;\r
1972   appData.icsLogon = ICS_LOGON;\r
1973   appData.icsHelper = "";\r
1974   appData.useTelnet = FALSE;\r
1975   appData.telnetProgram = TELNET_PROGRAM;\r
1976   appData.gateway = "";\r
1977   appData.loadGameFile = "";\r
1978   appData.loadGameIndex = 0;\r
1979   appData.saveGameFile = "";\r
1980   appData.autoSaveGames = FALSE;\r
1981   appData.loadPositionFile = "";\r
1982   appData.loadPositionIndex = 1;\r
1983   appData.savePositionFile = "";\r
1984   appData.matchMode = FALSE;\r
1985   appData.matchGames = 0;\r
1986   appData.monoMode = FALSE;\r
1987   appData.debugMode = FALSE;\r
1988   appData.clockMode = TRUE;\r
1989   boardSize = (BoardSize) -1; /* determine by screen size */\r
1990   appData.Iconic = FALSE; /*unused*/\r
1991   appData.searchTime = "";\r
1992   appData.searchDepth = 0;\r
1993   appData.showCoords = FALSE;\r
1994   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1995   appData.autoCallFlag = FALSE;\r
1996   appData.flipView = FALSE;\r
1997   appData.autoFlipView = TRUE;\r
1998   appData.cmailGameName = "";\r
1999   appData.alwaysPromoteToQueen = FALSE;\r
2000   appData.oldSaveStyle = FALSE;\r
2001   appData.quietPlay = FALSE;\r
2002   appData.showThinking = FALSE;\r
2003   appData.ponderNextMove = TRUE;\r
2004   appData.periodicUpdates = TRUE;\r
2005   appData.popupExitMessage = TRUE;\r
2006   appData.popupMoveErrors = FALSE;\r
2007   appData.autoObserve = FALSE;\r
2008   appData.autoComment = FALSE;\r
2009   appData.animate = TRUE;\r
2010   appData.animSpeed = 10;\r
2011   appData.animateDragging = TRUE;\r
2012   appData.highlightLastMove = TRUE;\r
2013   appData.getMoveList = TRUE;\r
2014   appData.testLegality = TRUE;\r
2015   appData.premove = TRUE;\r
2016   appData.premoveWhite = FALSE;\r
2017   appData.premoveWhiteText = "";\r
2018   appData.premoveBlack = FALSE;\r
2019   appData.premoveBlackText = "";\r
2020   appData.icsAlarm = TRUE;\r
2021   appData.icsAlarmTime = 5000;\r
2022   appData.autoRaiseBoard = TRUE;\r
2023   appData.localLineEditing = TRUE;\r
2024   appData.colorize = TRUE;\r
2025   appData.reuseFirst = TRUE;\r
2026   appData.reuseSecond = TRUE;\r
2027   appData.blindfold = FALSE;\r
2028   appData.icsEngineAnalyze = FALSE;\r
2029   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2030   dcb.DCBlength = sizeof(DCB);\r
2031   dcb.BaudRate = 9600;\r
2032   dcb.fBinary = TRUE;\r
2033   dcb.fParity = FALSE;\r
2034   dcb.fOutxCtsFlow = FALSE;\r
2035   dcb.fOutxDsrFlow = FALSE;\r
2036   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2037   dcb.fDsrSensitivity = FALSE;\r
2038   dcb.fTXContinueOnXoff = TRUE;\r
2039   dcb.fOutX = FALSE;\r
2040   dcb.fInX = FALSE;\r
2041   dcb.fNull = FALSE;\r
2042   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2043   dcb.fAbortOnError = FALSE;\r
2044   dcb.ByteSize = 7;\r
2045   dcb.Parity = SPACEPARITY;\r
2046   dcb.StopBits = ONESTOPBIT;\r
2047   settingsFileName = SETTINGS_FILE;\r
2048   saveSettingsOnExit = TRUE;\r
2049   boardX = CW_USEDEFAULT;\r
2050   boardY = CW_USEDEFAULT;\r
2051   analysisX = CW_USEDEFAULT; \r
2052   analysisY = CW_USEDEFAULT; \r
2053   analysisW = CW_USEDEFAULT;\r
2054   analysisH = CW_USEDEFAULT;\r
2055   commentX = CW_USEDEFAULT; \r
2056   commentY = CW_USEDEFAULT; \r
2057   commentW = CW_USEDEFAULT;\r
2058   commentH = CW_USEDEFAULT;\r
2059   editTagsX = CW_USEDEFAULT; \r
2060   editTagsY = CW_USEDEFAULT; \r
2061   editTagsW = CW_USEDEFAULT;\r
2062   editTagsH = CW_USEDEFAULT;\r
2063   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2064   icsNames = ICS_NAMES;\r
2065   firstChessProgramNames = FCP_NAMES;\r
2066   secondChessProgramNames = SCP_NAMES;\r
2067   appData.initialMode = "";\r
2068   appData.variant = "normal";\r
2069   appData.firstProtocolVersion = PROTOVER;\r
2070   appData.secondProtocolVersion = PROTOVER;\r
2071   appData.showButtonBar = TRUE;\r
2072 \r
2073    /* [AS] New properties (see comments in header file) */\r
2074   appData.firstScoreIsAbsolute = FALSE;\r
2075   appData.secondScoreIsAbsolute = FALSE;\r
2076   appData.saveExtendedInfoInPGN = FALSE;\r
2077   appData.hideThinkingFromHuman = FALSE;\r
2078   appData.liteBackTextureFile = "";\r
2079   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2080   appData.darkBackTextureFile = "";\r
2081   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2082   appData.renderPiecesWithFont = "";\r
2083   appData.fontToPieceTable = "";\r
2084   appData.fontBackColorWhite = 0;\r
2085   appData.fontForeColorWhite = 0;\r
2086   appData.fontBackColorBlack = 0;\r
2087   appData.fontForeColorBlack = 0;\r
2088   appData.fontPieceSize = 80;\r
2089   appData.overrideLineGap = 1;\r
2090   appData.adjudicateLossThreshold = 0;\r
2091   appData.delayBeforeQuit = 0;\r
2092   appData.delayAfterQuit = 0;\r
2093   appData.nameOfDebugFile = "winboard.debug";\r
2094   appData.pgnEventHeader = "Computer Chess Game";\r
2095   appData.defaultFrcPosition = -1;\r
2096   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2097   appData.saveOutOfBookInfo = TRUE;\r
2098   appData.showEvalInMoveHistory = TRUE;\r
2099   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2100   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2101   appData.highlightMoveWithArrow = FALSE;\r
2102   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2103   appData.useStickyWindows = TRUE;\r
2104   appData.adjudicateDrawMoves = 0;\r
2105   appData.autoDisplayComment = TRUE;\r
2106   appData.autoDisplayTags = TRUE;\r
2107   appData.firstIsUCI = FALSE;\r
2108   appData.secondIsUCI = FALSE;\r
2109   appData.firstHasOwnBookUCI = TRUE;\r
2110   appData.secondHasOwnBookUCI = TRUE;\r
2111   appData.polyglotDir = "";\r
2112   appData.usePolyglotBook = FALSE;\r
2113   appData.polyglotBook = "";\r
2114   appData.defaultHashSize = 64;\r
2115   appData.defaultCacheSizeEGTB = 4;\r
2116   appData.defaultPathEGTB = "c:\\egtb";\r
2117   appData.firstOptions = "";\r
2118   appData.secondOptions = "";\r
2119 \r
2120   InitWindowPlacement( &wpGameList );\r
2121   InitWindowPlacement( &wpMoveHistory );\r
2122   InitWindowPlacement( &wpEvalGraph );\r
2123   InitWindowPlacement( &wpEngineOutput );\r
2124   InitWindowPlacement( &wpConsole );\r
2125 \r
2126   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2127   appData.NrFiles      = -1;\r
2128   appData.NrRanks      = -1;\r
2129   appData.holdingsSize = -1;\r
2130   appData.testClaims   = FALSE;\r
2131   appData.checkMates   = FALSE;\r
2132   appData.materialDraws= FALSE;\r
2133   appData.trivialDraws = FALSE;\r
2134   appData.ruleMoves    = 51;\r
2135   appData.drawRepeats  = 6;\r
2136   appData.matchPause   = 10000;\r
2137   appData.alphaRank    = FALSE;\r
2138   appData.allWhite     = FALSE;\r
2139   appData.upsideDown   = FALSE;\r
2140   appData.serverPause  = 15;\r
2141   appData.serverMovesName   = NULL;\r
2142   appData.suppressLoadMoves = FALSE;\r
2143   appData.firstTimeOdds  = 1;\r
2144   appData.secondTimeOdds = 1;\r
2145   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2146   appData.secondAccumulateTC = 1;\r
2147   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2148   appData.secondNPS = -1;\r
2149   appData.engineComments = 1;\r
2150   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2151   appData.egtFormats = "";\r
2152 \r
2153 #ifdef ZIPPY\r
2154   appData.zippyTalk = ZIPPY_TALK;\r
2155   appData.zippyPlay = ZIPPY_PLAY;\r
2156   appData.zippyLines = ZIPPY_LINES;\r
2157   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2158   appData.zippyPassword = ZIPPY_PASSWORD;\r
2159   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2160   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2161   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2162   appData.zippyUseI = ZIPPY_USE_I;\r
2163   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2164   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2165   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2166   appData.zippyGameStart = ZIPPY_GAME_START;\r
2167   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2168   appData.zippyAbort = ZIPPY_ABORT;\r
2169   appData.zippyVariants = ZIPPY_VARIANTS;\r
2170   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2171   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2172 #endif\r
2173 \r
2174   /* Point font array elements to structures and\r
2175      parse default font names */\r
2176   for (i=0; i<NUM_FONTS; i++) {\r
2177     for (j=0; j<NUM_SIZES; j++) {\r
2178       font[j][i] = &fontRec[j][i];\r
2179       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2180     }\r
2181   }\r
2182   \r
2183   /* Parse default settings file if any */\r
2184   if (ParseSettingsFile(settingsFileName, buf)) {\r
2185     settingsFileName = strdup(buf);\r
2186   }\r
2187 \r
2188   /* Parse command line */\r
2189   ParseArgs(StringGet, &lpCmdLine);\r
2190 \r
2191   /* [HGM] make sure board size is acceptable */\r
2192   if(appData.NrFiles > BOARD_SIZE ||\r
2193      appData.NrRanks > BOARD_SIZE   )\r
2194       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2195 \r
2196   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2197    * with options from the command line, we now make an even higher priority\r
2198    * overrule by WB options attached to the engine command line. This so that\r
2199    * tournament managers can use WB options (such as /timeOdds) that follow\r
2200    * the engines.\r
2201    */\r
2202   if(appData.firstChessProgram != NULL) {\r
2203       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2204       static char *f = "first";\r
2205       char buf[MSG_SIZ], *q = buf;\r
2206       if(p != NULL) { // engine command line contains WinBoard options\r
2207           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2208           ParseArgs(StringGet, &q);\r
2209           p[-1] = 0; // cut them offengine command line\r
2210       }\r
2211   }\r
2212   // now do same for second chess program\r
2213   if(appData.secondChessProgram != NULL) {\r
2214       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2215       static char *s = "second";\r
2216       char buf[MSG_SIZ], *q = buf;\r
2217       if(p != NULL) { // engine command line contains WinBoard options\r
2218           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2219           ParseArgs(StringGet, &q);\r
2220           p[-1] = 0; // cut them offengine command line\r
2221       }\r
2222   }\r
2223 \r
2224 \r
2225   /* Propagate options that affect others */\r
2226   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2227   if (appData.icsActive || appData.noChessProgram) {\r
2228      chessProgram = FALSE;  /* not local chess program mode */\r
2229   }\r
2230 \r
2231   /* Open startup dialog if needed */\r
2232   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2233       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2234       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2235                         *appData.secondChessProgram == NULLCHAR))) {\r
2236     FARPROC lpProc;\r
2237     \r
2238     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2239     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2240     FreeProcInstance(lpProc);\r
2241   }\r
2242 \r
2243   /* Make sure save files land in the right (?) directory */\r
2244   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2245     appData.saveGameFile = strdup(buf);\r
2246   }\r
2247   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2248     appData.savePositionFile = strdup(buf);\r
2249   }\r
2250 \r
2251   /* Finish initialization for fonts and sounds */\r
2252   for (i=0; i<NUM_FONTS; i++) {\r
2253     for (j=0; j<NUM_SIZES; j++) {\r
2254       CreateFontInMF(font[j][i]);\r
2255     }\r
2256   }\r
2257   /* xboard, and older WinBoards, controlled the move sound with the\r
2258      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2259      always turn the option on (so that the backend will call us),\r
2260      then let the user turn the sound off by setting it to silence if\r
2261      desired.  To accommodate old winboard.ini files saved by old\r
2262      versions of WinBoard, we also turn off the sound if the option\r
2263      was initially set to false. */\r
2264   if (!appData.ringBellAfterMoves) {\r
2265     sounds[(int)SoundMove].name = strdup("");\r
2266     appData.ringBellAfterMoves = TRUE;\r
2267   }\r
2268   GetCurrentDirectory(MSG_SIZ, currDir);\r
2269   SetCurrentDirectory(installDir);\r
2270   LoadAllSounds();\r
2271   SetCurrentDirectory(currDir);\r
2272 \r
2273   p = icsTextMenuString;\r
2274   if (p[0] == '@') {\r
2275     FILE* f = fopen(p + 1, "r");\r
2276     if (f == NULL) {\r
2277       DisplayFatalError(p + 1, errno, 2);\r
2278       return;\r
2279     }\r
2280     i = fread(buf, 1, sizeof(buf)-1, f);\r
2281     fclose(f);\r
2282     buf[i] = NULLCHAR;\r
2283     p = buf;\r
2284   }\r
2285   ParseIcsTextMenu(strdup(p));\r
2286 }\r
2287 \r
2288 \r
2289 VOID\r
2290 InitMenuChecks()\r
2291 {\r
2292   HMENU hmenu = GetMenu(hwndMain);\r
2293 \r
2294   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2295                         MF_BYCOMMAND|((appData.icsActive &&\r
2296                                        *appData.icsCommPort != NULLCHAR) ?\r
2297                                       MF_ENABLED : MF_GRAYED));\r
2298   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2299                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2300                                      MF_CHECKED : MF_UNCHECKED));\r
2301 }\r
2302 \r
2303 \r
2304 VOID\r
2305 SaveSettings(char* name)\r
2306 {\r
2307   FILE *f;\r
2308   ArgDescriptor *ad;\r
2309   WINDOWPLACEMENT wp;\r
2310   char dir[MSG_SIZ];\r
2311 \r
2312   if (!hwndMain) return;\r
2313 \r
2314   GetCurrentDirectory(MSG_SIZ, dir);\r
2315   SetCurrentDirectory(installDir);\r
2316   f = fopen(name, "w");\r
2317   SetCurrentDirectory(dir);\r
2318   if (f == NULL) {\r
2319     DisplayError(name, errno);\r
2320     return;\r
2321   }\r
2322   fprintf(f, ";\n");\r
2323   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2324   fprintf(f, ";\n");\r
2325   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2326   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2327   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2328   fprintf(f, ";\n");\r
2329 \r
2330   wp.length = sizeof(WINDOWPLACEMENT);\r
2331   GetWindowPlacement(hwndMain, &wp);\r
2332   boardX = wp.rcNormalPosition.left;\r
2333   boardY = wp.rcNormalPosition.top;\r
2334 \r
2335   if (hwndConsole) {\r
2336     GetWindowPlacement(hwndConsole, &wp);\r
2337     wpConsole.x = wp.rcNormalPosition.left;\r
2338     wpConsole.y = wp.rcNormalPosition.top;\r
2339     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2340     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2341   }\r
2342 \r
2343   if (analysisDialog) {\r
2344     GetWindowPlacement(analysisDialog, &wp);\r
2345     analysisX = wp.rcNormalPosition.left;\r
2346     analysisY = wp.rcNormalPosition.top;\r
2347     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2348     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2349   }\r
2350 \r
2351   if (commentDialog) {\r
2352     GetWindowPlacement(commentDialog, &wp);\r
2353     commentX = wp.rcNormalPosition.left;\r
2354     commentY = wp.rcNormalPosition.top;\r
2355     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2356     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2357   }\r
2358 \r
2359   if (editTagsDialog) {\r
2360     GetWindowPlacement(editTagsDialog, &wp);\r
2361     editTagsX = wp.rcNormalPosition.left;\r
2362     editTagsY = wp.rcNormalPosition.top;\r
2363     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2364     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2365   }\r
2366 \r
2367   if (gameListDialog) {\r
2368     GetWindowPlacement(gameListDialog, &wp);\r
2369     wpGameList.x = wp.rcNormalPosition.left;\r
2370     wpGameList.y = wp.rcNormalPosition.top;\r
2371     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2372     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2373   }\r
2374 \r
2375   /* [AS] Move history */\r
2376   wpMoveHistory.visible = MoveHistoryIsUp();\r
2377   \r
2378   if( moveHistoryDialog ) {\r
2379     GetWindowPlacement(moveHistoryDialog, &wp);\r
2380     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2381     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2382     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2383     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2384   }\r
2385 \r
2386   /* [AS] Eval graph */\r
2387   wpEvalGraph.visible = EvalGraphIsUp();\r
2388 \r
2389   if( evalGraphDialog ) {\r
2390     GetWindowPlacement(evalGraphDialog, &wp);\r
2391     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2392     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2393     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2394     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2395   }\r
2396 \r
2397   /* [AS] Engine output */\r
2398   wpEngineOutput.visible = EngineOutputIsUp();\r
2399 \r
2400   if( engineOutputDialog ) {\r
2401     GetWindowPlacement(engineOutputDialog, &wp);\r
2402     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2403     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2404     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2405     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2406   }\r
2407 \r
2408   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2409     if (!ad->save) continue;\r
2410     switch (ad->argType) {\r
2411     case ArgString:\r
2412       {\r
2413         char *p = *(char **)ad->argLoc;\r
2414         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2415           /* Quote multiline values or \-containing values\r
2416              with { } if possible */\r
2417           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2418         } else {\r
2419           /* Else quote with " " */\r
2420           fprintf(f, "/%s=\"", ad->argName);\r
2421           while (*p) {\r
2422             if (*p == '\n') fprintf(f, "\n");\r
2423             else if (*p == '\r') fprintf(f, "\\r");\r
2424             else if (*p == '\t') fprintf(f, "\\t");\r
2425             else if (*p == '\b') fprintf(f, "\\b");\r
2426             else if (*p == '\f') fprintf(f, "\\f");\r
2427             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2428             else if (*p == '\"') fprintf(f, "\\\"");\r
2429             else if (*p == '\\') fprintf(f, "\\\\");\r
2430             else putc(*p, f);\r
2431             p++;\r
2432           }\r
2433           fprintf(f, "\"\n");\r
2434         }\r
2435       }\r
2436       break;\r
2437     case ArgInt:\r
2438     case ArgZ:\r
2439       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2440       break;\r
2441     case ArgX:\r
2442       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2443       break;\r
2444     case ArgY:\r
2445       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2446       break;\r
2447     case ArgFloat:\r
2448       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2449       break;\r
2450     case ArgBoolean:\r
2451       fprintf(f, "/%s=%s\n", ad->argName, \r
2452         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2453       break;\r
2454     case ArgTrue:\r
2455       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2456       break;\r
2457     case ArgFalse:\r
2458       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2459       break;\r
2460     case ArgColor:\r
2461       {\r
2462         COLORREF color = *(COLORREF *)ad->argLoc;\r
2463         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2464           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2465       }\r
2466       break;\r
2467     case ArgAttribs:\r
2468       {\r
2469         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2470         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2471           (ta->effects & CFE_BOLD) ? "b" : "",\r
2472           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2473           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2474           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2475           (ta->effects) ? " " : "",\r
2476           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2477       }\r
2478       break;\r
2479     case ArgFilename:\r
2480       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2481         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2482       } else {\r
2483         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2484       }\r
2485       break;\r
2486     case ArgBoardSize:\r
2487       fprintf(f, "/%s=%s\n", ad->argName,\r
2488               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2489       break;\r
2490     case ArgFont:\r
2491       {\r
2492         int bs;\r
2493         for (bs=0; bs<NUM_SIZES; bs++) {\r
2494           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2495           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2496           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2497             ad->argName, mfp->faceName, mfp->pointSize,\r
2498             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2499             mfp->bold ? "b" : "",\r
2500             mfp->italic ? "i" : "",\r
2501             mfp->underline ? "u" : "",\r
2502             mfp->strikeout ? "s" : "");\r
2503         }\r
2504       }\r
2505       break;\r
2506     case ArgCommSettings:\r
2507       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2508     case ArgNone:\r
2509     case ArgSettingsFilename: ;\r
2510     }\r
2511   }\r
2512   fclose(f);\r
2513 }\r
2514 \r
2515 \r
2516 \r
2517 /*---------------------------------------------------------------------------*\\r
2518  *\r
2519  * GDI board drawing routines\r
2520  *\r
2521 \*---------------------------------------------------------------------------*/\r
2522 \r
2523 /* [AS] Draw square using background texture */\r
2524 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2525 {\r
2526     XFORM   x;\r
2527 \r
2528     if( mode == 0 ) {\r
2529         return; /* Should never happen! */\r
2530     }\r
2531 \r
2532     SetGraphicsMode( dst, GM_ADVANCED );\r
2533 \r
2534     switch( mode ) {\r
2535     case 1:\r
2536         /* Identity */\r
2537         break;\r
2538     case 2:\r
2539         /* X reflection */\r
2540         x.eM11 = -1.0;\r
2541         x.eM12 = 0;\r
2542         x.eM21 = 0;\r
2543         x.eM22 = 1.0;\r
2544         x.eDx = (FLOAT) dw + dx - 1;\r
2545         x.eDy = 0;\r
2546         dx = 0;\r
2547         SetWorldTransform( dst, &x );\r
2548         break;\r
2549     case 3:\r
2550         /* Y reflection */\r
2551         x.eM11 = 1.0;\r
2552         x.eM12 = 0;\r
2553         x.eM21 = 0;\r
2554         x.eM22 = -1.0;\r
2555         x.eDx = 0;\r
2556         x.eDy = (FLOAT) dh + dy - 1;\r
2557         dy = 0;\r
2558         SetWorldTransform( dst, &x );\r
2559         break;\r
2560     case 4:\r
2561         /* X/Y flip */\r
2562         x.eM11 = 0;\r
2563         x.eM12 = 1.0;\r
2564         x.eM21 = 1.0;\r
2565         x.eM22 = 0;\r
2566         x.eDx = (FLOAT) dx;\r
2567         x.eDy = (FLOAT) dy;\r
2568         dx = 0;\r
2569         dy = 0;\r
2570         SetWorldTransform( dst, &x );\r
2571         break;\r
2572     }\r
2573 \r
2574     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2575 \r
2576     x.eM11 = 1.0;\r
2577     x.eM12 = 0;\r
2578     x.eM21 = 0;\r
2579     x.eM22 = 1.0;\r
2580     x.eDx = 0;\r
2581     x.eDy = 0;\r
2582     SetWorldTransform( dst, &x );\r
2583 \r
2584     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2585 }\r
2586 \r
2587 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2588 enum {\r
2589     PM_WP = (int) WhitePawn, \r
2590     PM_WN = (int) WhiteKnight, \r
2591     PM_WB = (int) WhiteBishop, \r
2592     PM_WR = (int) WhiteRook, \r
2593     PM_WQ = (int) WhiteQueen, \r
2594     PM_WF = (int) WhiteFerz, \r
2595     PM_WW = (int) WhiteWazir, \r
2596     PM_WE = (int) WhiteAlfil, \r
2597     PM_WM = (int) WhiteMan, \r
2598     PM_WO = (int) WhiteCannon, \r
2599     PM_WU = (int) WhiteUnicorn, \r
2600     PM_WH = (int) WhiteNightrider, \r
2601     PM_WA = (int) WhiteAngel, \r
2602     PM_WC = (int) WhiteMarshall, \r
2603     PM_WAB = (int) WhiteCardinal, \r
2604     PM_WD = (int) WhiteDragon, \r
2605     PM_WL = (int) WhiteLance, \r
2606     PM_WS = (int) WhiteCobra, \r
2607     PM_WV = (int) WhiteFalcon, \r
2608     PM_WSG = (int) WhiteSilver, \r
2609     PM_WG = (int) WhiteGrasshopper, \r
2610     PM_WK = (int) WhiteKing,\r
2611     PM_BP = (int) BlackPawn, \r
2612     PM_BN = (int) BlackKnight, \r
2613     PM_BB = (int) BlackBishop, \r
2614     PM_BR = (int) BlackRook, \r
2615     PM_BQ = (int) BlackQueen, \r
2616     PM_BF = (int) BlackFerz, \r
2617     PM_BW = (int) BlackWazir, \r
2618     PM_BE = (int) BlackAlfil, \r
2619     PM_BM = (int) BlackMan,\r
2620     PM_BO = (int) BlackCannon, \r
2621     PM_BU = (int) BlackUnicorn, \r
2622     PM_BH = (int) BlackNightrider, \r
2623     PM_BA = (int) BlackAngel, \r
2624     PM_BC = (int) BlackMarshall, \r
2625     PM_BG = (int) BlackGrasshopper, \r
2626     PM_BAB = (int) BlackCardinal,\r
2627     PM_BD = (int) BlackDragon,\r
2628     PM_BL = (int) BlackLance,\r
2629     PM_BS = (int) BlackCobra,\r
2630     PM_BV = (int) BlackFalcon,\r
2631     PM_BSG = (int) BlackSilver,\r
2632     PM_BK = (int) BlackKing\r
2633 };\r
2634 \r
2635 static HFONT hPieceFont = NULL;\r
2636 static HBITMAP hPieceMask[(int) EmptySquare];\r
2637 static HBITMAP hPieceFace[(int) EmptySquare];\r
2638 static int fontBitmapSquareSize = 0;\r
2639 static char pieceToFontChar[(int) EmptySquare] =\r
2640                               { 'p', 'n', 'b', 'r', 'q', \r
2641                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2642                       'k', 'o', 'm', 'v', 't', 'w', \r
2643                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2644                                                               'l' };\r
2645 \r
2646 extern BOOL SetCharTable( char *table, const char * map );\r
2647 /* [HGM] moved to backend.c */\r
2648 \r
2649 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2650 {\r
2651     HBRUSH hbrush;\r
2652     BYTE r1 = GetRValue( color );\r
2653     BYTE g1 = GetGValue( color );\r
2654     BYTE b1 = GetBValue( color );\r
2655     BYTE r2 = r1 / 2;\r
2656     BYTE g2 = g1 / 2;\r
2657     BYTE b2 = b1 / 2;\r
2658     RECT rc;\r
2659 \r
2660     /* Create a uniform background first */\r
2661     hbrush = CreateSolidBrush( color );\r
2662     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2663     FillRect( hdc, &rc, hbrush );\r
2664     DeleteObject( hbrush );\r
2665     \r
2666     if( mode == 1 ) {\r
2667         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2668         int steps = squareSize / 2;\r
2669         int i;\r
2670 \r
2671         for( i=0; i<steps; i++ ) {\r
2672             BYTE r = r1 - (r1-r2) * i / steps;\r
2673             BYTE g = g1 - (g1-g2) * i / steps;\r
2674             BYTE b = b1 - (b1-b2) * i / steps;\r
2675 \r
2676             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2677             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2678             FillRect( hdc, &rc, hbrush );\r
2679             DeleteObject(hbrush);\r
2680         }\r
2681     }\r
2682     else if( mode == 2 ) {\r
2683         /* Diagonal gradient, good more or less for every piece */\r
2684         POINT triangle[3];\r
2685         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2686         HBRUSH hbrush_old;\r
2687         int steps = squareSize;\r
2688         int i;\r
2689 \r
2690         triangle[0].x = squareSize - steps;\r
2691         triangle[0].y = squareSize;\r
2692         triangle[1].x = squareSize;\r
2693         triangle[1].y = squareSize;\r
2694         triangle[2].x = squareSize;\r
2695         triangle[2].y = squareSize - steps;\r
2696 \r
2697         for( i=0; i<steps; i++ ) {\r
2698             BYTE r = r1 - (r1-r2) * i / steps;\r
2699             BYTE g = g1 - (g1-g2) * i / steps;\r
2700             BYTE b = b1 - (b1-b2) * i / steps;\r
2701 \r
2702             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2703             hbrush_old = SelectObject( hdc, hbrush );\r
2704             Polygon( hdc, triangle, 3 );\r
2705             SelectObject( hdc, hbrush_old );\r
2706             DeleteObject(hbrush);\r
2707             triangle[0].x++;\r
2708             triangle[2].y++;\r
2709         }\r
2710 \r
2711         SelectObject( hdc, hpen );\r
2712     }\r
2713 }\r
2714 \r
2715 /*\r
2716     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2717     seems to work ok. The main problem here is to find the "inside" of a chess\r
2718     piece: follow the steps as explained below.\r
2719 */\r
2720 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2721 {\r
2722     HBITMAP hbm;\r
2723     HBITMAP hbm_old;\r
2724     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2725     RECT rc;\r
2726     SIZE sz;\r
2727     POINT pt;\r
2728     int backColor = whitePieceColor; \r
2729     int foreColor = blackPieceColor;\r
2730     \r
2731     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2732         backColor = appData.fontBackColorWhite;\r
2733         foreColor = appData.fontForeColorWhite;\r
2734     }\r
2735     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2736         backColor = appData.fontBackColorBlack;\r
2737         foreColor = appData.fontForeColorBlack;\r
2738     }\r
2739 \r
2740     /* Mask */\r
2741     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2742 \r
2743     hbm_old = SelectObject( hdc, hbm );\r
2744 \r
2745     rc.left = 0;\r
2746     rc.top = 0;\r
2747     rc.right = squareSize;\r
2748     rc.bottom = squareSize;\r
2749 \r
2750     /* Step 1: background is now black */\r
2751     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2752 \r
2753     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2754 \r
2755     pt.x = (squareSize - sz.cx) / 2;\r
2756     pt.y = (squareSize - sz.cy) / 2;\r
2757 \r
2758     SetBkMode( hdc, TRANSPARENT );\r
2759     SetTextColor( hdc, chroma );\r
2760     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2761     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2762 \r
2763     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2764     /* Step 3: the area outside the piece is filled with white */\r
2765 //    FloodFill( hdc, 0, 0, chroma );\r
2766     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2767     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2768     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2769     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2770     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2771     /* \r
2772         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2773         but if the start point is not inside the piece we're lost!\r
2774         There should be a better way to do this... if we could create a region or path\r
2775         from the fill operation we would be fine for example.\r
2776     */\r
2777 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2778     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2779 \r
2780     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2781         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2782         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2783 \r
2784         SelectObject( dc2, bm2 );\r
2785         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2786         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2787         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2788         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2789         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2790 \r
2791         DeleteDC( dc2 );\r
2792         DeleteObject( bm2 );\r
2793     }\r
2794 \r
2795     SetTextColor( hdc, 0 );\r
2796     /* \r
2797         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2798         draw the piece again in black for safety.\r
2799     */\r
2800     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2801 \r
2802     SelectObject( hdc, hbm_old );\r
2803 \r
2804     if( hPieceMask[index] != NULL ) {\r
2805         DeleteObject( hPieceMask[index] );\r
2806     }\r
2807 \r
2808     hPieceMask[index] = hbm;\r
2809 \r
2810     /* Face */\r
2811     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2812 \r
2813     SelectObject( hdc, hbm );\r
2814 \r
2815     {\r
2816         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2817         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2818         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2819 \r
2820         SelectObject( dc1, hPieceMask[index] );\r
2821         SelectObject( dc2, bm2 );\r
2822         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2823         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2824         \r
2825         /* \r
2826             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2827             the piece background and deletes (makes transparent) the rest.\r
2828             Thanks to that mask, we are free to paint the background with the greates\r
2829             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2830             We use this, to make gradients and give the pieces a "roundish" look.\r
2831         */\r
2832         SetPieceBackground( hdc, backColor, 2 );\r
2833         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2834 \r
2835         DeleteDC( dc2 );\r
2836         DeleteDC( dc1 );\r
2837         DeleteObject( bm2 );\r
2838     }\r
2839 \r
2840     SetTextColor( hdc, foreColor );\r
2841     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2842 \r
2843     SelectObject( hdc, hbm_old );\r
2844 \r
2845     if( hPieceFace[index] != NULL ) {\r
2846         DeleteObject( hPieceFace[index] );\r
2847     }\r
2848 \r
2849     hPieceFace[index] = hbm;\r
2850 }\r
2851 \r
2852 static int TranslatePieceToFontPiece( int piece )\r
2853 {\r
2854     switch( piece ) {\r
2855     case BlackPawn:\r
2856         return PM_BP;\r
2857     case BlackKnight:\r
2858         return PM_BN;\r
2859     case BlackBishop:\r
2860         return PM_BB;\r
2861     case BlackRook:\r
2862         return PM_BR;\r
2863     case BlackQueen:\r
2864         return PM_BQ;\r
2865     case BlackKing:\r
2866         return PM_BK;\r
2867     case WhitePawn:\r
2868         return PM_WP;\r
2869     case WhiteKnight:\r
2870         return PM_WN;\r
2871     case WhiteBishop:\r
2872         return PM_WB;\r
2873     case WhiteRook:\r
2874         return PM_WR;\r
2875     case WhiteQueen:\r
2876         return PM_WQ;\r
2877     case WhiteKing:\r
2878         return PM_WK;\r
2879 \r
2880     case BlackAngel:\r
2881         return PM_BA;\r
2882     case BlackMarshall:\r
2883         return PM_BC;\r
2884     case BlackFerz:\r
2885         return PM_BF;\r
2886     case BlackNightrider:\r
2887         return PM_BH;\r
2888     case BlackAlfil:\r
2889         return PM_BE;\r
2890     case BlackWazir:\r
2891         return PM_BW;\r
2892     case BlackUnicorn:\r
2893         return PM_BU;\r
2894     case BlackCannon:\r
2895         return PM_BO;\r
2896     case BlackGrasshopper:\r
2897         return PM_BG;\r
2898     case BlackMan:\r
2899         return PM_BM;\r
2900     case BlackSilver:\r
2901         return PM_BSG;\r
2902     case BlackLance:\r
2903         return PM_BL;\r
2904     case BlackFalcon:\r
2905         return PM_BV;\r
2906     case BlackCobra:\r
2907         return PM_BS;\r
2908     case BlackCardinal:\r
2909         return PM_BAB;\r
2910     case BlackDragon:\r
2911         return PM_BD;\r
2912 \r
2913     case WhiteAngel:\r
2914         return PM_WA;\r
2915     case WhiteMarshall:\r
2916         return PM_WC;\r
2917     case WhiteFerz:\r
2918         return PM_WF;\r
2919     case WhiteNightrider:\r
2920         return PM_WH;\r
2921     case WhiteAlfil:\r
2922         return PM_WE;\r
2923     case WhiteWazir:\r
2924         return PM_WW;\r
2925     case WhiteUnicorn:\r
2926         return PM_WU;\r
2927     case WhiteCannon:\r
2928         return PM_WO;\r
2929     case WhiteGrasshopper:\r
2930         return PM_WG;\r
2931     case WhiteMan:\r
2932         return PM_WM;\r
2933     case WhiteSilver:\r
2934         return PM_WSG;\r
2935     case WhiteLance:\r
2936         return PM_WL;\r
2937     case WhiteFalcon:\r
2938         return PM_WV;\r
2939     case WhiteCobra:\r
2940         return PM_WS;\r
2941     case WhiteCardinal:\r
2942         return PM_WAB;\r
2943     case WhiteDragon:\r
2944         return PM_WD;\r
2945     }\r
2946 \r
2947     return 0;\r
2948 }\r
2949 \r
2950 void CreatePiecesFromFont()\r
2951 {\r
2952     LOGFONT lf;\r
2953     HDC hdc_window = NULL;\r
2954     HDC hdc = NULL;\r
2955     HFONT hfont_old;\r
2956     int fontHeight;\r
2957     int i;\r
2958 \r
2959     if( fontBitmapSquareSize < 0 ) {\r
2960         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2961         return;\r
2962     }\r
2963 \r
2964     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2965         fontBitmapSquareSize = -1;\r
2966         return;\r
2967     }\r
2968 \r
2969     if( fontBitmapSquareSize != squareSize ) {\r
2970         hdc_window = GetDC( hwndMain );\r
2971         hdc = CreateCompatibleDC( hdc_window );\r
2972 \r
2973         if( hPieceFont != NULL ) {\r
2974             DeleteObject( hPieceFont );\r
2975         }\r
2976         else {\r
2977             for( i=0; i<=(int)BlackKing; i++ ) {\r
2978                 hPieceMask[i] = NULL;\r
2979                 hPieceFace[i] = NULL;\r
2980             }\r
2981         }\r
2982 \r
2983         fontHeight = 75;\r
2984 \r
2985         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2986             fontHeight = appData.fontPieceSize;\r
2987         }\r
2988 \r
2989         fontHeight = (fontHeight * squareSize) / 100;\r
2990 \r
2991         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2992         lf.lfWidth = 0;\r
2993         lf.lfEscapement = 0;\r
2994         lf.lfOrientation = 0;\r
2995         lf.lfWeight = FW_NORMAL;\r
2996         lf.lfItalic = 0;\r
2997         lf.lfUnderline = 0;\r
2998         lf.lfStrikeOut = 0;\r
2999         lf.lfCharSet = DEFAULT_CHARSET;\r
3000         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
3001         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
3002         lf.lfQuality = PROOF_QUALITY;\r
3003         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
3004         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
3005         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
3006 \r
3007         hPieceFont = CreateFontIndirect( &lf );\r
3008 \r
3009         if( hPieceFont == NULL ) {\r
3010             fontBitmapSquareSize = -2;\r
3011         }\r
3012         else {\r
3013             /* Setup font-to-piece character table */\r
3014             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
3015                 /* No (or wrong) global settings, try to detect the font */\r
3016                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
3017                     /* Alpha */\r
3018                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
3019                 }\r
3020                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3021                     /* DiagramTT* family */\r
3022                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3023                 }\r
3024                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3025                     /* Fairy symbols */\r
3026                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3027                 }\r
3028                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3029                     /* Good Companion (Some characters get warped as literal :-( */\r
3030                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3031                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3032                     SetCharTable(pieceToFontChar, s);\r
3033                 }\r
3034                 else {\r
3035                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3036                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3037                 }\r
3038             }\r
3039 \r
3040             /* Create bitmaps */\r
3041             hfont_old = SelectObject( hdc, hPieceFont );\r
3042 #if 0\r
3043             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
3044             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
3045             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
3046             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
3047             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
3048             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
3049             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
3050             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
3051             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
3052             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
3053             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
3054             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
3055 \r
3056             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
3057             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
3058             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
3059             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
3060             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
3061             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
3062             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
3063             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
3064             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
3065             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
3066             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
3067             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
3068             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
3069             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
3070             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
3071             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
3072             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
3073             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
3074             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
3075             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
3076             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
3077             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
3078             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
3079             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
3080             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
3081             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
3082             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
3083             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
3084             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
3085             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
3086             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
3087             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
3088 #else\r
3089             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3090                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3091                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3092 #endif\r
3093             SelectObject( hdc, hfont_old );\r
3094 \r
3095             fontBitmapSquareSize = squareSize;\r
3096         }\r
3097     }\r
3098 \r
3099     if( hdc != NULL ) {\r
3100         DeleteDC( hdc );\r
3101     }\r
3102 \r
3103     if( hdc_window != NULL ) {\r
3104         ReleaseDC( hwndMain, hdc_window );\r
3105     }\r
3106 }\r
3107 \r
3108 HBITMAP\r
3109 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3110 {\r
3111   char name[128];\r
3112 \r
3113   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3114   if (gameInfo.event &&\r
3115       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3116       strcmp(name, "k80s") == 0) {\r
3117     strcpy(name, "tim");\r
3118   }\r
3119   return LoadBitmap(hinst, name);\r
3120 }\r
3121 \r
3122 \r
3123 /* Insert a color into the program's logical palette\r
3124    structure.  This code assumes the given color is\r
3125    the result of the RGB or PALETTERGB macro, and it\r
3126    knows how those macros work (which is documented).\r
3127 */\r
3128 VOID\r
3129 InsertInPalette(COLORREF color)\r
3130 {\r
3131   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3132 \r
3133   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3134     DisplayFatalError("Too many colors", 0, 1);\r
3135     pLogPal->palNumEntries--;\r
3136     return;\r
3137   }\r
3138 \r
3139   pe->peFlags = (char) 0;\r
3140   pe->peRed = (char) (0xFF & color);\r
3141   pe->peGreen = (char) (0xFF & (color >> 8));\r
3142   pe->peBlue = (char) (0xFF & (color >> 16));\r
3143   return;\r
3144 }\r
3145 \r
3146 \r
3147 VOID\r
3148 InitDrawingColors()\r
3149 {\r
3150   if (pLogPal == NULL) {\r
3151     /* Allocate enough memory for a logical palette with\r
3152      * PALETTESIZE entries and set the size and version fields\r
3153      * of the logical palette structure.\r
3154      */\r
3155     pLogPal = (NPLOGPALETTE)\r
3156       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3157                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3158     pLogPal->palVersion    = 0x300;\r
3159   }\r
3160   pLogPal->palNumEntries = 0;\r
3161 \r
3162   InsertInPalette(lightSquareColor);\r
3163   InsertInPalette(darkSquareColor);\r
3164   InsertInPalette(whitePieceColor);\r
3165   InsertInPalette(blackPieceColor);\r
3166   InsertInPalette(highlightSquareColor);\r
3167   InsertInPalette(premoveHighlightColor);\r
3168 \r
3169   /*  create a logical color palette according the information\r
3170    *  in the LOGPALETTE structure.\r
3171    */\r
3172   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3173 \r
3174   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3175   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3176   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3177   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3178   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3179   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3180   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3181   /* [AS] Force rendering of the font-based pieces */\r
3182   if( fontBitmapSquareSize > 0 ) {\r
3183     fontBitmapSquareSize = 0;\r
3184   }\r
3185 }\r
3186 \r
3187 \r
3188 int\r
3189 BoardWidth(int boardSize, int n)\r
3190 { /* [HGM] argument n added to allow different width and height */\r
3191   int lineGap = sizeInfo[boardSize].lineGap;\r
3192 \r
3193   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3194       lineGap = appData.overrideLineGap;\r
3195   }\r
3196 \r
3197   return (n + 1) * lineGap +\r
3198           n * sizeInfo[boardSize].squareSize;\r
3199 }\r
3200 \r
3201 /* Respond to board resize by dragging edge */\r
3202 VOID\r
3203 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3204 {\r
3205   BoardSize newSize = NUM_SIZES - 1;\r
3206   static int recurse = 0;\r
3207   if (IsIconic(hwndMain)) return;\r
3208   if (recurse > 0) return;\r
3209   recurse++;\r
3210   while (newSize > 0) {\r
3211         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3212         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3213            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3214     newSize--;\r
3215   } \r
3216   boardSize = newSize;\r
3217   InitDrawingSizes(boardSize, flags);\r
3218   recurse--;\r
3219 }\r
3220 \r
3221 \r
3222 \r
3223 VOID\r
3224 InitDrawingSizes(BoardSize boardSize, int flags)\r
3225 {\r
3226   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3227   ChessSquare piece;\r
3228   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3229   HDC hdc;\r
3230   SIZE clockSize, messageSize;\r
3231   HFONT oldFont;\r
3232   char buf[MSG_SIZ];\r
3233   char *str;\r
3234   HMENU hmenu = GetMenu(hwndMain);\r
3235   RECT crect, wrect, oldRect;\r
3236   int offby;\r
3237   LOGBRUSH logbrush;\r
3238 \r
3239   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3240   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3241 \r
3242   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3243   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3244 \r
3245   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3246   oldRect.top = boardY;\r
3247   oldRect.right = boardX + winWidth;\r
3248   oldRect.bottom = boardY + winHeight;\r
3249 \r
3250   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3251   smallLayout = sizeInfo[boardSize].smallLayout;\r
3252   squareSize = sizeInfo[boardSize].squareSize;\r
3253   lineGap = sizeInfo[boardSize].lineGap;\r
3254   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3255 \r
3256   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3257       lineGap = appData.overrideLineGap;\r
3258   }\r
3259 \r
3260   if (tinyLayout != oldTinyLayout) {\r
3261     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3262     if (tinyLayout) {\r
3263       style &= ~WS_SYSMENU;\r
3264       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3265                  "&Minimize\tCtrl+F4");\r
3266     } else {\r
3267       style |= WS_SYSMENU;\r
3268       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3269     }\r
3270     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3271 \r
3272     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3273       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3274         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3275     }\r
3276     DrawMenuBar(hwndMain);\r
3277   }\r
3278 \r
3279   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3280   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3281 \r
3282   /* Get text area sizes */\r
3283   hdc = GetDC(hwndMain);\r
3284   if (appData.clockMode) {\r
3285     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3286   } else {\r
3287     sprintf(buf, "White");\r
3288   }\r
3289   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3290   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3291   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3292   str = "We only care about the height here";\r
3293   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3294   SelectObject(hdc, oldFont);\r
3295   ReleaseDC(hwndMain, hdc);\r
3296 \r
3297   /* Compute where everything goes */\r
3298   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3299         /* [HGM] logo: if either logo is on, reserve space for it */\r
3300         logoHeight =  2*clockSize.cy;\r
3301         leftLogoRect.left   = OUTER_MARGIN;\r
3302         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3303         leftLogoRect.top    = OUTER_MARGIN;\r
3304         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3305 \r
3306         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3307         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3308         rightLogoRect.top    = OUTER_MARGIN;\r
3309         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3310 \r
3311 \r
3312     whiteRect.left = leftLogoRect.right;\r
3313     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3314     whiteRect.top = OUTER_MARGIN;\r
3315     whiteRect.bottom = whiteRect.top + logoHeight;\r
3316 \r
3317     blackRect.right = rightLogoRect.left;\r
3318     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3319     blackRect.top = whiteRect.top;\r
3320     blackRect.bottom = whiteRect.bottom;\r
3321   } else {\r
3322     whiteRect.left = OUTER_MARGIN;\r
3323     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3324     whiteRect.top = OUTER_MARGIN;\r
3325     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3326 \r
3327     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3328     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3329     blackRect.top = whiteRect.top;\r
3330     blackRect.bottom = whiteRect.bottom;\r
3331   }\r
3332 \r
3333   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3334   if (appData.showButtonBar) {\r
3335     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3336       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3337   } else {\r
3338     messageRect.right = OUTER_MARGIN + boardWidth;\r
3339   }\r
3340   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3341   messageRect.bottom = messageRect.top + messageSize.cy;\r
3342 \r
3343   boardRect.left = OUTER_MARGIN;\r
3344   boardRect.right = boardRect.left + boardWidth;\r
3345   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3346   boardRect.bottom = boardRect.top + boardHeight;\r
3347 \r
3348   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3349   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3350   oldBoardSize = boardSize;\r
3351   oldTinyLayout = tinyLayout;\r
3352   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3353   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3354     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3355   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3356   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3357   winHeight = winH; //       without disturbing window attachments\r
3358   GetWindowRect(hwndMain, &wrect);\r
3359   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3360                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3361 \r
3362   // [HGM] placement: let attached windows follow size change.\r
3363   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3364   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3365   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3366   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3367   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3368 \r
3369   /* compensate if menu bar wrapped */\r
3370   GetClientRect(hwndMain, &crect);\r
3371   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3372   winHeight += offby;\r
3373   switch (flags) {\r
3374   case WMSZ_TOPLEFT:\r
3375     SetWindowPos(hwndMain, NULL, \r
3376                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3377                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3378     break;\r
3379 \r
3380   case WMSZ_TOPRIGHT:\r
3381   case WMSZ_TOP:\r
3382     SetWindowPos(hwndMain, NULL, \r
3383                  wrect.left, wrect.bottom - winHeight, \r
3384                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3385     break;\r
3386 \r
3387   case WMSZ_BOTTOMLEFT:\r
3388   case WMSZ_LEFT:\r
3389     SetWindowPos(hwndMain, NULL, \r
3390                  wrect.right - winWidth, wrect.top, \r
3391                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3392     break;\r
3393 \r
3394   case WMSZ_BOTTOMRIGHT:\r
3395   case WMSZ_BOTTOM:\r
3396   case WMSZ_RIGHT:\r
3397   default:\r
3398     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3399                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3400     break;\r
3401   }\r
3402 \r
3403   hwndPause = NULL;\r
3404   for (i = 0; i < N_BUTTONS; i++) {\r
3405     if (buttonDesc[i].hwnd != NULL) {\r
3406       DestroyWindow(buttonDesc[i].hwnd);\r
3407       buttonDesc[i].hwnd = NULL;\r
3408     }\r
3409     if (appData.showButtonBar) {\r
3410       buttonDesc[i].hwnd =\r
3411         CreateWindow("BUTTON", buttonDesc[i].label,\r
3412                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3413                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3414                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3415                      (HMENU) buttonDesc[i].id,\r
3416                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3417       if (tinyLayout) {\r
3418         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3419                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3420                     MAKELPARAM(FALSE, 0));\r
3421       }\r
3422       if (buttonDesc[i].id == IDM_Pause)\r
3423         hwndPause = buttonDesc[i].hwnd;\r
3424       buttonDesc[i].wndproc = (WNDPROC)\r
3425         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3426     }\r
3427   }\r
3428   if (gridPen != NULL) DeleteObject(gridPen);\r
3429   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3430   if (premovePen != NULL) DeleteObject(premovePen);\r
3431   if (lineGap != 0) {\r
3432     logbrush.lbStyle = BS_SOLID;\r
3433     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3434     gridPen =\r
3435       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3436                    lineGap, &logbrush, 0, NULL);\r
3437     logbrush.lbColor = highlightSquareColor;\r
3438     highlightPen =\r
3439       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3440                    lineGap, &logbrush, 0, NULL);\r
3441 \r
3442     logbrush.lbColor = premoveHighlightColor; \r
3443     premovePen =\r
3444       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3445                    lineGap, &logbrush, 0, NULL);\r
3446 \r
3447     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3448     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3449       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3450       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3451         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3452       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3453         BOARD_WIDTH * (squareSize + lineGap);\r
3454       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3455     }\r
3456     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3457       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3458       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3459         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3460         lineGap / 2 + (i * (squareSize + lineGap));\r
3461       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3462         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3463       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3464     }\r
3465   }\r
3466 \r
3467   /* [HGM] Licensing requirement */\r
3468 #ifdef GOTHIC\r
3469   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3470 #endif\r
3471 #ifdef FALCON\r
3472   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3473 #endif\r
3474   GothicPopUp( "", VariantNormal);\r
3475 \r
3476 \r
3477 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3478 \r
3479   /* Load piece bitmaps for this board size */\r
3480   for (i=0; i<=2; i++) {\r
3481     for (piece = WhitePawn;\r
3482          (int) piece < (int) BlackPawn;\r
3483          piece = (ChessSquare) ((int) piece + 1)) {\r
3484       if (pieceBitmap[i][piece] != NULL)\r
3485         DeleteObject(pieceBitmap[i][piece]);\r
3486     }\r
3487   }\r
3488 \r
3489   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3490   // Orthodox Chess pieces\r
3491   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3492   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3493   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3494   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3495   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3496   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3497   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3498   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3499   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3500   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3501   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3502   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3503   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3504   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3505   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3506   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3507     // in Shogi, Hijack the unused Queen for Lance\r
3508     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3509     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3510     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3511   } else {\r
3512     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3513     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3514     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3515   }\r
3516 \r
3517   if(squareSize <= 72 && squareSize >= 33) { \r
3518     /* A & C are available in most sizes now */\r
3519     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3520       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3521       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3522       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3523       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3524       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3525       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3526       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3527       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3528       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3529       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3530       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3531       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3532     } else { // Smirf-like\r
3533       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3534       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3535       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3536     }\r
3537     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3538       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3539       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3540       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3541     } else { // WinBoard standard\r
3542       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3543       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3544       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3545     }\r
3546   }\r
3547 \r
3548 \r
3549   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3550     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3551     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3552     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3553     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3554     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3555     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3556     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3557     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3558     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3559     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3560     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3561     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3562     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3563     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3564     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3565     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3566     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3567     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3568     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3569     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3570     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3571     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3572     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3573     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3574     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3575     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3576     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3577     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3578     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3579     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3580 \r
3581     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3582       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3583       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3584       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3585       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3586       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3587       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3588       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3589       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3590       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3591       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3592       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3593       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3594     } else {\r
3595       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3596       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3597       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3598       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3599       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3600       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3601       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3602       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3603       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3604       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3605       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3606       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3607     }\r
3608 \r
3609   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3610     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3611     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3612     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3613     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3614     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3615     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3616     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3617     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3618     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3619     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3620     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3621     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3622     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3623     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3624   }\r
3625 \r
3626 \r
3627   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3628   /* special Shogi support in this size */\r
3629   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3630       for (piece = WhitePawn;\r
3631            (int) piece < (int) BlackPawn;\r
3632            piece = (ChessSquare) ((int) piece + 1)) {\r
3633         if (pieceBitmap[i][piece] != NULL)\r
3634           DeleteObject(pieceBitmap[i][piece]);\r
3635       }\r
3636     }\r
3637   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3638   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3639   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3640   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3641   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3642   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3643   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3644   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3645   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3646   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3647   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3648   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3649   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3650   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3651   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3652   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3653   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3654   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3655   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3656   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3657   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3658   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3659   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3660   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3661   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3662   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3663   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3664   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3665   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3666   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3667   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3668   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3669   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3670   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3671   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3672   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3673   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3674   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3675   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3676   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3677   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3678   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3679   minorSize = 0;\r
3680   }\r
3681 }\r
3682 \r
3683 HBITMAP\r
3684 PieceBitmap(ChessSquare p, int kind)\r
3685 {\r
3686   if ((int) p >= (int) BlackPawn)\r
3687     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3688 \r
3689   return pieceBitmap[kind][(int) p];\r
3690 }\r
3691 \r
3692 /***************************************************************/\r
3693 \r
3694 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3695 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3696 /*\r
3697 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3698 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3699 */\r
3700 \r
3701 VOID\r
3702 SquareToPos(int row, int column, int * x, int * y)\r
3703 {\r
3704   if (flipView) {\r
3705     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3706     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3707   } else {\r
3708     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3709     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3710   }\r
3711 }\r
3712 \r
3713 VOID\r
3714 DrawCoordsOnDC(HDC hdc)\r
3715 {\r
3716   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
3717   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
3718   char str[2] = { NULLCHAR, NULLCHAR };\r
3719   int oldMode, oldAlign, x, y, start, i;\r
3720   HFONT oldFont;\r
3721   HBRUSH oldBrush;\r
3722 \r
3723   if (!appData.showCoords)\r
3724     return;\r
3725 \r
3726   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3727 \r
3728   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3729   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3730   oldAlign = GetTextAlign(hdc);\r
3731   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3732 \r
3733   y = boardRect.top + lineGap;\r
3734   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3735 \r
3736   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3737   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3738     str[0] = files[start + i];\r
3739     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3740     y += squareSize + lineGap;\r
3741   }\r
3742 \r
3743   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3744 \r
3745   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3746   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3747     str[0] = ranks[start + i];\r
3748     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3749     x += squareSize + lineGap;\r
3750   }    \r
3751 \r
3752   SelectObject(hdc, oldBrush);\r
3753   SetBkMode(hdc, oldMode);\r
3754   SetTextAlign(hdc, oldAlign);\r
3755   SelectObject(hdc, oldFont);\r
3756 }\r
3757 \r
3758 VOID\r
3759 DrawGridOnDC(HDC hdc)\r
3760 {\r
3761   HPEN oldPen;\r
3762  \r
3763   if (lineGap != 0) {\r
3764     oldPen = SelectObject(hdc, gridPen);\r
3765     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3766     SelectObject(hdc, oldPen);\r
3767   }\r
3768 }\r
3769 \r
3770 #define HIGHLIGHT_PEN 0\r
3771 #define PREMOVE_PEN   1\r
3772 \r
3773 VOID\r
3774 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3775 {\r
3776   int x1, y1;\r
3777   HPEN oldPen, hPen;\r
3778   if (lineGap == 0) return;\r
3779   if (flipView) {\r
3780     x1 = boardRect.left +\r
3781       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3782     y1 = boardRect.top +\r
3783       lineGap/2 + y * (squareSize + lineGap);\r
3784   } else {\r
3785     x1 = boardRect.left +\r
3786       lineGap/2 + x * (squareSize + lineGap);\r
3787     y1 = boardRect.top +\r
3788       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3789   }\r
3790   hPen = pen ? premovePen : highlightPen;\r
3791   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3792   MoveToEx(hdc, x1, y1, NULL);\r
3793   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3794   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3795   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3796   LineTo(hdc, x1, y1);\r
3797   SelectObject(hdc, oldPen);\r
3798 }\r
3799 \r
3800 VOID\r
3801 DrawHighlightsOnDC(HDC hdc)\r
3802 {\r
3803   int i;\r
3804   for (i=0; i<2; i++) {\r
3805     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3806       DrawHighlightOnDC(hdc, TRUE,\r
3807                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3808                         HIGHLIGHT_PEN);\r
3809   }\r
3810   for (i=0; i<2; i++) {\r
3811     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3812         premoveHighlightInfo.sq[i].y >= 0) {\r
3813         DrawHighlightOnDC(hdc, TRUE,\r
3814                           premoveHighlightInfo.sq[i].x, \r
3815                           premoveHighlightInfo.sq[i].y,\r
3816                           PREMOVE_PEN);\r
3817     }\r
3818   }\r
3819 }\r
3820 \r
3821 /* Note: sqcolor is used only in monoMode */\r
3822 /* Note that this code is largely duplicated in woptions.c,\r
3823    function DrawSampleSquare, so that needs to be updated too */\r
3824 VOID\r
3825 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3826 {\r
3827   HBITMAP oldBitmap;\r
3828   HBRUSH oldBrush;\r
3829   int tmpSize;\r
3830 \r
3831   if (appData.blindfold) return;\r
3832 \r
3833   /* [AS] Use font-based pieces if needed */\r
3834   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3835     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3836     CreatePiecesFromFont();\r
3837 \r
3838     if( fontBitmapSquareSize == squareSize ) {\r
3839         int index = TranslatePieceToFontPiece(piece);\r
3840 \r
3841         SelectObject( tmphdc, hPieceMask[ index ] );\r
3842 \r
3843         BitBlt( hdc,\r
3844             x, y,\r
3845             squareSize, squareSize,\r
3846             tmphdc,\r
3847             0, 0,\r
3848             SRCAND );\r
3849 \r
3850         SelectObject( tmphdc, hPieceFace[ index ] );\r
3851 \r
3852         BitBlt( hdc,\r
3853             x, y,\r
3854             squareSize, squareSize,\r
3855             tmphdc,\r
3856             0, 0,\r
3857             SRCPAINT );\r
3858 \r
3859         return;\r
3860     }\r
3861   }\r
3862 \r
3863   if (appData.monoMode) {\r
3864     SelectObject(tmphdc, PieceBitmap(piece, \r
3865       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3866     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3867            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3868   } else {\r
3869     tmpSize = squareSize;\r
3870     if(minorSize &&\r
3871         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3872          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3873       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3874       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3875       x += (squareSize - minorSize)>>1;\r
3876       y += squareSize - minorSize - 2;\r
3877       tmpSize = minorSize;\r
3878     }\r
3879     if (color || appData.allWhite ) {\r
3880       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3881       if( color )\r
3882               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3883       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3884       if(appData.upsideDown && color==flipView)\r
3885         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3886       else\r
3887         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3888 #if 0\r
3889       /* Use black piece color for outline of white pieces */\r
3890       /* Not sure this looks really good (though xboard does it).\r
3891          Maybe better to have another selectable color, default black */\r
3892       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3893       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3894       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3895 #else\r
3896       /* Use black for outline of white pieces */\r
3897       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3898       if(appData.upsideDown && color==flipView)\r
3899         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3900       else\r
3901         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3902 #endif\r
3903     } else {\r
3904 #if 0\r
3905       /* Use white piece color for details of black pieces */\r
3906       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3907          WHITE_PIECE ones aren't always the right shape. */\r
3908       /* Not sure this looks really good (though xboard does it).\r
3909          Maybe better to have another selectable color, default medium gray? */\r
3910       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3911       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3912       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3913       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3914       SelectObject(hdc, blackPieceBrush);\r
3915       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3916 #else\r
3917       /* Use square color for details of black pieces */\r
3918       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3919       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3920       if(appData.upsideDown && !flipView)\r
3921         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3922       else\r
3923         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3924 #endif\r
3925     }\r
3926     SelectObject(hdc, oldBrush);\r
3927     SelectObject(tmphdc, oldBitmap);\r
3928   }\r
3929 }\r
3930 \r
3931 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3932 int GetBackTextureMode( int algo )\r
3933 {\r
3934     int result = BACK_TEXTURE_MODE_DISABLED;\r
3935 \r
3936     switch( algo ) \r
3937     {\r
3938         case BACK_TEXTURE_MODE_PLAIN:\r
3939             result = 1; /* Always use identity map */\r
3940             break;\r
3941         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3942             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3943             break;\r
3944     }\r
3945 \r
3946     return result;\r
3947 }\r
3948 \r
3949 /* \r
3950     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3951     to handle redraws cleanly (as random numbers would always be different).\r
3952 */\r
3953 VOID RebuildTextureSquareInfo()\r
3954 {\r
3955     BITMAP bi;\r
3956     int lite_w = 0;\r
3957     int lite_h = 0;\r
3958     int dark_w = 0;\r
3959     int dark_h = 0;\r
3960     int row;\r
3961     int col;\r
3962 \r
3963     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3964 \r
3965     if( liteBackTexture != NULL ) {\r
3966         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3967             lite_w = bi.bmWidth;\r
3968             lite_h = bi.bmHeight;\r
3969         }\r
3970     }\r
3971 \r
3972     if( darkBackTexture != NULL ) {\r
3973         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3974             dark_w = bi.bmWidth;\r
3975             dark_h = bi.bmHeight;\r
3976         }\r
3977     }\r
3978 \r
3979     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3980         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3981             if( (col + row) & 1 ) {\r
3982                 /* Lite square */\r
3983                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3984                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3985                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3986                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3987                 }\r
3988             }\r
3989             else {\r
3990                 /* Dark square */\r
3991                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3992                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3993                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3994                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3995                 }\r
3996             }\r
3997         }\r
3998     }\r
3999 }\r
4000 \r
4001 /* [AS] Arrow highlighting support */\r
4002 \r
4003 static int A_WIDTH = 5; /* Width of arrow body */\r
4004 \r
4005 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
4006 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
4007 \r
4008 static double Sqr( double x )\r
4009 {\r
4010     return x*x;\r
4011 }\r
4012 \r
4013 static int Round( double x )\r
4014 {\r
4015     return (int) (x + 0.5);\r
4016 }\r
4017 \r
4018 /* Draw an arrow between two points using current settings */\r
4019 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
4020 {\r
4021     POINT arrow[7];\r
4022     double dx, dy, j, k, x, y;\r
4023 \r
4024     if( d_x == s_x ) {\r
4025         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4026 \r
4027         arrow[0].x = s_x + A_WIDTH;\r
4028         arrow[0].y = s_y;\r
4029 \r
4030         arrow[1].x = s_x + A_WIDTH;\r
4031         arrow[1].y = d_y - h;\r
4032 \r
4033         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
4034         arrow[2].y = d_y - h;\r
4035 \r
4036         arrow[3].x = d_x;\r
4037         arrow[3].y = d_y;\r
4038 \r
4039         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
4040         arrow[4].y = d_y - h;\r
4041 \r
4042         arrow[5].x = s_x - A_WIDTH;\r
4043         arrow[5].y = d_y - h;\r
4044 \r
4045         arrow[6].x = s_x - A_WIDTH;\r
4046         arrow[6].y = s_y;\r
4047     }\r
4048     else if( d_y == s_y ) {\r
4049         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4050 \r
4051         arrow[0].x = s_x;\r
4052         arrow[0].y = s_y + A_WIDTH;\r
4053 \r
4054         arrow[1].x = d_x - w;\r
4055         arrow[1].y = s_y + A_WIDTH;\r
4056 \r
4057         arrow[2].x = d_x - w;\r
4058         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
4059 \r
4060         arrow[3].x = d_x;\r
4061         arrow[3].y = d_y;\r
4062 \r
4063         arrow[4].x = d_x - w;\r
4064         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
4065 \r
4066         arrow[5].x = d_x - w;\r
4067         arrow[5].y = s_y - A_WIDTH;\r
4068 \r
4069         arrow[6].x = s_x;\r
4070         arrow[6].y = s_y - A_WIDTH;\r
4071     }\r
4072     else {\r
4073         /* [AS] Needed a lot of paper for this! :-) */\r
4074         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4075         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4076   \r
4077         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4078 \r
4079         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4080 \r
4081         x = s_x;\r
4082         y = s_y;\r
4083 \r
4084         arrow[0].x = Round(x - j);\r
4085         arrow[0].y = Round(y + j*dx);\r
4086 \r
4087         arrow[1].x = Round(x + j);\r
4088         arrow[1].y = Round(y - j*dx);\r
4089 \r
4090         if( d_x > s_x ) {\r
4091             x = (double) d_x - k;\r
4092             y = (double) d_y - k*dy;\r
4093         }\r
4094         else {\r
4095             x = (double) d_x + k;\r
4096             y = (double) d_y + k*dy;\r
4097         }\r
4098 \r
4099         arrow[2].x = Round(x + j);\r
4100         arrow[2].y = Round(y - j*dx);\r
4101 \r
4102         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4103         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4104 \r
4105         arrow[4].x = d_x;\r
4106         arrow[4].y = d_y;\r
4107 \r
4108         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4109         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4110 \r
4111         arrow[6].x = Round(x - j);\r
4112         arrow[6].y = Round(y + j*dx);\r
4113     }\r
4114 \r
4115     Polygon( hdc, arrow, 7 );\r
4116 }\r
4117 \r
4118 /* [AS] Draw an arrow between two squares */\r
4119 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4120 {\r
4121     int s_x, s_y, d_x, d_y;\r
4122     HPEN hpen;\r
4123     HPEN holdpen;\r
4124     HBRUSH hbrush;\r
4125     HBRUSH holdbrush;\r
4126     LOGBRUSH stLB;\r
4127 \r
4128     if( s_col == d_col && s_row == d_row ) {\r
4129         return;\r
4130     }\r
4131 \r
4132     /* Get source and destination points */\r
4133     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4134     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4135 \r
4136     if( d_y > s_y ) {\r
4137         d_y += squareSize / 4;\r
4138     }\r
4139     else if( d_y < s_y ) {\r
4140         d_y += 3 * squareSize / 4;\r
4141     }\r
4142     else {\r
4143         d_y += squareSize / 2;\r
4144     }\r
4145 \r
4146     if( d_x > s_x ) {\r
4147         d_x += squareSize / 4;\r
4148     }\r
4149     else if( d_x < s_x ) {\r
4150         d_x += 3 * squareSize / 4;\r
4151     }\r
4152     else {\r
4153         d_x += squareSize / 2;\r
4154     }\r
4155 \r
4156     s_x += squareSize / 2;\r
4157     s_y += squareSize / 2;\r
4158 \r
4159     /* Adjust width */\r
4160     A_WIDTH = squareSize / 14;\r
4161 \r
4162     /* Draw */\r
4163     stLB.lbStyle = BS_SOLID;\r
4164     stLB.lbColor = appData.highlightArrowColor;\r
4165     stLB.lbHatch = 0;\r
4166 \r
4167     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4168     holdpen = SelectObject( hdc, hpen );\r
4169     hbrush = CreateBrushIndirect( &stLB );\r
4170     holdbrush = SelectObject( hdc, hbrush );\r
4171 \r
4172     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4173 \r
4174     SelectObject( hdc, holdpen );\r
4175     SelectObject( hdc, holdbrush );\r
4176     DeleteObject( hpen );\r
4177     DeleteObject( hbrush );\r
4178 }\r
4179 \r
4180 BOOL HasHighlightInfo()\r
4181 {\r
4182     BOOL result = FALSE;\r
4183 \r
4184     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4185         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4186     {\r
4187         result = TRUE;\r
4188     }\r
4189 \r
4190     return result;\r
4191 }\r
4192 \r
4193 BOOL IsDrawArrowEnabled()\r
4194 {\r
4195     BOOL result = FALSE;\r
4196 \r
4197     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4198         result = TRUE;\r
4199     }\r
4200 \r
4201     return result;\r
4202 }\r
4203 \r
4204 VOID DrawArrowHighlight( HDC hdc )\r
4205 {\r
4206     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4207         DrawArrowBetweenSquares( hdc,\r
4208             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4209             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4210     }\r
4211 }\r
4212 \r
4213 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4214 {\r
4215     HRGN result = NULL;\r
4216 \r
4217     if( HasHighlightInfo() ) {\r
4218         int x1, y1, x2, y2;\r
4219         int sx, sy, dx, dy;\r
4220 \r
4221         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4222         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4223 \r
4224         sx = MIN( x1, x2 );\r
4225         sy = MIN( y1, y2 );\r
4226         dx = MAX( x1, x2 ) + squareSize;\r
4227         dy = MAX( y1, y2 ) + squareSize;\r
4228 \r
4229         result = CreateRectRgn( sx, sy, dx, dy );\r
4230     }\r
4231 \r
4232     return result;\r
4233 }\r
4234 \r
4235 /*\r
4236     Warning: this function modifies the behavior of several other functions. \r
4237     \r
4238     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4239     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4240     repaint is scattered all over the place, which is not good for features such as\r
4241     "arrow highlighting" that require a full repaint of the board.\r
4242 \r
4243     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4244     user interaction, when speed is not so important) but especially to avoid errors\r
4245     in the displayed graphics.\r
4246 \r
4247     In such patched places, I always try refer to this function so there is a single\r
4248     place to maintain knowledge.\r
4249     \r
4250     To restore the original behavior, just return FALSE unconditionally.\r
4251 */\r
4252 BOOL IsFullRepaintPreferrable()\r
4253 {\r
4254     BOOL result = FALSE;\r
4255 \r
4256     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4257         /* Arrow may appear on the board */\r
4258         result = TRUE;\r
4259     }\r
4260 \r
4261     return result;\r
4262 }\r
4263 \r
4264 /* \r
4265     This function is called by DrawPosition to know whether a full repaint must\r
4266     be forced or not.\r
4267 \r
4268     Only DrawPosition may directly call this function, which makes use of \r
4269     some state information. Other function should call DrawPosition specifying \r
4270     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4271 */\r
4272 BOOL DrawPositionNeedsFullRepaint()\r
4273 {\r
4274     BOOL result = FALSE;\r
4275 \r
4276     /* \r
4277         Probably a slightly better policy would be to trigger a full repaint\r
4278         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4279         but animation is fast enough that it's difficult to notice.\r
4280     */\r
4281     if( animInfo.piece == EmptySquare ) {\r
4282         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4283             result = TRUE;\r
4284         }\r
4285     }\r
4286 \r
4287     return result;\r
4288 }\r
4289 \r
4290 VOID\r
4291 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4292 {\r
4293   int row, column, x, y, square_color, piece_color;\r
4294   ChessSquare piece;\r
4295   HBRUSH oldBrush;\r
4296   HDC texture_hdc = NULL;\r
4297 \r
4298   /* [AS] Initialize background textures if needed */\r
4299   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4300       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4301       if( backTextureSquareSize != squareSize \r
4302        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4303           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4304           backTextureSquareSize = squareSize;\r
4305           RebuildTextureSquareInfo();\r
4306       }\r
4307 \r
4308       texture_hdc = CreateCompatibleDC( hdc );\r
4309   }\r
4310 \r
4311   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4312     for (column = 0; column < BOARD_WIDTH; column++) {\r
4313   \r
4314       SquareToPos(row, column, &x, &y);\r
4315 \r
4316       piece = board[row][column];\r
4317 \r
4318       square_color = ((column + row) % 2) == 1;\r
4319       if( gameInfo.variant == VariantXiangqi ) {\r
4320           square_color = !InPalace(row, column);\r
4321           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4322           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4323       }\r
4324       piece_color = (int) piece < (int) BlackPawn;\r
4325 \r
4326 \r
4327       /* [HGM] holdings file: light square or black */\r
4328       if(column == BOARD_LEFT-2) {\r
4329             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4330                 square_color = 1;\r
4331             else {\r
4332                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4333                 continue;\r
4334             }\r
4335       } else\r
4336       if(column == BOARD_RGHT + 1 ) {\r
4337             if( row < gameInfo.holdingsSize )\r
4338                 square_color = 1;\r
4339             else {\r
4340                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4341                 continue;\r
4342             }\r
4343       }\r
4344       if(column == BOARD_LEFT-1 ) /* left align */\r
4345             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4346       else if( column == BOARD_RGHT) /* right align */\r
4347             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4348       else\r
4349       if (appData.monoMode) {\r
4350         if (piece == EmptySquare) {\r
4351           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4352                  square_color ? WHITENESS : BLACKNESS);\r
4353         } else {\r
4354           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4355         }\r
4356       } \r
4357       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4358           /* [AS] Draw the square using a texture bitmap */\r
4359           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4360           int r = row, c = column; // [HGM] do not flip board in flipView\r
4361           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4362 \r
4363           DrawTile( x, y, \r
4364               squareSize, squareSize, \r
4365               hdc, \r
4366               texture_hdc,\r
4367               backTextureSquareInfo[r][c].mode,\r
4368               backTextureSquareInfo[r][c].x,\r
4369               backTextureSquareInfo[r][c].y );\r
4370 \r
4371           SelectObject( texture_hdc, hbm );\r
4372 \r
4373           if (piece != EmptySquare) {\r
4374               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4375           }\r
4376       }\r
4377       else {\r
4378         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4379 \r
4380         oldBrush = SelectObject(hdc, brush );\r
4381         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4382         SelectObject(hdc, oldBrush);\r
4383         if (piece != EmptySquare)\r
4384           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4385       }\r
4386     }\r
4387   }\r
4388 \r
4389   if( texture_hdc != NULL ) {\r
4390     DeleteDC( texture_hdc );\r
4391   }\r
4392 }\r
4393 \r
4394 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4395 void fputDW(FILE *f, int x)\r
4396 {\r
4397         fputc(x     & 255, f);\r
4398         fputc(x>>8  & 255, f);\r
4399         fputc(x>>16 & 255, f);\r
4400         fputc(x>>24 & 255, f);\r
4401 }\r
4402 \r
4403 #define MAX_CLIPS 200   /* more than enough */\r
4404 \r
4405 VOID\r
4406 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4407 {\r
4408 //  HBITMAP bufferBitmap;\r
4409   BITMAP bi;\r
4410 //  RECT Rect;\r
4411   HDC tmphdc;\r
4412   HBITMAP hbm;\r
4413   int w = 100, h = 50;\r
4414 \r
4415   if(logo == NULL) return;\r
4416 //  GetClientRect(hwndMain, &Rect);\r
4417 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4418 //                                      Rect.bottom-Rect.top+1);\r
4419   tmphdc = CreateCompatibleDC(hdc);\r
4420   hbm = SelectObject(tmphdc, logo);\r
4421   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4422             w = bi.bmWidth;\r
4423             h = bi.bmHeight;\r
4424   }\r
4425   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4426                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4427   SelectObject(tmphdc, hbm);\r
4428   DeleteDC(tmphdc);\r
4429 }\r
4430 \r
4431 VOID\r
4432 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4433 {\r
4434   static Board lastReq, lastDrawn;\r
4435   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4436   static int lastDrawnFlipView = 0;\r
4437   static int lastReqValid = 0, lastDrawnValid = 0;\r
4438   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4439   HDC tmphdc;\r
4440   HDC hdcmem;\r
4441   HBITMAP bufferBitmap;\r
4442   HBITMAP oldBitmap;\r
4443   RECT Rect;\r
4444   HRGN clips[MAX_CLIPS];\r
4445   ChessSquare dragged_piece = EmptySquare;\r
4446 \r
4447   /* I'm undecided on this - this function figures out whether a full\r
4448    * repaint is necessary on its own, so there's no real reason to have the\r
4449    * caller tell it that.  I think this can safely be set to FALSE - but\r
4450    * if we trust the callers not to request full repaints unnessesarily, then\r
4451    * we could skip some clipping work.  In other words, only request a full\r
4452    * redraw when the majority of pieces have changed positions (ie. flip, \r
4453    * gamestart and similar)  --Hawk\r
4454    */\r
4455   Boolean fullrepaint = repaint;\r
4456 \r
4457   if( DrawPositionNeedsFullRepaint() ) {\r
4458       fullrepaint = TRUE;\r
4459   }\r
4460 \r
4461 #if 0\r
4462   if( fullrepaint ) {\r
4463       static int repaint_count = 0;\r
4464       char buf[128];\r
4465 \r
4466       repaint_count++;\r
4467       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4468       OutputDebugString( buf );\r
4469   }\r
4470 #endif\r
4471 \r
4472   if (board == NULL) {\r
4473     if (!lastReqValid) {\r
4474       return;\r
4475     }\r
4476     board = lastReq;\r
4477   } else {\r
4478     CopyBoard(lastReq, board);\r
4479     lastReqValid = 1;\r
4480   }\r
4481 \r
4482   if (doingSizing) {\r
4483     return;\r
4484   }\r
4485 \r
4486   if (IsIconic(hwndMain)) {\r
4487     return;\r
4488   }\r
4489 \r
4490   if (hdc == NULL) {\r
4491     hdc = GetDC(hwndMain);\r
4492     if (!appData.monoMode) {\r
4493       SelectPalette(hdc, hPal, FALSE);\r
4494       RealizePalette(hdc);\r
4495     }\r
4496     releaseDC = TRUE;\r
4497   } else {\r
4498     releaseDC = FALSE;\r
4499   }\r
4500 \r
4501 #if 0\r
4502   fprintf(debugFP, "*******************************\n"\r
4503                    "repaint = %s\n"\r
4504                    "dragInfo.from (%d,%d)\n"\r
4505                    "dragInfo.start (%d,%d)\n"\r
4506                    "dragInfo.pos (%d,%d)\n"\r
4507                    "dragInfo.lastpos (%d,%d)\n", \r
4508                     repaint ? "TRUE" : "FALSE",\r
4509                     dragInfo.from.x, dragInfo.from.y, \r
4510                     dragInfo.start.x, dragInfo.start.y,\r
4511                     dragInfo.pos.x, dragInfo.pos.y,\r
4512                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4513   fprintf(debugFP, "prev:  ");\r
4514   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4515     for (column = 0; column < BOARD_WIDTH; column++) {\r
4516       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4517     }\r
4518   }\r
4519   fprintf(debugFP, "\n");\r
4520   fprintf(debugFP, "board: ");\r
4521   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4522     for (column = 0; column < BOARD_WIDTH; column++) {\r
4523       fprintf(debugFP, "%d ", board[row][column]);\r
4524     }\r
4525   }\r
4526   fprintf(debugFP, "\n");\r
4527   fflush(debugFP);\r
4528 #endif\r
4529 \r
4530   /* Create some work-DCs */\r
4531   hdcmem = CreateCompatibleDC(hdc);\r
4532   tmphdc = CreateCompatibleDC(hdc);\r
4533 \r
4534   /* If dragging is in progress, we temporarely remove the piece */\r
4535   /* [HGM] or temporarily decrease count if stacked              */\r
4536   /*       !! Moved to before board compare !!                   */\r
4537   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4538     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4539     if(dragInfo.from.x == BOARD_LEFT-2 ) {\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     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4544             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4545         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4546     } else \r
4547         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4548   }\r
4549 \r
4550   /* Figure out which squares need updating by comparing the \r
4551    * newest board with the last drawn board and checking if\r
4552    * flipping has changed.\r
4553    */\r
4554   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4555     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4556       for (column = 0; column < BOARD_WIDTH; column++) {\r
4557         if (lastDrawn[row][column] != board[row][column]) {\r
4558           SquareToPos(row, column, &x, &y);\r
4559           clips[num_clips++] =\r
4560             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4561         }\r
4562       }\r
4563     }\r
4564     for (i=0; i<2; i++) {\r
4565       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4566           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4567         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4568             lastDrawnHighlight.sq[i].y >= 0) {\r
4569           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4570                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4571           clips[num_clips++] =\r
4572             CreateRectRgn(x - lineGap, y - lineGap, \r
4573                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4574         }\r
4575         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4576           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4577           clips[num_clips++] =\r
4578             CreateRectRgn(x - lineGap, y - lineGap, \r
4579                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4580         }\r
4581       }\r
4582     }\r
4583     for (i=0; i<2; i++) {\r
4584       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4585           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4586         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4587             lastDrawnPremove.sq[i].y >= 0) {\r
4588           SquareToPos(lastDrawnPremove.sq[i].y,\r
4589                       lastDrawnPremove.sq[i].x, &x, &y);\r
4590           clips[num_clips++] =\r
4591             CreateRectRgn(x - lineGap, y - lineGap, \r
4592                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4593         }\r
4594         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4595             premoveHighlightInfo.sq[i].y >= 0) {\r
4596           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4597                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4598           clips[num_clips++] =\r
4599             CreateRectRgn(x - lineGap, y - lineGap, \r
4600                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4601         }\r
4602       }\r
4603     }\r
4604   } else {\r
4605     fullrepaint = TRUE;\r
4606   }\r
4607 \r
4608   /* Create a buffer bitmap - this is the actual bitmap\r
4609    * being written to.  When all the work is done, we can\r
4610    * copy it to the real DC (the screen).  This avoids\r
4611    * the problems with flickering.\r
4612    */\r
4613   GetClientRect(hwndMain, &Rect);\r
4614   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4615                                         Rect.bottom-Rect.top+1);\r
4616   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4617   if (!appData.monoMode) {\r
4618     SelectPalette(hdcmem, hPal, FALSE);\r
4619   }\r
4620 \r
4621   /* Create clips for dragging */\r
4622   if (!fullrepaint) {\r
4623     if (dragInfo.from.x >= 0) {\r
4624       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4625       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4626     }\r
4627     if (dragInfo.start.x >= 0) {\r
4628       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4629       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4630     }\r
4631     if (dragInfo.pos.x >= 0) {\r
4632       x = dragInfo.pos.x - squareSize / 2;\r
4633       y = dragInfo.pos.y - squareSize / 2;\r
4634       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4635     }\r
4636     if (dragInfo.lastpos.x >= 0) {\r
4637       x = dragInfo.lastpos.x - squareSize / 2;\r
4638       y = dragInfo.lastpos.y - squareSize / 2;\r
4639       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4640     }\r
4641   }\r
4642 \r
4643   /* Are we animating a move?  \r
4644    * If so, \r
4645    *   - remove the piece from the board (temporarely)\r
4646    *   - calculate the clipping region\r
4647    */\r
4648   if (!fullrepaint) {\r
4649     if (animInfo.piece != EmptySquare) {\r
4650       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4651       x = boardRect.left + animInfo.lastpos.x;\r
4652       y = boardRect.top + animInfo.lastpos.y;\r
4653       x2 = boardRect.left + animInfo.pos.x;\r
4654       y2 = boardRect.top + animInfo.pos.y;\r
4655       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4656       /* Slight kludge.  The real problem is that after AnimateMove is\r
4657          done, the position on the screen does not match lastDrawn.\r
4658          This currently causes trouble only on e.p. captures in\r
4659          atomic, where the piece moves to an empty square and then\r
4660          explodes.  The old and new positions both had an empty square\r
4661          at the destination, but animation has drawn a piece there and\r
4662          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4663       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4664     }\r
4665   }\r
4666 \r
4667   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4668   if (num_clips == 0)\r
4669     fullrepaint = TRUE;\r
4670 \r
4671   /* Set clipping on the memory DC */\r
4672   if (!fullrepaint) {\r
4673     SelectClipRgn(hdcmem, clips[0]);\r
4674     for (x = 1; x < num_clips; x++) {\r
4675       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4676         abort();  // this should never ever happen!\r
4677     }\r
4678   }\r
4679 \r
4680   /* Do all the drawing to the memory DC */\r
4681   if(explodeInfo.radius) { // [HGM] atomic\r
4682         HBRUSH oldBrush;\r
4683         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4684         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4685         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4686         x += squareSize/2;\r
4687         y += squareSize/2;\r
4688         if(!fullrepaint) {\r
4689           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4690           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4691         }\r
4692         DrawGridOnDC(hdcmem);\r
4693         DrawHighlightsOnDC(hdcmem);\r
4694         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4695         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4696         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4697         SelectObject(hdcmem, oldBrush);\r
4698   } else {\r
4699     DrawGridOnDC(hdcmem);\r
4700     DrawHighlightsOnDC(hdcmem);\r
4701     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4702   }\r
4703   if(logoHeight) {\r
4704         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4705         if(appData.autoLogo) {\r
4706           \r
4707           switch(gameMode) { // pick logos based on game mode\r
4708             case IcsObserving:\r
4709                 whiteLogo = second.programLogo; // ICS logo\r
4710                 blackLogo = second.programLogo;\r
4711             default:\r
4712                 break;\r
4713             case IcsPlayingWhite:\r
4714                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4715                 blackLogo = second.programLogo; // ICS logo\r
4716                 break;\r
4717             case IcsPlayingBlack:\r
4718                 whiteLogo = second.programLogo; // ICS logo\r
4719                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4720                 break;\r
4721             case TwoMachinesPlay:\r
4722                 if(first.twoMachinesColor[0] == 'b') {\r
4723                     whiteLogo = second.programLogo;\r
4724                     blackLogo = first.programLogo;\r
4725                 }\r
4726                 break;\r
4727             case MachinePlaysWhite:\r
4728                 blackLogo = userLogo;\r
4729                 break;\r
4730             case MachinePlaysBlack:\r
4731                 whiteLogo = userLogo;\r
4732                 blackLogo = first.programLogo;\r
4733           }\r
4734         }\r
4735         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4736         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4737   }\r
4738 \r
4739   if( appData.highlightMoveWithArrow ) {\r
4740     DrawArrowHighlight(hdcmem);\r
4741   }\r
4742 \r
4743   DrawCoordsOnDC(hdcmem);\r
4744 \r
4745   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4746                  /* to make sure lastDrawn contains what is actually drawn */\r
4747 \r
4748   /* Put the dragged piece back into place and draw it (out of place!) */\r
4749     if (dragged_piece != EmptySquare) {\r
4750     /* [HGM] or restack */\r
4751     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4752                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4753     else\r
4754     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4755                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4756     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4757     x = dragInfo.pos.x - squareSize / 2;\r
4758     y = dragInfo.pos.y - squareSize / 2;\r
4759     DrawPieceOnDC(hdcmem, dragged_piece,\r
4760                   ((int) dragged_piece < (int) BlackPawn), \r
4761                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4762   }   \r
4763   \r
4764   /* Put the animated piece back into place and draw it */\r
4765   if (animInfo.piece != EmptySquare) {\r
4766     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4767     x = boardRect.left + animInfo.pos.x;\r
4768     y = boardRect.top + animInfo.pos.y;\r
4769     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4770                   ((int) animInfo.piece < (int) BlackPawn),\r
4771                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4772   }\r
4773 \r
4774   /* Release the bufferBitmap by selecting in the old bitmap \r
4775    * and delete the memory DC\r
4776    */\r
4777   SelectObject(hdcmem, oldBitmap);\r
4778   DeleteDC(hdcmem);\r
4779 \r
4780   /* Set clipping on the target DC */\r
4781   if (!fullrepaint) {\r
4782     SelectClipRgn(hdc, clips[0]);\r
4783     for (x = 1; x < num_clips; x++) {\r
4784       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4785         abort();   // this should never ever happen!\r
4786     } \r
4787   }\r
4788 \r
4789   /* Copy the new bitmap onto the screen in one go.\r
4790    * This way we avoid any flickering\r
4791    */\r
4792   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4793   BitBlt(hdc, boardRect.left, boardRect.top,\r
4794          boardRect.right - boardRect.left,\r
4795          boardRect.bottom - boardRect.top,\r
4796          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4797   if(saveDiagFlag) { \r
4798     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4799     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4800 \r
4801     GetObject(bufferBitmap, sizeof(b), &b);\r
4802     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4803         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4804         bih.biWidth = b.bmWidth;\r
4805         bih.biHeight = b.bmHeight;\r
4806         bih.biPlanes = 1;\r
4807         bih.biBitCount = b.bmBitsPixel;\r
4808         bih.biCompression = 0;\r
4809         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4810         bih.biXPelsPerMeter = 0;\r
4811         bih.biYPelsPerMeter = 0;\r
4812         bih.biClrUsed = 0;\r
4813         bih.biClrImportant = 0;\r
4814 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4815 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4816         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4817 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4818 \r
4819 #if 1\r
4820         wb = b.bmWidthBytes;\r
4821         // count colors\r
4822         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4823                 int k = ((int*) pData)[i];\r
4824                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4825                 if(j >= 16) break;\r
4826                 color[j] = k;\r
4827                 if(j >= nrColors) nrColors = j+1;\r
4828         }\r
4829         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4830                 INT p = 0;\r
4831                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4832                     for(w=0; w<(wb>>2); w+=2) {\r
4833                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4834                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4835                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4836                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4837                         pData[p++] = m | j<<4;\r
4838                     }\r
4839                     while(p&3) pData[p++] = 0;\r
4840                 }\r
4841                 fac = 3;\r
4842                 wb = ((wb+31)>>5)<<2;\r
4843         }\r
4844         // write BITMAPFILEHEADER\r
4845         fprintf(diagFile, "BM");\r
4846         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4847         fputDW(diagFile, 0);\r
4848         fputDW(diagFile, 0x36 + (fac?64:0));\r
4849         // write BITMAPINFOHEADER\r
4850         fputDW(diagFile, 40);\r
4851         fputDW(diagFile, b.bmWidth);\r
4852         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4853         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4854         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4855         fputDW(diagFile, 0);\r
4856         fputDW(diagFile, 0);\r
4857         fputDW(diagFile, 0);\r
4858         fputDW(diagFile, 0);\r
4859         fputDW(diagFile, 0);\r
4860         fputDW(diagFile, 0);\r
4861         // write color table\r
4862         if(fac)\r
4863         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4864         // write bitmap data\r
4865         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4866                 fputc(pData[i], diagFile);\r
4867 #endif\r
4868      }\r
4869   }\r
4870 \r
4871   SelectObject(tmphdc, oldBitmap);\r
4872 \r
4873   /* Massive cleanup */\r
4874   for (x = 0; x < num_clips; x++)\r
4875     DeleteObject(clips[x]);\r
4876 \r
4877   DeleteDC(tmphdc);\r
4878   DeleteObject(bufferBitmap);\r
4879 \r
4880   if (releaseDC) \r
4881     ReleaseDC(hwndMain, hdc);\r
4882   \r
4883   if (lastDrawnFlipView != flipView) {\r
4884     if (flipView)\r
4885       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4886     else\r
4887       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4888   }\r
4889 \r
4890 /*  CopyBoard(lastDrawn, board);*/\r
4891   lastDrawnHighlight = highlightInfo;\r
4892   lastDrawnPremove   = premoveHighlightInfo;\r
4893   lastDrawnFlipView = flipView;\r
4894   lastDrawnValid = 1;\r
4895 }\r
4896 \r
4897 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4898 int\r
4899 SaveDiagram(f)\r
4900      FILE *f;\r
4901 {\r
4902     saveDiagFlag = 1; diagFile = f;\r
4903     HDCDrawPosition(NULL, TRUE, NULL);\r
4904 \r
4905     saveDiagFlag = 0;\r
4906 \r
4907 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4908     \r
4909     fclose(f);\r
4910     return TRUE;\r
4911 }\r
4912 \r
4913 \r
4914 /*---------------------------------------------------------------------------*\\r
4915 | CLIENT PAINT PROCEDURE\r
4916 |   This is the main event-handler for the WM_PAINT message.\r
4917 |\r
4918 \*---------------------------------------------------------------------------*/\r
4919 VOID\r
4920 PaintProc(HWND hwnd)\r
4921 {\r
4922   HDC         hdc;\r
4923   PAINTSTRUCT ps;\r
4924   HFONT       oldFont;\r
4925 \r
4926   if((hdc = BeginPaint(hwnd, &ps))) {\r
4927     if (IsIconic(hwnd)) {\r
4928       DrawIcon(hdc, 2, 2, iconCurrent);\r
4929     } else {\r
4930       if (!appData.monoMode) {\r
4931         SelectPalette(hdc, hPal, FALSE);\r
4932         RealizePalette(hdc);\r
4933       }\r
4934       HDCDrawPosition(hdc, 1, NULL);\r
4935       oldFont =\r
4936         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4937       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4938                  ETO_CLIPPED|ETO_OPAQUE,\r
4939                  &messageRect, messageText, strlen(messageText), NULL);\r
4940       SelectObject(hdc, oldFont);\r
4941       DisplayBothClocks();\r
4942     }\r
4943     EndPaint(hwnd,&ps);\r
4944   }\r
4945 \r
4946   return;\r
4947 }\r
4948 \r
4949 \r
4950 /*\r
4951  * If the user selects on a border boundary, return -1; if off the board,\r
4952  *   return -2.  Otherwise map the event coordinate to the square.\r
4953  * The offset boardRect.left or boardRect.top must already have been\r
4954  *   subtracted from x.\r
4955  */\r
4956 int\r
4957 EventToSquare(int x)\r
4958 {\r
4959   if (x <= 0)\r
4960     return -2;\r
4961   if (x < lineGap)\r
4962     return -1;\r
4963   x -= lineGap;\r
4964   if ((x % (squareSize + lineGap)) >= squareSize)\r
4965     return -1;\r
4966   x /= (squareSize + lineGap);\r
4967   if (x >= BOARD_SIZE)\r
4968     return -2;\r
4969   return x;\r
4970 }\r
4971 \r
4972 typedef struct {\r
4973   char piece;\r
4974   int command;\r
4975   char* name;\r
4976 } DropEnable;\r
4977 \r
4978 DropEnable dropEnables[] = {\r
4979   { 'P', DP_Pawn, "Pawn" },\r
4980   { 'N', DP_Knight, "Knight" },\r
4981   { 'B', DP_Bishop, "Bishop" },\r
4982   { 'R', DP_Rook, "Rook" },\r
4983   { 'Q', DP_Queen, "Queen" },\r
4984 };\r
4985 \r
4986 VOID\r
4987 SetupDropMenu(HMENU hmenu)\r
4988 {\r
4989   int i, count, enable;\r
4990   char *p;\r
4991   extern char white_holding[], black_holding[];\r
4992   char item[MSG_SIZ];\r
4993 \r
4994   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4995     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4996                dropEnables[i].piece);\r
4997     count = 0;\r
4998     while (p && *p++ == dropEnables[i].piece) count++;\r
4999     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
5000     enable = count > 0 || !appData.testLegality\r
5001       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
5002                       && !appData.icsActive);\r
5003     ModifyMenu(hmenu, dropEnables[i].command,\r
5004                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
5005                dropEnables[i].command, item);\r
5006   }\r
5007 }\r
5008 \r
5009 /* Event handler for mouse messages */\r
5010 VOID\r
5011 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5012 {\r
5013   int x, y;\r
5014   POINT pt;\r
5015   static int recursive = 0;\r
5016   HMENU hmenu;\r
5017 //  BOOLEAN needsRedraw = FALSE;\r
5018   BOOLEAN saveAnimate;\r
5019   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
5020   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
5021   ChessMove moveType;\r
5022 \r
5023   if (recursive) {\r
5024     if (message == WM_MBUTTONUP) {\r
5025       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
5026          to the middle button: we simulate pressing the left button too!\r
5027          */\r
5028       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
5029       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
5030     }\r
5031     return;\r
5032   }\r
5033   recursive++;\r
5034   \r
5035   pt.x = LOWORD(lParam);\r
5036   pt.y = HIWORD(lParam);\r
5037   x = EventToSquare(pt.x - boardRect.left);\r
5038   y = EventToSquare(pt.y - boardRect.top);\r
5039   if (!flipView && y >= 0) {\r
5040     y = BOARD_HEIGHT - 1 - y;\r
5041   }\r
5042   if (flipView && x >= 0) {\r
5043     x = BOARD_WIDTH - 1 - x;\r
5044   }\r
5045 \r
5046   switch (message) {\r
5047   case WM_LBUTTONDOWN:\r
5048     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
5049         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
5050         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
5051         if(gameInfo.holdingsWidth && \r
5052                 (WhiteOnMove(currentMove) \r
5053                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
5054                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
5055             // click in right holdings, for determining promotion piece\r
5056             ChessSquare p = boards[currentMove][y][x];\r
5057             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
5058             if(p != EmptySquare) {\r
5059                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
5060                 fromX = fromY = -1;\r
5061                 break;\r
5062             }\r
5063         }\r
5064         DrawPosition(FALSE, boards[currentMove]);\r
5065         break;\r
5066     }\r
5067     ErrorPopDown();\r
5068     sameAgain = FALSE;\r
5069     if (y == -2) {\r
5070       /* Downclick vertically off board; check if on clock */\r
5071       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5072         if (gameMode == EditPosition) {\r
5073           SetWhiteToPlayEvent();\r
5074         } else if (gameMode == IcsPlayingBlack ||\r
5075                    gameMode == MachinePlaysWhite) {\r
5076           CallFlagEvent();\r
5077         } else if (gameMode == EditGame) {\r
5078           AdjustClock(flipClock, -1);\r
5079         }\r
5080       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5081         if (gameMode == EditPosition) {\r
5082           SetBlackToPlayEvent();\r
5083         } else if (gameMode == IcsPlayingWhite ||\r
5084                    gameMode == MachinePlaysBlack) {\r
5085           CallFlagEvent();\r
5086         } else if (gameMode == EditGame) {\r
5087           AdjustClock(!flipClock, -1);\r
5088         }\r
5089       }\r
5090       if (!appData.highlightLastMove) {\r
5091         ClearHighlights();\r
5092         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
5093       }\r
5094       fromX = fromY = -1;\r
5095       dragInfo.start.x = dragInfo.start.y = -1;\r
5096       dragInfo.from = dragInfo.start;\r
5097       break;\r
5098     } else if (x < 0 || y < 0\r
5099       /* [HGM] block clicks between board and holdings */\r
5100               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
5101               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
5102               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
5103         /* EditPosition, empty square, or different color piece;\r
5104            click-click move is possible */\r
5105                                ) {\r
5106       break;\r
5107     } else if (fromX == x && fromY == y) {\r
5108       /* Downclick on same square again */\r
5109       ClearHighlights();\r
5110       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5111       sameAgain = TRUE;  \r
5112     } else if (fromX != -1 &&\r
5113                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5114                                                                         ) {\r
5115       /* Downclick on different square. */\r
5116       /* [HGM] if on holdings file, should count as new first click ! */\r
5117       /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5118         toX = x;\r
5119         toY = y;\r
5120         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5121            to make sure move is legal before showing promotion popup */\r
5122         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5123         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5124                 fromX = fromY = -1; \r
5125                 ClearHighlights();\r
5126                 DrawPosition(FALSE, boards[currentMove]);\r
5127                 break; \r
5128         } else \r
5129         if(moveType != ImpossibleMove) {\r
5130           if(moveType == IllegalMove) {\r
5131                 ;\r
5132           } else\r
5133           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5134           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5135             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5136               appData.alwaysPromoteToQueen)) {\r
5137                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5138                   if (!appData.highlightLastMove) {\r
5139                       ClearHighlights();\r
5140                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5141                   }\r
5142           } else\r
5143           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5144                   SetHighlights(fromX, fromY, toX, toY);\r
5145                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5146                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5147                      If promotion to Q is legal, all are legal! */\r
5148                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5149                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5150                     // kludge to temporarily execute move on display, without promoting yet\r
5151                     promotionChoice = TRUE;\r
5152                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5153                     boards[currentMove][toY][toX] = p;\r
5154                     DrawPosition(FALSE, boards[currentMove]);\r
5155                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5156                     boards[currentMove][toY][toX] = q;\r
5157                   } else\r
5158                   PromotionPopup(hwnd);\r
5159           } else {       /* not a promotion */\r
5160              if (appData.animate || appData.highlightLastMove) {\r
5161                  SetHighlights(fromX, fromY, toX, toY);\r
5162              } else {\r
5163                  ClearHighlights();\r
5164              }\r
5165              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5166              if (appData.animate && !appData.highlightLastMove) {\r
5167                   ClearHighlights();\r
5168                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5169              }\r
5170           }\r
5171           fromX = fromY = -1;\r
5172           break;\r
5173         }\r
5174         if (gotPremove) {\r
5175             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5176             DrawPosition(forceFullRepaint || FALSE, NULL);\r
5177         } else ClearHighlights();\r
5178         fromX = fromY = -1;\r
5179         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5180     }\r
5181     /* First downclick, or restart on a square with same color piece */\r
5182     if (!frozen && OKToStartUserMove(x, y)) {\r
5183       fromX = x;\r
5184       fromY = y;\r
5185       dragInfo.lastpos = pt;\r
5186       dragInfo.from.x = fromX;\r
5187       dragInfo.from.y = fromY;\r
5188       dragInfo.start = dragInfo.from;\r
5189       SetCapture(hwndMain);\r
5190     } else {\r
5191       fromX = fromY = -1;\r
5192       dragInfo.start.x = dragInfo.start.y = -1;\r
5193       dragInfo.from = dragInfo.start;\r
5194       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5195     }\r
5196     break;\r
5197 \r
5198   case WM_LBUTTONUP:\r
5199     ReleaseCapture();\r
5200     if (fromX == -1) break;\r
5201     if (x == fromX && y == fromY) {\r
5202       dragInfo.from.x = dragInfo.from.y = -1;\r
5203       /* Upclick on same square */\r
5204       if (sameAgain) {\r
5205         /* Clicked same square twice: abort click-click move */\r
5206         fromX = fromY = -1;\r
5207         gotPremove = 0;\r
5208         ClearPremoveHighlights();\r
5209       } else {\r
5210         /* First square clicked: start click-click move */\r
5211         SetHighlights(fromX, fromY, -1, -1);\r
5212       }\r
5213       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5214     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5215       /* Errant click; ignore */\r
5216       break;\r
5217     } else {\r
5218       /* Finish drag move. */\r
5219     if (appData.debugMode) {\r
5220         fprintf(debugFP, "release\n");\r
5221     }\r
5222       dragInfo.from.x = dragInfo.from.y = -1;\r
5223       toX = x;\r
5224       toY = y;\r
5225       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5226       appData.animate = appData.animate && !appData.animateDragging;\r
5227       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5228       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5229                 fromX = fromY = -1; \r
5230                 ClearHighlights();\r
5231                 DrawPosition(FALSE, boards[currentMove]);\r
5232                 appData.animate = saveAnimate;\r
5233                 break; \r
5234       } else \r
5235       if(moveType != ImpossibleMove) {\r
5236           /* [HGM] use move type to determine if move is promotion.\r
5237              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5238           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5239             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5240               appData.alwaysPromoteToQueen)) \r
5241                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5242           else \r
5243           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5244                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5245                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5246                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5247                     // kludge to temporarily execute move on display, wthout promotng yet\r
5248                     promotionChoice = TRUE;\r
5249                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5250                     boards[currentMove][toY][toX] = p;\r
5251                     DrawPosition(FALSE, boards[currentMove]);\r
5252                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5253                     boards[currentMove][toY][toX] = q;\r
5254                     appData.animate = saveAnimate;\r
5255                     break;\r
5256                   } else\r
5257                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5258           } else {\r
5259             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5260                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5261                                         moveType == WhiteCapturesEnPassant || \r
5262                                         moveType == BlackCapturesEnPassant   ) )\r
5263                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5264             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5265           }\r
5266       }\r
5267       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5268       appData.animate = saveAnimate;\r
5269       fromX = fromY = -1;\r
5270       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5271         ClearHighlights();\r
5272       }\r
5273       if (appData.animate || appData.animateDragging ||\r
5274           appData.highlightDragging || gotPremove) {\r
5275         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5276       }\r
5277     }\r
5278     dragInfo.start.x = dragInfo.start.y = -1; \r
5279     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5280     break;\r
5281 \r
5282   case WM_MOUSEMOVE:\r
5283     if ((appData.animateDragging || appData.highlightDragging)\r
5284         && (wParam & MK_LBUTTON)\r
5285         && dragInfo.from.x >= 0) \r
5286     {\r
5287       BOOL full_repaint = FALSE;\r
5288 \r
5289       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5290       if (appData.animateDragging) {\r
5291         dragInfo.pos = pt;\r
5292       }\r
5293       if (appData.highlightDragging) {\r
5294         SetHighlights(fromX, fromY, x, y);\r
5295         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5296             full_repaint = TRUE;\r
5297         }\r
5298       }\r
5299       \r
5300       DrawPosition( full_repaint, NULL);\r
5301       \r
5302       dragInfo.lastpos = dragInfo.pos;\r
5303     }\r
5304     break;\r
5305 \r
5306   case WM_MOUSEWHEEL: // [DM]\r
5307     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5308        /* Mouse Wheel is being rolled forward\r
5309         * Play moves forward\r
5310         */\r
5311        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5312                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5313        /* Mouse Wheel is being rolled backward\r
5314         * Play moves backward\r
5315         */\r
5316        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5317                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5318     }\r
5319     break;\r
5320 \r
5321   case WM_MBUTTONDOWN:\r
5322   case WM_RBUTTONDOWN:\r
5323     ErrorPopDown();\r
5324     ReleaseCapture();\r
5325     fromX = fromY = -1;\r
5326     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5327     dragInfo.start.x = dragInfo.start.y = -1;\r
5328     dragInfo.from = dragInfo.start;\r
5329     dragInfo.lastpos = dragInfo.pos;\r
5330     if (appData.highlightDragging) {\r
5331       ClearHighlights();\r
5332     }\r
5333     if(y == -2) {\r
5334       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5335       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5336           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5337       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5338           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5339       }\r
5340     }\r
5341     DrawPosition(TRUE, NULL);\r
5342 \r
5343     switch (gameMode) {\r
5344     case EditPosition:\r
5345     case IcsExamining:\r
5346       if (x < 0 || y < 0) break;\r
5347       fromX = x;\r
5348       fromY = y;\r
5349       if (message == WM_MBUTTONDOWN) {\r
5350         buttonCount = 3;  /* even if system didn't think so */\r
5351         if (wParam & MK_SHIFT) \r
5352           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5353         else\r
5354           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5355       } else { /* message == WM_RBUTTONDOWN */\r
5356 #if 0\r
5357         if (buttonCount == 3) {\r
5358           if (wParam & MK_SHIFT) \r
5359             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5360           else\r
5361             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5362         } else {\r
5363           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5364         }\r
5365 #else\r
5366         /* Just have one menu, on the right button.  Windows users don't\r
5367            think to try the middle one, and sometimes other software steals\r
5368            it, or it doesn't really exist. */\r
5369         if(gameInfo.variant != VariantShogi)\r
5370             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5371         else\r
5372             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5373 #endif\r
5374       }\r
5375       break;\r
5376     case IcsPlayingWhite:\r
5377     case IcsPlayingBlack:\r
5378     case EditGame:\r
5379     case MachinePlaysWhite:\r
5380     case MachinePlaysBlack:\r
5381       if (appData.testLegality &&\r
5382           gameInfo.variant != VariantBughouse &&\r
5383           gameInfo.variant != VariantCrazyhouse) break;\r
5384       if (x < 0 || y < 0) break;\r
5385       fromX = x;\r
5386       fromY = y;\r
5387       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5388       SetupDropMenu(hmenu);\r
5389       MenuPopup(hwnd, pt, hmenu, -1);\r
5390       break;\r
5391     default:\r
5392       break;\r
5393     }\r
5394     break;\r
5395   }\r
5396 \r
5397   recursive--;\r
5398 }\r
5399 \r
5400 /* Preprocess messages for buttons in main window */\r
5401 LRESULT CALLBACK\r
5402 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5403 {\r
5404   int id = GetWindowLong(hwnd, GWL_ID);\r
5405   int i, dir;\r
5406 \r
5407   for (i=0; i<N_BUTTONS; i++) {\r
5408     if (buttonDesc[i].id == id) break;\r
5409   }\r
5410   if (i == N_BUTTONS) return 0;\r
5411   switch (message) {\r
5412   case WM_KEYDOWN:\r
5413     switch (wParam) {\r
5414     case VK_LEFT:\r
5415     case VK_RIGHT:\r
5416       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5417       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5418       return TRUE;\r
5419     }\r
5420     break;\r
5421   case WM_CHAR:\r
5422     switch (wParam) {\r
5423     case '\r':\r
5424       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5425       return TRUE;\r
5426     default:\r
5427       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5428         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5429         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5430         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5431         SetFocus(h);\r
5432         SendMessage(h, WM_CHAR, wParam, lParam);\r
5433         return TRUE;\r
5434       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5435         PopUpMoveDialog((char)wParam);\r
5436       }\r
5437       break;\r
5438     }\r
5439     break;\r
5440   }\r
5441   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5442 }\r
5443 \r
5444 /* Process messages for Promotion dialog box */\r
5445 LRESULT CALLBACK\r
5446 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5447 {\r
5448   char promoChar;\r
5449 \r
5450   switch (message) {\r
5451   case WM_INITDIALOG: /* message: initialize dialog box */\r
5452     /* Center the dialog over the application window */\r
5453     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5454     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5455       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5456        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5457                SW_SHOW : SW_HIDE);\r
5458     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5459     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5460        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5461          PieceToChar(WhiteAngel) != '~') ||\r
5462         (PieceToChar(BlackAngel) >= 'A' &&\r
5463          PieceToChar(BlackAngel) != '~')   ) ?\r
5464                SW_SHOW : SW_HIDE);\r
5465     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5466        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5467          PieceToChar(WhiteMarshall) != '~') ||\r
5468         (PieceToChar(BlackMarshall) >= 'A' &&\r
5469          PieceToChar(BlackMarshall) != '~')   ) ?\r
5470                SW_SHOW : SW_HIDE);\r
5471     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5472     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5473        gameInfo.variant != VariantShogi ?\r
5474                SW_SHOW : SW_HIDE);\r
5475     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5476        gameInfo.variant != VariantShogi ?\r
5477                SW_SHOW : SW_HIDE);\r
5478     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5479        gameInfo.variant == VariantShogi ?\r
5480                SW_SHOW : SW_HIDE);\r
5481     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5482        gameInfo.variant == VariantShogi ?\r
5483                SW_SHOW : SW_HIDE);\r
5484     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5485        gameInfo.variant == VariantSuper ?\r
5486                SW_SHOW : SW_HIDE);\r
5487     return TRUE;\r
5488 \r
5489   case WM_COMMAND: /* message: received a command */\r
5490     switch (LOWORD(wParam)) {\r
5491     case IDCANCEL:\r
5492       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5493       ClearHighlights();\r
5494       DrawPosition(FALSE, NULL);\r
5495       return TRUE;\r
5496     case PB_King:\r
5497       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5498       break;\r
5499     case PB_Queen:\r
5500       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5501       break;\r
5502     case PB_Rook:\r
5503       promoChar = PieceToChar(BlackRook);\r
5504       break;\r
5505     case PB_Bishop:\r
5506       promoChar = PieceToChar(BlackBishop);\r
5507       break;\r
5508     case PB_Chancellor:\r
5509       promoChar = PieceToChar(BlackMarshall);\r
5510       break;\r
5511     case PB_Archbishop:\r
5512       promoChar = PieceToChar(BlackAngel);\r
5513       break;\r
5514     case PB_Knight:\r
5515       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5516       break;\r
5517     default:\r
5518       return FALSE;\r
5519     }\r
5520     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5521     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5522        only show the popup when we are already sure the move is valid or\r
5523        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5524        will figure out it is a promotion from the promoChar. */\r
5525     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5526     if (!appData.highlightLastMove) {\r
5527       ClearHighlights();\r
5528       DrawPosition(FALSE, NULL);\r
5529     }\r
5530     return TRUE;\r
5531   }\r
5532   return FALSE;\r
5533 }\r
5534 \r
5535 /* Pop up promotion dialog */\r
5536 VOID\r
5537 PromotionPopup(HWND hwnd)\r
5538 {\r
5539   FARPROC lpProc;\r
5540 \r
5541   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5542   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5543     hwnd, (DLGPROC)lpProc);\r
5544   FreeProcInstance(lpProc);\r
5545 }\r
5546 \r
5547 /* Toggle ShowThinking */\r
5548 VOID\r
5549 ToggleShowThinking()\r
5550 {\r
5551   appData.showThinking = !appData.showThinking;\r
5552   ShowThinkingEvent();\r
5553 }\r
5554 \r
5555 VOID\r
5556 LoadGameDialog(HWND hwnd, char* title)\r
5557 {\r
5558   UINT number = 0;\r
5559   FILE *f;\r
5560   char fileTitle[MSG_SIZ];\r
5561   f = OpenFileDialog(hwnd, "rb", "",\r
5562                      appData.oldSaveStyle ? "gam" : "pgn",\r
5563                      GAME_FILT,\r
5564                      title, &number, fileTitle, NULL);\r
5565   if (f != NULL) {\r
5566     cmailMsgLoaded = FALSE;\r
5567     if (number == 0) {\r
5568       int error = GameListBuild(f);\r
5569       if (error) {\r
5570         DisplayError("Cannot build game list", error);\r
5571       } else if (!ListEmpty(&gameList) &&\r
5572                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5573         GameListPopUp(f, fileTitle);\r
5574         return;\r
5575       }\r
5576       GameListDestroy();\r
5577       number = 1;\r
5578     }\r
5579     LoadGame(f, number, fileTitle, FALSE);\r
5580   }\r
5581 }\r
5582 \r
5583 VOID\r
5584 ChangedConsoleFont()\r
5585 {\r
5586   CHARFORMAT cfmt;\r
5587   CHARRANGE tmpsel, sel;\r
5588   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5589   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5590   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5591   PARAFORMAT paraf;\r
5592 \r
5593   cfmt.cbSize = sizeof(CHARFORMAT);\r
5594   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5595   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5596   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5597    * size.  This was undocumented in the version of MSVC++ that I had\r
5598    * when I wrote the code, but is apparently documented now.\r
5599    */\r
5600   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5601   cfmt.bCharSet = f->lf.lfCharSet;\r
5602   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5603   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5604   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5605   /* Why are the following seemingly needed too? */\r
5606   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5607   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5608   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5609   tmpsel.cpMin = 0;\r
5610   tmpsel.cpMax = -1; /*999999?*/\r
5611   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5612   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5613   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5614    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5615    */\r
5616   paraf.cbSize = sizeof(paraf);\r
5617   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5618   paraf.dxStartIndent = 0;\r
5619   paraf.dxOffset = WRAP_INDENT;\r
5620   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5621   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5622 }\r
5623 \r
5624 /*---------------------------------------------------------------------------*\\r
5625  *\r
5626  * Window Proc for main window\r
5627  *\r
5628 \*---------------------------------------------------------------------------*/\r
5629 \r
5630 /* Process messages for main window, etc. */\r
5631 LRESULT CALLBACK\r
5632 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5633 {\r
5634   FARPROC lpProc;\r
5635   int wmId, wmEvent;\r
5636   char *defName;\r
5637   FILE *f;\r
5638   UINT number;\r
5639   char fileTitle[MSG_SIZ];\r
5640   char buf[MSG_SIZ];\r
5641   static SnapData sd;\r
5642 \r
5643   switch (message) {\r
5644 \r
5645   case WM_PAINT: /* message: repaint portion of window */\r
5646     PaintProc(hwnd);\r
5647     break;\r
5648 \r
5649   case WM_ERASEBKGND:\r
5650     if (IsIconic(hwnd)) {\r
5651       /* Cheat; change the message */\r
5652       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5653     } else {\r
5654       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5655     }\r
5656     break;\r
5657 \r
5658   case WM_LBUTTONDOWN:\r
5659   case WM_MBUTTONDOWN:\r
5660   case WM_RBUTTONDOWN:\r
5661   case WM_LBUTTONUP:\r
5662   case WM_MBUTTONUP:\r
5663   case WM_RBUTTONUP:\r
5664   case WM_MOUSEMOVE:\r
5665   case WM_MOUSEWHEEL:\r
5666     MouseEvent(hwnd, message, wParam, lParam);\r
5667     break;\r
5668 \r
5669   JAWS_KB_NAVIGATION\r
5670 \r
5671   case WM_CHAR:\r
5672     \r
5673     JAWS_ALT_INTERCEPT\r
5674 \r
5675     if (appData.icsActive && (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9')) { \r
5676         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5677         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5678         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5679         SetFocus(h);\r
5680         SendMessage(h, message, wParam, lParam);\r
5681     } else if(lParam != KF_REPEAT) {\r
5682         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5683                 PopUpMoveDialog((char)wParam);\r
5684         } else if((char)wParam == 003) CopyGameToClipboard();\r
5685          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5686     }\r
5687 \r
5688     break;\r
5689 \r
5690   case WM_PALETTECHANGED:\r
5691     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5692       int nnew;\r
5693       HDC hdc = GetDC(hwndMain);\r
5694       SelectPalette(hdc, hPal, TRUE);\r
5695       nnew = RealizePalette(hdc);\r
5696       if (nnew > 0) {\r
5697         paletteChanged = TRUE;\r
5698 #if 0\r
5699         UpdateColors(hdc);\r
5700 #else\r
5701         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5702 #endif\r
5703       }\r
5704       ReleaseDC(hwnd, hdc);\r
5705     }\r
5706     break;\r
5707 \r
5708   case WM_QUERYNEWPALETTE:\r
5709     if (!appData.monoMode /*&& paletteChanged*/) {\r
5710       int nnew;\r
5711       HDC hdc = GetDC(hwndMain);\r
5712       paletteChanged = FALSE;\r
5713       SelectPalette(hdc, hPal, FALSE);\r
5714       nnew = RealizePalette(hdc);\r
5715       if (nnew > 0) {\r
5716         InvalidateRect(hwnd, &boardRect, FALSE);\r
5717       }\r
5718       ReleaseDC(hwnd, hdc);\r
5719       return TRUE;\r
5720     }\r
5721     return FALSE;\r
5722 \r
5723   case WM_COMMAND: /* message: command from application menu */\r
5724     wmId    = LOWORD(wParam);\r
5725     wmEvent = HIWORD(wParam);\r
5726 \r
5727     switch (wmId) {\r
5728     case IDM_NewGame:\r
5729       ResetGameEvent();\r
5730       AnalysisPopDown();\r
5731       SAY("new game enter a move to play against the computer with white");\r
5732       break;\r
5733 \r
5734     case IDM_NewGameFRC:\r
5735       if( NewGameFRC() == 0 ) {\r
5736         ResetGameEvent();\r
5737         AnalysisPopDown();\r
5738       }\r
5739       break;\r
5740 \r
5741     case IDM_NewVariant:\r
5742       NewVariantPopup(hwnd);\r
5743       break;\r
5744 \r
5745     case IDM_LoadGame:\r
5746       LoadGameDialog(hwnd, "Load Game from File");\r
5747       break;\r
5748 \r
5749     case IDM_LoadNextGame:\r
5750       ReloadGame(1);\r
5751       break;\r
5752 \r
5753     case IDM_LoadPrevGame:\r
5754       ReloadGame(-1);\r
5755       break;\r
5756 \r
5757     case IDM_ReloadGame:\r
5758       ReloadGame(0);\r
5759       break;\r
5760 \r
5761     case IDM_LoadPosition:\r
5762       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5763         Reset(FALSE, TRUE);\r
5764       }\r
5765       number = 1;\r
5766       f = OpenFileDialog(hwnd, "rb", "",\r
5767                          appData.oldSaveStyle ? "pos" : "fen",\r
5768                          POSITION_FILT,\r
5769                          "Load Position from File", &number, fileTitle, NULL);\r
5770       if (f != NULL) {\r
5771         LoadPosition(f, number, fileTitle);\r
5772       }\r
5773       break;\r
5774 \r
5775     case IDM_LoadNextPosition:\r
5776       ReloadPosition(1);\r
5777       break;\r
5778 \r
5779     case IDM_LoadPrevPosition:\r
5780       ReloadPosition(-1);\r
5781       break;\r
5782 \r
5783     case IDM_ReloadPosition:\r
5784       ReloadPosition(0);\r
5785       break;\r
5786 \r
5787     case IDM_SaveGame:\r
5788       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5789       f = OpenFileDialog(hwnd, "a", defName,\r
5790                          appData.oldSaveStyle ? "gam" : "pgn",\r
5791                          GAME_FILT,\r
5792                          "Save Game to File", NULL, fileTitle, NULL);\r
5793       if (f != NULL) {\r
5794         SaveGame(f, 0, "");\r
5795       }\r
5796       break;\r
5797 \r
5798     case IDM_SavePosition:\r
5799       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5800       f = OpenFileDialog(hwnd, "a", defName,\r
5801                          appData.oldSaveStyle ? "pos" : "fen",\r
5802                          POSITION_FILT,\r
5803                          "Save Position to File", NULL, fileTitle, NULL);\r
5804       if (f != NULL) {\r
5805         SavePosition(f, 0, "");\r
5806       }\r
5807       break;\r
5808 \r
5809     case IDM_SaveDiagram:\r
5810       defName = "diagram";\r
5811       f = OpenFileDialog(hwnd, "wb", defName,\r
5812                          "bmp",\r
5813                          DIAGRAM_FILT,\r
5814                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5815       if (f != NULL) {\r
5816         SaveDiagram(f);\r
5817       }\r
5818       break;\r
5819 \r
5820     case IDM_CopyGame:\r
5821       CopyGameToClipboard();\r
5822       break;\r
5823 \r
5824     case IDM_PasteGame:\r
5825       PasteGameFromClipboard();\r
5826       break;\r
5827 \r
5828     case IDM_CopyGameListToClipboard:\r
5829       CopyGameListToClipboard();\r
5830       break;\r
5831 \r
5832     /* [AS] Autodetect FEN or PGN data */\r
5833     case IDM_PasteAny:\r
5834       PasteGameOrFENFromClipboard();\r
5835       break;\r
5836 \r
5837     /* [AS] Move history */\r
5838     case IDM_ShowMoveHistory:\r
5839         if( MoveHistoryIsUp() ) {\r
5840             MoveHistoryPopDown();\r
5841         }\r
5842         else {\r
5843             MoveHistoryPopUp();\r
5844         }\r
5845         break;\r
5846 \r
5847     /* [AS] Eval graph */\r
5848     case IDM_ShowEvalGraph:\r
5849         if( EvalGraphIsUp() ) {\r
5850             EvalGraphPopDown();\r
5851         }\r
5852         else {\r
5853             EvalGraphPopUp();\r
5854             SetFocus(hwndMain);\r
5855         }\r
5856         break;\r
5857 \r
5858     /* [AS] Engine output */\r
5859     case IDM_ShowEngineOutput:\r
5860         if( EngineOutputIsUp() ) {\r
5861             EngineOutputPopDown();\r
5862         }\r
5863         else {\r
5864             EngineOutputPopUp();\r
5865         }\r
5866         break;\r
5867 \r
5868     /* [AS] User adjudication */\r
5869     case IDM_UserAdjudication_White:\r
5870         UserAdjudicationEvent( +1 );\r
5871         break;\r
5872 \r
5873     case IDM_UserAdjudication_Black:\r
5874         UserAdjudicationEvent( -1 );\r
5875         break;\r
5876 \r
5877     case IDM_UserAdjudication_Draw:\r
5878         UserAdjudicationEvent( 0 );\r
5879         break;\r
5880 \r
5881     /* [AS] Game list options dialog */\r
5882     case IDM_GameListOptions:\r
5883       GameListOptions();\r
5884       break;\r
5885 \r
5886     case IDM_NewChat:\r
5887       ChatPopUp();\r
5888       break;\r
5889 \r
5890     case IDM_CopyPosition:\r
5891       CopyFENToClipboard();\r
5892       break;\r
5893 \r
5894     case IDM_PastePosition:\r
5895       PasteFENFromClipboard();\r
5896       break;\r
5897 \r
5898     case IDM_MailMove:\r
5899       MailMoveEvent();\r
5900       break;\r
5901 \r
5902     case IDM_ReloadCMailMsg:\r
5903       Reset(TRUE, TRUE);\r
5904       ReloadCmailMsgEvent(FALSE);\r
5905       break;\r
5906 \r
5907     case IDM_Minimize:\r
5908       ShowWindow(hwnd, SW_MINIMIZE);\r
5909       break;\r
5910 \r
5911     case IDM_Exit:\r
5912       ExitEvent(0);\r
5913       break;\r
5914 \r
5915     case IDM_MachineWhite:\r
5916       MachineWhiteEvent();\r
5917       /*\r
5918        * refresh the tags dialog only if it's visible\r
5919        */\r
5920       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5921           char *tags;\r
5922           tags = PGNTags(&gameInfo);\r
5923           TagsPopUp(tags, CmailMsg());\r
5924           free(tags);\r
5925       }\r
5926       SAY("computer starts playing white");\r
5927       break;\r
5928 \r
5929     case IDM_MachineBlack:\r
5930       MachineBlackEvent();\r
5931       /*\r
5932        * refresh the tags dialog only if it's visible\r
5933        */\r
5934       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5935           char *tags;\r
5936           tags = PGNTags(&gameInfo);\r
5937           TagsPopUp(tags, CmailMsg());\r
5938           free(tags);\r
5939       }\r
5940       SAY("computer starts playing black");\r
5941       break;\r
5942 \r
5943     case IDM_TwoMachines:\r
5944       TwoMachinesEvent();\r
5945       /*\r
5946        * refresh the tags dialog only if it's visible\r
5947        */\r
5948       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5949           char *tags;\r
5950           tags = PGNTags(&gameInfo);\r
5951           TagsPopUp(tags, CmailMsg());\r
5952           free(tags);\r
5953       }\r
5954       SAY("programs start playing each other");\r
5955       break;\r
5956 \r
5957     case IDM_AnalysisMode:\r
5958       if (!first.analysisSupport) {\r
5959         sprintf(buf, "%s does not support analysis", first.tidy);\r
5960         DisplayError(buf, 0);\r
5961       } else {\r
5962         SAY("analyzing current position");\r
5963         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5964         if (appData.icsActive) {\r
5965                if (gameMode != IcsObserving) {\r
5966                        sprintf(buf, "You are not observing a game");\r
5967                        DisplayError(buf, 0);\r
5968                        /* secure check */\r
5969                        if (appData.icsEngineAnalyze) {\r
5970                                if (appData.debugMode) \r
5971                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5972                                ExitAnalyzeMode();\r
5973                                ModeHighlight();\r
5974                                break;\r
5975                        }\r
5976                        break;\r
5977                } else {\r
5978                        /* if enable, user want disable icsEngineAnalyze */\r
5979                        if (appData.icsEngineAnalyze) {\r
5980                                ExitAnalyzeMode();\r
5981                                ModeHighlight();\r
5982                                break;\r
5983                        }\r
5984                        appData.icsEngineAnalyze = TRUE;\r
5985                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5986                }\r
5987         } \r
5988         if (!appData.showThinking) ToggleShowThinking();\r
5989         AnalyzeModeEvent();\r
5990       }\r
5991       break;\r
5992 \r
5993     case IDM_AnalyzeFile:\r
5994       if (!first.analysisSupport) {\r
5995         char buf[MSG_SIZ];\r
5996         sprintf(buf, "%s does not support analysis", first.tidy);\r
5997         DisplayError(buf, 0);\r
5998       } else {\r
5999         if (!appData.showThinking) ToggleShowThinking();\r
6000         AnalyzeFileEvent();\r
6001         LoadGameDialog(hwnd, "Analyze Game from File");\r
6002         AnalysisPeriodicEvent(1);\r
6003       }\r
6004       break;\r
6005 \r
6006     case IDM_IcsClient:\r
6007       IcsClientEvent();\r
6008       break;\r
6009 \r
6010     case IDM_EditGame:\r
6011       EditGameEvent();\r
6012       SAY("edit game");\r
6013       break;\r
6014 \r
6015     case IDM_EditPosition:\r
6016       EditPositionEvent();\r
6017       SAY("to set up a position type a FEN");\r
6018       break;\r
6019 \r
6020     case IDM_Training:\r
6021       TrainingEvent();\r
6022       break;\r
6023 \r
6024     case IDM_ShowGameList:\r
6025       ShowGameListProc();\r
6026       break;\r
6027 \r
6028     case IDM_EditTags:\r
6029       EditTagsProc();\r
6030       break;\r
6031 \r
6032     case IDM_EditComment:\r
6033       if (commentDialogUp && editComment) {\r
6034         CommentPopDown();\r
6035       } else {\r
6036         EditCommentEvent();\r
6037       }\r
6038       break;\r
6039 \r
6040     case IDM_Pause:\r
6041       PauseEvent();\r
6042       break;\r
6043 \r
6044     case IDM_Accept:\r
6045       AcceptEvent();\r
6046       break;\r
6047 \r
6048     case IDM_Decline:\r
6049       DeclineEvent();\r
6050       break;\r
6051 \r
6052     case IDM_Rematch:\r
6053       RematchEvent();\r
6054       break;\r
6055 \r
6056     case IDM_CallFlag:\r
6057       CallFlagEvent();\r
6058       break;\r
6059 \r
6060     case IDM_Draw:\r
6061       DrawEvent();\r
6062       break;\r
6063 \r
6064     case IDM_Adjourn:\r
6065       AdjournEvent();\r
6066       break;\r
6067 \r
6068     case IDM_Abort:\r
6069       AbortEvent();\r
6070       break;\r
6071 \r
6072     case IDM_Resign:\r
6073       ResignEvent();\r
6074       break;\r
6075 \r
6076     case IDM_StopObserving:\r
6077       StopObservingEvent();\r
6078       break;\r
6079 \r
6080     case IDM_StopExamining:\r
6081       StopExaminingEvent();\r
6082       break;\r
6083 \r
6084     case IDM_TypeInMove:\r
6085       PopUpMoveDialog('\000');\r
6086       break;\r
6087 \r
6088     case IDM_TypeInName:\r
6089       PopUpNameDialog('\000');\r
6090       break;\r
6091 \r
6092     case IDM_Backward:\r
6093       BackwardEvent();\r
6094       SetFocus(hwndMain);\r
6095       break;\r
6096 \r
6097     JAWS_MENU_ITEMS\r
6098 \r
6099     case IDM_Forward:\r
6100       ForwardEvent();\r
6101       SetFocus(hwndMain);\r
6102       break;\r
6103 \r
6104     case IDM_ToStart:\r
6105       ToStartEvent();\r
6106       SetFocus(hwndMain);\r
6107       break;\r
6108 \r
6109     case IDM_ToEnd:\r
6110       ToEndEvent();\r
6111       SetFocus(hwndMain);\r
6112       break;\r
6113 \r
6114     case IDM_Revert:\r
6115       RevertEvent();\r
6116       break;\r
6117 \r
6118     case IDM_TruncateGame:\r
6119       TruncateGameEvent();\r
6120       break;\r
6121 \r
6122     case IDM_MoveNow:\r
6123       MoveNowEvent();\r
6124       break;\r
6125 \r
6126     case IDM_RetractMove:\r
6127       RetractMoveEvent();\r
6128       break;\r
6129 \r
6130     case IDM_FlipView:\r
6131       flipView = !flipView;\r
6132       DrawPosition(FALSE, NULL);\r
6133       break;\r
6134 \r
6135     case IDM_FlipClock:\r
6136       flipClock = !flipClock;\r
6137       DisplayBothClocks();\r
6138       DrawPosition(FALSE, NULL);\r
6139       break;\r
6140 \r
6141     case IDM_MuteSounds:\r
6142       mute = !mute; // [HGM] mute: keep track of global muting variable\r
6143       CheckMenuItem(GetMenu(hwndMain),IDM_MuteSounds, \r
6144                                 MF_BYCOMMAND|(mute?MF_CHECKED:MF_UNCHECKED));\r
6145       break;\r
6146 \r
6147     case IDM_GeneralOptions:\r
6148       GeneralOptionsPopup(hwnd);\r
6149       DrawPosition(TRUE, NULL);\r
6150       break;\r
6151 \r
6152     case IDM_BoardOptions:\r
6153       BoardOptionsPopup(hwnd);\r
6154       break;\r
6155 \r
6156     case IDM_EnginePlayOptions:\r
6157       EnginePlayOptionsPopup(hwnd);\r
6158       break;\r
6159 \r
6160     case IDM_Engine1Options:\r
6161       EngineOptionsPopup(hwnd, &first);\r
6162       break;\r
6163 \r
6164     case IDM_Engine2Options:\r
6165       EngineOptionsPopup(hwnd, &second);\r
6166       break;\r
6167 \r
6168     case IDM_OptionsUCI:\r
6169       UciOptionsPopup(hwnd);\r
6170       break;\r
6171 \r
6172     case IDM_IcsOptions:\r
6173       IcsOptionsPopup(hwnd);\r
6174       break;\r
6175 \r
6176     case IDM_Fonts:\r
6177       FontsOptionsPopup(hwnd);\r
6178       break;\r
6179 \r
6180     case IDM_Sounds:\r
6181       SoundOptionsPopup(hwnd);\r
6182       break;\r
6183 \r
6184     case IDM_CommPort:\r
6185       CommPortOptionsPopup(hwnd);\r
6186       break;\r
6187 \r
6188     case IDM_LoadOptions:\r
6189       LoadOptionsPopup(hwnd);\r
6190       break;\r
6191 \r
6192     case IDM_SaveOptions:\r
6193       SaveOptionsPopup(hwnd);\r
6194       break;\r
6195 \r
6196     case IDM_TimeControl:\r
6197       TimeControlOptionsPopup(hwnd);\r
6198       break;\r
6199 \r
6200     case IDM_SaveSettings:\r
6201       SaveSettings(settingsFileName);\r
6202       break;\r
6203 \r
6204     case IDM_SaveSettingsOnExit:\r
6205       saveSettingsOnExit = !saveSettingsOnExit;\r
6206       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6207                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6208                                          MF_CHECKED : MF_UNCHECKED));\r
6209       break;\r
6210 \r
6211     case IDM_Hint:\r
6212       HintEvent();\r
6213       break;\r
6214 \r
6215     case IDM_Book:\r
6216       BookEvent();\r
6217       break;\r
6218 \r
6219     case IDM_AboutGame:\r
6220       AboutGameEvent();\r
6221       break;\r
6222 \r
6223     case IDM_Debug:\r
6224       appData.debugMode = !appData.debugMode;\r
6225       if (appData.debugMode) {\r
6226         char dir[MSG_SIZ];\r
6227         GetCurrentDirectory(MSG_SIZ, dir);\r
6228         SetCurrentDirectory(installDir);\r
6229         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6230         SetCurrentDirectory(dir);\r
6231         setbuf(debugFP, NULL);\r
6232       } else {\r
6233         fclose(debugFP);\r
6234         debugFP = NULL;\r
6235       }\r
6236       break;\r
6237 \r
6238     case IDM_HELPCONTENTS:\r
6239       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6240           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6241           MessageBox (GetFocus(),\r
6242                     "Unable to activate help",\r
6243                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6244       }\r
6245       break;\r
6246 \r
6247     case IDM_HELPSEARCH:\r
6248         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6249             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6250         MessageBox (GetFocus(),\r
6251                     "Unable to activate help",\r
6252                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6253       }\r
6254       break;\r
6255 \r
6256     case IDM_HELPHELP:\r
6257       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6258         MessageBox (GetFocus(),\r
6259                     "Unable to activate help",\r
6260                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6261       }\r
6262       break;\r
6263 \r
6264     case IDM_ABOUT:\r
6265       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6266       DialogBox(hInst, \r
6267         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6268         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6269       FreeProcInstance(lpProc);\r
6270       break;\r
6271 \r
6272     case IDM_DirectCommand1:\r
6273       AskQuestionEvent("Direct Command",\r
6274                        "Send to chess program:", "", "1");\r
6275       break;\r
6276     case IDM_DirectCommand2:\r
6277       AskQuestionEvent("Direct Command",\r
6278                        "Send to second chess program:", "", "2");\r
6279       break;\r
6280 \r
6281     case EP_WhitePawn:\r
6282       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6283       fromX = fromY = -1;\r
6284       break;\r
6285 \r
6286     case EP_WhiteKnight:\r
6287       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6288       fromX = fromY = -1;\r
6289       break;\r
6290 \r
6291     case EP_WhiteBishop:\r
6292       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6293       fromX = fromY = -1;\r
6294       break;\r
6295 \r
6296     case EP_WhiteRook:\r
6297       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6298       fromX = fromY = -1;\r
6299       break;\r
6300 \r
6301     case EP_WhiteQueen:\r
6302       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6303       fromX = fromY = -1;\r
6304       break;\r
6305 \r
6306     case EP_WhiteFerz:\r
6307       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6308       fromX = fromY = -1;\r
6309       break;\r
6310 \r
6311     case EP_WhiteWazir:\r
6312       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6313       fromX = fromY = -1;\r
6314       break;\r
6315 \r
6316     case EP_WhiteAlfil:\r
6317       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6318       fromX = fromY = -1;\r
6319       break;\r
6320 \r
6321     case EP_WhiteCannon:\r
6322       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6323       fromX = fromY = -1;\r
6324       break;\r
6325 \r
6326     case EP_WhiteCardinal:\r
6327       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6328       fromX = fromY = -1;\r
6329       break;\r
6330 \r
6331     case EP_WhiteMarshall:\r
6332       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6333       fromX = fromY = -1;\r
6334       break;\r
6335 \r
6336     case EP_WhiteKing:\r
6337       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6338       fromX = fromY = -1;\r
6339       break;\r
6340 \r
6341     case EP_BlackPawn:\r
6342       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6343       fromX = fromY = -1;\r
6344       break;\r
6345 \r
6346     case EP_BlackKnight:\r
6347       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6348       fromX = fromY = -1;\r
6349       break;\r
6350 \r
6351     case EP_BlackBishop:\r
6352       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6353       fromX = fromY = -1;\r
6354       break;\r
6355 \r
6356     case EP_BlackRook:\r
6357       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6358       fromX = fromY = -1;\r
6359       break;\r
6360 \r
6361     case EP_BlackQueen:\r
6362       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6363       fromX = fromY = -1;\r
6364       break;\r
6365 \r
6366     case EP_BlackFerz:\r
6367       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6368       fromX = fromY = -1;\r
6369       break;\r
6370 \r
6371     case EP_BlackWazir:\r
6372       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6373       fromX = fromY = -1;\r
6374       break;\r
6375 \r
6376     case EP_BlackAlfil:\r
6377       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6378       fromX = fromY = -1;\r
6379       break;\r
6380 \r
6381     case EP_BlackCannon:\r
6382       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6383       fromX = fromY = -1;\r
6384       break;\r
6385 \r
6386     case EP_BlackCardinal:\r
6387       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6388       fromX = fromY = -1;\r
6389       break;\r
6390 \r
6391     case EP_BlackMarshall:\r
6392       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6393       fromX = fromY = -1;\r
6394       break;\r
6395 \r
6396     case EP_BlackKing:\r
6397       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6398       fromX = fromY = -1;\r
6399       break;\r
6400 \r
6401     case EP_EmptySquare:\r
6402       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6403       fromX = fromY = -1;\r
6404       break;\r
6405 \r
6406     case EP_ClearBoard:\r
6407       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6408       fromX = fromY = -1;\r
6409       break;\r
6410 \r
6411     case EP_White:\r
6412       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6413       fromX = fromY = -1;\r
6414       break;\r
6415 \r
6416     case EP_Black:\r
6417       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6418       fromX = fromY = -1;\r
6419       break;\r
6420 \r
6421     case EP_Promote:\r
6422       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6423       fromX = fromY = -1;\r
6424       break;\r
6425 \r
6426     case EP_Demote:\r
6427       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6428       fromX = fromY = -1;\r
6429       break;\r
6430 \r
6431     case DP_Pawn:\r
6432       DropMenuEvent(WhitePawn, fromX, fromY);\r
6433       fromX = fromY = -1;\r
6434       break;\r
6435 \r
6436     case DP_Knight:\r
6437       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6438       fromX = fromY = -1;\r
6439       break;\r
6440 \r
6441     case DP_Bishop:\r
6442       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6443       fromX = fromY = -1;\r
6444       break;\r
6445 \r
6446     case DP_Rook:\r
6447       DropMenuEvent(WhiteRook, fromX, fromY);\r
6448       fromX = fromY = -1;\r
6449       break;\r
6450 \r
6451     case DP_Queen:\r
6452       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6453       fromX = fromY = -1;\r
6454       break;\r
6455 \r
6456     default:\r
6457       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6458     }\r
6459     break;\r
6460 \r
6461   case WM_TIMER:\r
6462     switch (wParam) {\r
6463     case CLOCK_TIMER_ID:\r
6464       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6465       clockTimerEvent = 0;\r
6466       DecrementClocks(); /* call into back end */\r
6467       break;\r
6468     case LOAD_GAME_TIMER_ID:\r
6469       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6470       loadGameTimerEvent = 0;\r
6471       AutoPlayGameLoop(); /* call into back end */\r
6472       break;\r
6473     case ANALYSIS_TIMER_ID:\r
6474       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6475                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6476         AnalysisPeriodicEvent(0);\r
6477       } else {\r
6478         KillTimer(hwnd, analysisTimerEvent);\r
6479         analysisTimerEvent = 0;\r
6480       }\r
6481       break;\r
6482     case DELAYED_TIMER_ID:\r
6483       KillTimer(hwnd, delayedTimerEvent);\r
6484       delayedTimerEvent = 0;\r
6485       delayedTimerCallback();\r
6486       break;\r
6487     }\r
6488     break;\r
6489 \r
6490   case WM_USER_Input:\r
6491     InputEvent(hwnd, message, wParam, lParam);\r
6492     break;\r
6493 \r
6494   /* [AS] Also move "attached" child windows */\r
6495   case WM_WINDOWPOSCHANGING:\r
6496 \r
6497     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6498         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6499 \r
6500         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6501             /* Window is moving */\r
6502             RECT rcMain;\r
6503 \r
6504 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6505             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6506             rcMain.right  = boardX + winWidth;\r
6507             rcMain.top    = boardY;\r
6508             rcMain.bottom = boardY + winHeight;\r
6509             \r
6510             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6511             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6512             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6513             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6514             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6515             boardX = lpwp->x;\r
6516             boardY = lpwp->y;\r
6517         }\r
6518     }\r
6519     break;\r
6520 \r
6521   /* [AS] Snapping */\r
6522   case WM_ENTERSIZEMOVE:\r
6523     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6524     if (hwnd == hwndMain) {\r
6525       doingSizing = TRUE;\r
6526       lastSizing = 0;\r
6527     }\r
6528     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6529     break;\r
6530 \r
6531   case WM_SIZING:\r
6532     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6533     if (hwnd == hwndMain) {\r
6534       lastSizing = wParam;\r
6535     }\r
6536     break;\r
6537 \r
6538   case WM_MOVING:\r
6539     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6540       return OnMoving( &sd, hwnd, wParam, lParam );\r
6541 \r
6542   case WM_EXITSIZEMOVE:\r
6543     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6544     if (hwnd == hwndMain) {\r
6545       RECT client;\r
6546       doingSizing = FALSE;\r
6547       InvalidateRect(hwnd, &boardRect, FALSE);\r
6548       GetClientRect(hwnd, &client);\r
6549       ResizeBoard(client.right, client.bottom, lastSizing);\r
6550       lastSizing = 0;\r
6551       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6552     }\r
6553     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6554     break;\r
6555 \r
6556   case WM_DESTROY: /* message: window being destroyed */\r
6557     PostQuitMessage(0);\r
6558     break;\r
6559 \r
6560   case WM_CLOSE:\r
6561     if (hwnd == hwndMain) {\r
6562       ExitEvent(0);\r
6563     }\r
6564     break;\r
6565 \r
6566   default:      /* Passes it on if unprocessed */\r
6567     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6568   }\r
6569   return 0;\r
6570 }\r
6571 \r
6572 /*---------------------------------------------------------------------------*\\r
6573  *\r
6574  * Misc utility routines\r
6575  *\r
6576 \*---------------------------------------------------------------------------*/\r
6577 \r
6578 /*\r
6579  * Decent random number generator, at least not as bad as Windows\r
6580  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6581  */\r
6582 unsigned int randstate;\r
6583 \r
6584 int\r
6585 myrandom(void)\r
6586 {\r
6587   randstate = randstate * 1664525 + 1013904223;\r
6588   return (int) randstate & 0x7fffffff;\r
6589 }\r
6590 \r
6591 void\r
6592 mysrandom(unsigned int seed)\r
6593 {\r
6594   randstate = seed;\r
6595 }\r
6596 \r
6597 \r
6598 /* \r
6599  * returns TRUE if user selects a different color, FALSE otherwise \r
6600  */\r
6601 \r
6602 BOOL\r
6603 ChangeColor(HWND hwnd, COLORREF *which)\r
6604 {\r
6605   static BOOL firstTime = TRUE;\r
6606   static DWORD customColors[16];\r
6607   CHOOSECOLOR cc;\r
6608   COLORREF newcolor;\r
6609   int i;\r
6610   ColorClass ccl;\r
6611 \r
6612   if (firstTime) {\r
6613     /* Make initial colors in use available as custom colors */\r
6614     /* Should we put the compiled-in defaults here instead? */\r
6615     i = 0;\r
6616     customColors[i++] = lightSquareColor & 0xffffff;\r
6617     customColors[i++] = darkSquareColor & 0xffffff;\r
6618     customColors[i++] = whitePieceColor & 0xffffff;\r
6619     customColors[i++] = blackPieceColor & 0xffffff;\r
6620     customColors[i++] = highlightSquareColor & 0xffffff;\r
6621     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6622 \r
6623     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6624       customColors[i++] = textAttribs[ccl].color;\r
6625     }\r
6626     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6627     firstTime = FALSE;\r
6628   }\r
6629 \r
6630   cc.lStructSize = sizeof(cc);\r
6631   cc.hwndOwner = hwnd;\r
6632   cc.hInstance = NULL;\r
6633   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6634   cc.lpCustColors = (LPDWORD) customColors;\r
6635   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6636 \r
6637   if (!ChooseColor(&cc)) return FALSE;\r
6638 \r
6639   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6640   if (newcolor == *which) return FALSE;\r
6641   *which = newcolor;\r
6642   return TRUE;\r
6643 \r
6644   /*\r
6645   InitDrawingColors();\r
6646   InvalidateRect(hwnd, &boardRect, FALSE);\r
6647   */\r
6648 }\r
6649 \r
6650 BOOLEAN\r
6651 MyLoadSound(MySound *ms)\r
6652 {\r
6653   BOOL ok = FALSE;\r
6654   struct stat st;\r
6655   FILE *f;\r
6656 \r
6657   if (ms->data) free(ms->data);\r
6658   ms->data = NULL;\r
6659 \r
6660   switch (ms->name[0]) {\r
6661   case NULLCHAR:\r
6662     /* Silence */\r
6663     ok = TRUE;\r
6664     break;\r
6665   case '$':\r
6666     /* System sound from Control Panel.  Don't preload here. */\r
6667     ok = TRUE;\r
6668     break;\r
6669   case '!':\r
6670     if (ms->name[1] == NULLCHAR) {\r
6671       /* "!" alone = silence */\r
6672       ok = TRUE;\r
6673     } else {\r
6674       /* Builtin wave resource.  Error if not found. */\r
6675       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6676       if (h == NULL) break;\r
6677       ms->data = (void *)LoadResource(hInst, h);\r
6678       if (h == NULL) break;\r
6679       ok = TRUE;\r
6680     }\r
6681     break;\r
6682   default:\r
6683     /* .wav file.  Error if not found. */\r
6684     f = fopen(ms->name, "rb");\r
6685     if (f == NULL) break;\r
6686     if (fstat(fileno(f), &st) < 0) break;\r
6687     ms->data = malloc(st.st_size);\r
6688     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6689     fclose(f);\r
6690     ok = TRUE;\r
6691     break;\r
6692   }\r
6693   if (!ok) {\r
6694     char buf[MSG_SIZ];\r
6695     sprintf(buf, "Error loading sound %s", ms->name);\r
6696     DisplayError(buf, GetLastError());\r
6697   }\r
6698   return ok;\r
6699 }\r
6700 \r
6701 BOOLEAN\r
6702 MyPlaySound(MySound *ms)\r
6703 {\r
6704   BOOLEAN ok = FALSE;\r
6705 \r
6706   if(mute) return TRUE; // [HGM] mute: suppress all sound play when muted\r
6707   switch (ms->name[0]) {\r
6708   case NULLCHAR:\r
6709         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6710     /* Silence */\r
6711     ok = TRUE;\r
6712     break;\r
6713   case '$':\r
6714     /* System sound from Control Panel (deprecated feature).\r
6715        "$" alone or an unset sound name gets default beep (still in use). */\r
6716     if (ms->name[1]) {\r
6717       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6718     }\r
6719     if (!ok) ok = MessageBeep(MB_OK);\r
6720     break; \r
6721   case '!':\r
6722     /* Builtin wave resource, or "!" alone for silence */\r
6723     if (ms->name[1]) {\r
6724       if (ms->data == NULL) return FALSE;\r
6725       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6726     } else {\r
6727       ok = TRUE;\r
6728     }\r
6729     break;\r
6730   default:\r
6731     /* .wav file.  Error if not found. */\r
6732     if (ms->data == NULL) return FALSE;\r
6733     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6734     break;\r
6735   }\r
6736   /* Don't print an error: this can happen innocently if the sound driver\r
6737      is busy; for instance, if another instance of WinBoard is playing\r
6738      a sound at about the same time. */\r
6739 #if 0\r
6740   if (!ok) {\r
6741     char buf[MSG_SIZ];\r
6742     sprintf(buf, "Error playing sound %s", ms->name);\r
6743     DisplayError(buf, GetLastError());\r
6744   }\r
6745 #endif\r
6746   return ok;\r
6747 }\r
6748 \r
6749 \r
6750 LRESULT CALLBACK\r
6751 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6752 {\r
6753   BOOL ok;\r
6754   OPENFILENAME *ofn;\r
6755   static UINT *number; /* gross that this is static */\r
6756 \r
6757   switch (message) {\r
6758   case WM_INITDIALOG: /* message: initialize dialog box */\r
6759     /* Center the dialog over the application window */\r
6760     ofn = (OPENFILENAME *) lParam;\r
6761     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6762       number = (UINT *) ofn->lCustData;\r
6763       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6764     } else {\r
6765       number = NULL;\r
6766     }\r
6767     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6768     return FALSE;  /* Allow for further processing */\r
6769 \r
6770   case WM_COMMAND:\r
6771     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6772       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6773     }\r
6774     return FALSE;  /* Allow for further processing */\r
6775   }\r
6776   return FALSE;\r
6777 }\r
6778 \r
6779 UINT APIENTRY\r
6780 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6781 {\r
6782   static UINT *number;\r
6783   OPENFILENAME *ofname;\r
6784   OFNOTIFY *ofnot;\r
6785   switch (uiMsg) {\r
6786   case WM_INITDIALOG:\r
6787     ofname = (OPENFILENAME *)lParam;\r
6788     number = (UINT *)(ofname->lCustData);\r
6789     break;\r
6790   case WM_NOTIFY:\r
6791     ofnot = (OFNOTIFY *)lParam;\r
6792     if (ofnot->hdr.code == CDN_FILEOK) {\r
6793       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6794     }\r
6795     break;\r
6796   }\r
6797   return 0;\r
6798 }\r
6799 \r
6800 \r
6801 FILE *\r
6802 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6803                char *nameFilt, char *dlgTitle, UINT *number,\r
6804                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6805 {\r
6806   OPENFILENAME openFileName;\r
6807   char buf1[MSG_SIZ];\r
6808   FILE *f;\r
6809 \r
6810   if (fileName == NULL) fileName = buf1;\r
6811   if (defName == NULL) {\r
6812     strcpy(fileName, "*.");\r
6813     strcat(fileName, defExt);\r
6814   } else {\r
6815     strcpy(fileName, defName);\r
6816   }\r
6817   if (fileTitle) strcpy(fileTitle, "");\r
6818   if (number) *number = 0;\r
6819 \r
6820   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6821   openFileName.hwndOwner         = hwnd;\r
6822   openFileName.hInstance         = (HANDLE) hInst;\r
6823   openFileName.lpstrFilter       = nameFilt;\r
6824   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6825   openFileName.nMaxCustFilter    = 0L;\r
6826   openFileName.nFilterIndex      = 1L;\r
6827   openFileName.lpstrFile         = fileName;\r
6828   openFileName.nMaxFile          = MSG_SIZ;\r
6829   openFileName.lpstrFileTitle    = fileTitle;\r
6830   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6831   openFileName.lpstrInitialDir   = NULL;\r
6832   openFileName.lpstrTitle        = dlgTitle;\r
6833   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6834     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6835     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6836     | (oldDialog ? 0 : OFN_EXPLORER);\r
6837   openFileName.nFileOffset       = 0;\r
6838   openFileName.nFileExtension    = 0;\r
6839   openFileName.lpstrDefExt       = defExt;\r
6840   openFileName.lCustData         = (LONG) number;\r
6841   openFileName.lpfnHook          = oldDialog ?\r
6842     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6843   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6844 \r
6845   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6846                         GetOpenFileName(&openFileName)) {\r
6847     /* open the file */\r
6848     f = fopen(openFileName.lpstrFile, write);\r
6849     if (f == NULL) {\r
6850       MessageBox(hwnd, "File open failed", NULL,\r
6851                  MB_OK|MB_ICONEXCLAMATION);\r
6852       return NULL;\r
6853     }\r
6854   } else {\r
6855     int err = CommDlgExtendedError();\r
6856     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6857     return FALSE;\r
6858   }\r
6859   return f;\r
6860 }\r
6861 \r
6862 \r
6863 \r
6864 VOID APIENTRY\r
6865 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6866 {\r
6867   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6868 \r
6869   /*\r
6870    * Get the first pop-up menu in the menu template. This is the\r
6871    * menu that TrackPopupMenu displays.\r
6872    */\r
6873   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6874 \r
6875   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6876 \r
6877   /*\r
6878    * TrackPopup uses screen coordinates, so convert the\r
6879    * coordinates of the mouse click to screen coordinates.\r
6880    */\r
6881   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6882 \r
6883   /* Draw and track the floating pop-up menu. */\r
6884   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6885                  pt.x, pt.y, 0, hwnd, NULL);\r
6886 \r
6887   /* Destroy the menu.*/\r
6888   DestroyMenu(hmenu);\r
6889 }\r
6890    \r
6891 typedef struct {\r
6892   HWND hDlg, hText;\r
6893   int sizeX, sizeY, newSizeX, newSizeY;\r
6894   HDWP hdwp;\r
6895 } ResizeEditPlusButtonsClosure;\r
6896 \r
6897 BOOL CALLBACK\r
6898 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6899 {\r
6900   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6901   RECT rect;\r
6902   POINT pt;\r
6903 \r
6904   if (hChild == cl->hText) return TRUE;\r
6905   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6906   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6907   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6908   ScreenToClient(cl->hDlg, &pt);\r
6909   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6910     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6911   return TRUE;\r
6912 }\r
6913 \r
6914 /* Resize a dialog that has a (rich) edit field filling most of\r
6915    the top, with a row of buttons below */\r
6916 VOID\r
6917 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6918 {\r
6919   RECT rectText;\r
6920   int newTextHeight, newTextWidth;\r
6921   ResizeEditPlusButtonsClosure cl;\r
6922   \r
6923   /*if (IsIconic(hDlg)) return;*/\r
6924   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6925   \r
6926   cl.hdwp = BeginDeferWindowPos(8);\r
6927 \r
6928   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6929   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6930   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6931   if (newTextHeight < 0) {\r
6932     newSizeY += -newTextHeight;\r
6933     newTextHeight = 0;\r
6934   }\r
6935   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6936     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6937 \r
6938   cl.hDlg = hDlg;\r
6939   cl.hText = hText;\r
6940   cl.sizeX = sizeX;\r
6941   cl.sizeY = sizeY;\r
6942   cl.newSizeX = newSizeX;\r
6943   cl.newSizeY = newSizeY;\r
6944   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6945 \r
6946   EndDeferWindowPos(cl.hdwp);\r
6947 }\r
6948 \r
6949 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6950 {\r
6951     RECT    rChild, rParent;\r
6952     int     wChild, hChild, wParent, hParent;\r
6953     int     wScreen, hScreen, xNew, yNew;\r
6954     HDC     hdc;\r
6955 \r
6956     /* Get the Height and Width of the child window */\r
6957     GetWindowRect (hwndChild, &rChild);\r
6958     wChild = rChild.right - rChild.left;\r
6959     hChild = rChild.bottom - rChild.top;\r
6960 \r
6961     /* Get the Height and Width of the parent window */\r
6962     GetWindowRect (hwndParent, &rParent);\r
6963     wParent = rParent.right - rParent.left;\r
6964     hParent = rParent.bottom - rParent.top;\r
6965 \r
6966     /* Get the display limits */\r
6967     hdc = GetDC (hwndChild);\r
6968     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6969     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6970     ReleaseDC(hwndChild, hdc);\r
6971 \r
6972     /* Calculate new X position, then adjust for screen */\r
6973     xNew = rParent.left + ((wParent - wChild) /2);\r
6974     if (xNew < 0) {\r
6975         xNew = 0;\r
6976     } else if ((xNew+wChild) > wScreen) {\r
6977         xNew = wScreen - wChild;\r
6978     }\r
6979 \r
6980     /* Calculate new Y position, then adjust for screen */\r
6981     if( mode == 0 ) {\r
6982         yNew = rParent.top  + ((hParent - hChild) /2);\r
6983     }\r
6984     else {\r
6985         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6986     }\r
6987 \r
6988     if (yNew < 0) {\r
6989         yNew = 0;\r
6990     } else if ((yNew+hChild) > hScreen) {\r
6991         yNew = hScreen - hChild;\r
6992     }\r
6993 \r
6994     /* Set it, and return */\r
6995     return SetWindowPos (hwndChild, NULL,\r
6996                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6997 }\r
6998 \r
6999 /* Center one window over another */\r
7000 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
7001 {\r
7002     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
7003 }\r
7004 \r
7005 /*---------------------------------------------------------------------------*\\r
7006  *\r
7007  * Startup Dialog functions\r
7008  *\r
7009 \*---------------------------------------------------------------------------*/\r
7010 void\r
7011 InitComboStrings(HANDLE hwndCombo, char **cd)\r
7012 {\r
7013   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7014 \r
7015   while (*cd != NULL) {\r
7016     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
7017     cd++;\r
7018   }\r
7019 }\r
7020 \r
7021 void\r
7022 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
7023 {\r
7024   char buf1[ARG_MAX];\r
7025   int len;\r
7026 \r
7027   if (str[0] == '@') {\r
7028     FILE* f = fopen(str + 1, "r");\r
7029     if (f == NULL) {\r
7030       DisplayFatalError(str + 1, errno, 2);\r
7031       return;\r
7032     }\r
7033     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
7034     fclose(f);\r
7035     buf1[len] = NULLCHAR;\r
7036     str = buf1;\r
7037   }\r
7038 \r
7039   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7040 \r
7041   for (;;) {\r
7042     char buf[MSG_SIZ];\r
7043     char *end = strchr(str, '\n');\r
7044     if (end == NULL) return;\r
7045     memcpy(buf, str, end - str);\r
7046     buf[end - str] = NULLCHAR;\r
7047     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
7048     str = end + 1;\r
7049   }\r
7050 }\r
7051 \r
7052 void\r
7053 SetStartupDialogEnables(HWND hDlg)\r
7054 {\r
7055   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
7056     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7057     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
7058   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7059     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
7060   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
7061     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
7062   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
7063     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
7064   EnableWindow(GetDlgItem(hDlg, IDOK),\r
7065     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7066     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
7067     IsDlgButtonChecked(hDlg, OPT_View));\r
7068 }\r
7069 \r
7070 char *\r
7071 QuoteForFilename(char *filename)\r
7072 {\r
7073   int dquote, space;\r
7074   dquote = strchr(filename, '"') != NULL;\r
7075   space = strchr(filename, ' ') != NULL;\r
7076   if (dquote || space) {\r
7077     if (dquote) {\r
7078       return "'";\r
7079     } else {\r
7080       return "\"";\r
7081     }\r
7082   } else {\r
7083     return "";\r
7084   }\r
7085 }\r
7086 \r
7087 VOID\r
7088 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
7089 {\r
7090   char buf[MSG_SIZ];\r
7091   char *q;\r
7092 \r
7093   InitComboStringsFromOption(hwndCombo, nthnames);\r
7094   q = QuoteForFilename(nthcp);\r
7095   sprintf(buf, "%s%s%s", q, nthcp, q);\r
7096   if (*nthdir != NULLCHAR) {\r
7097     q = QuoteForFilename(nthdir);\r
7098     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
7099   }\r
7100   if (*nthcp == NULLCHAR) {\r
7101     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7102   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7103     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7104     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7105   }\r
7106 }\r
7107 \r
7108 LRESULT CALLBACK\r
7109 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7110 {\r
7111   char buf[MSG_SIZ];\r
7112   HANDLE hwndCombo;\r
7113   char *p;\r
7114 \r
7115   switch (message) {\r
7116   case WM_INITDIALOG:\r
7117     /* Center the dialog */\r
7118     CenterWindow (hDlg, GetDesktopWindow());\r
7119     /* Initialize the dialog items */\r
7120     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7121                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7122                   firstChessProgramNames);\r
7123     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7124                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7125                   secondChessProgramNames);\r
7126     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7127     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7128     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7129     if (*appData.icsHelper != NULLCHAR) {\r
7130       char *q = QuoteForFilename(appData.icsHelper);\r
7131       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7132     }\r
7133     if (*appData.icsHost == NULLCHAR) {\r
7134       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7135       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7136     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7137       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7138       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7139     }\r
7140 \r
7141     if (appData.icsActive) {\r
7142       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7143     }\r
7144     else if (appData.noChessProgram) {\r
7145       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7146     }\r
7147     else {\r
7148       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7149     }\r
7150 \r
7151     SetStartupDialogEnables(hDlg);\r
7152     return TRUE;\r
7153 \r
7154   case WM_COMMAND:\r
7155     switch (LOWORD(wParam)) {\r
7156     case IDOK:\r
7157       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7158         strcpy(buf, "/fcp=");\r
7159         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7160         p = buf;\r
7161         ParseArgs(StringGet, &p);\r
7162         strcpy(buf, "/scp=");\r
7163         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7164         p = buf;\r
7165         ParseArgs(StringGet, &p);\r
7166         appData.noChessProgram = FALSE;\r
7167         appData.icsActive = FALSE;\r
7168       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7169         strcpy(buf, "/ics /icshost=");\r
7170         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7171         p = buf;\r
7172         ParseArgs(StringGet, &p);\r
7173         if (appData.zippyPlay) {\r
7174           strcpy(buf, "/fcp=");\r
7175           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7176           p = buf;\r
7177           ParseArgs(StringGet, &p);\r
7178         }\r
7179       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7180         appData.noChessProgram = TRUE;\r
7181         appData.icsActive = FALSE;\r
7182       } else {\r
7183         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7184                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7185         return TRUE;\r
7186       }\r
7187       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7188         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7189         p = buf;\r
7190         ParseArgs(StringGet, &p);\r
7191       }\r
7192       EndDialog(hDlg, TRUE);\r
7193       return TRUE;\r
7194 \r
7195     case IDCANCEL:\r
7196       ExitEvent(0);\r
7197       return TRUE;\r
7198 \r
7199     case IDM_HELPCONTENTS:\r
7200       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7201         MessageBox (GetFocus(),\r
7202                     "Unable to activate help",\r
7203                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7204       }\r
7205       break;\r
7206 \r
7207     default:\r
7208       SetStartupDialogEnables(hDlg);\r
7209       break;\r
7210     }\r
7211     break;\r
7212   }\r
7213   return FALSE;\r
7214 }\r
7215 \r
7216 /*---------------------------------------------------------------------------*\\r
7217  *\r
7218  * About box dialog functions\r
7219  *\r
7220 \*---------------------------------------------------------------------------*/\r
7221 \r
7222 /* Process messages for "About" dialog box */\r
7223 LRESULT CALLBACK\r
7224 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7225 {\r
7226   switch (message) {\r
7227   case WM_INITDIALOG: /* message: initialize dialog box */\r
7228     /* Center the dialog over the application window */\r
7229     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7230     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7231     JAWS_COPYRIGHT\r
7232     return (TRUE);\r
7233 \r
7234   case WM_COMMAND: /* message: received a command */\r
7235     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7236         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7237       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7238       return (TRUE);\r
7239     }\r
7240     break;\r
7241   }\r
7242   return (FALSE);\r
7243 }\r
7244 \r
7245 /*---------------------------------------------------------------------------*\\r
7246  *\r
7247  * Comment Dialog functions\r
7248  *\r
7249 \*---------------------------------------------------------------------------*/\r
7250 \r
7251 LRESULT CALLBACK\r
7252 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7253 {\r
7254   static HANDLE hwndText = NULL;\r
7255   int len, newSizeX, newSizeY, flags;\r
7256   static int sizeX, sizeY;\r
7257   char *str;\r
7258   RECT rect;\r
7259   MINMAXINFO *mmi;\r
7260 \r
7261   switch (message) {\r
7262   case WM_INITDIALOG: /* message: initialize dialog box */\r
7263     /* Initialize the dialog items */\r
7264     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7265     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7266     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7267     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7268     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7269     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7270     SetWindowText(hDlg, commentTitle);\r
7271     if (editComment) {\r
7272       SetFocus(hwndText);\r
7273     } else {\r
7274       SetFocus(GetDlgItem(hDlg, IDOK));\r
7275     }\r
7276     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7277                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7278                 MAKELPARAM(FALSE, 0));\r
7279     /* Size and position the dialog */\r
7280     if (!commentDialog) {\r
7281       commentDialog = hDlg;\r
7282       flags = SWP_NOZORDER;\r
7283       GetClientRect(hDlg, &rect);\r
7284       sizeX = rect.right;\r
7285       sizeY = rect.bottom;\r
7286       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7287           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7288         WINDOWPLACEMENT wp;\r
7289         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7290         wp.length = sizeof(WINDOWPLACEMENT);\r
7291         wp.flags = 0;\r
7292         wp.showCmd = SW_SHOW;\r
7293         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7294         wp.rcNormalPosition.left = commentX;\r
7295         wp.rcNormalPosition.right = commentX + commentW;\r
7296         wp.rcNormalPosition.top = commentY;\r
7297         wp.rcNormalPosition.bottom = commentY + commentH;\r
7298         SetWindowPlacement(hDlg, &wp);\r
7299 \r
7300         GetClientRect(hDlg, &rect);\r
7301         newSizeX = rect.right;\r
7302         newSizeY = rect.bottom;\r
7303         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7304                               newSizeX, newSizeY);\r
7305         sizeX = newSizeX;\r
7306         sizeY = newSizeY;\r
7307       }\r
7308     }\r
7309     return FALSE;\r
7310 \r
7311   case WM_COMMAND: /* message: received a command */\r
7312     switch (LOWORD(wParam)) {\r
7313     case IDOK:\r
7314       if (editComment) {\r
7315         char *p, *q;\r
7316         /* Read changed options from the dialog box */\r
7317         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7318         len = GetWindowTextLength(hwndText);\r
7319         str = (char *) malloc(len + 1);\r
7320         GetWindowText(hwndText, str, len + 1);\r
7321         p = q = str;\r
7322         while (*q) {\r
7323           if (*q == '\r')\r
7324             q++;\r
7325           else\r
7326             *p++ = *q++;\r
7327         }\r
7328         *p = NULLCHAR;\r
7329         ReplaceComment(commentIndex, str);\r
7330         free(str);\r
7331       }\r
7332       CommentPopDown();\r
7333       return TRUE;\r
7334 \r
7335     case IDCANCEL:\r
7336     case OPT_CancelComment:\r
7337       CommentPopDown();\r
7338       return TRUE;\r
7339 \r
7340     case OPT_ClearComment:\r
7341       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7342       break;\r
7343 \r
7344     case OPT_EditComment:\r
7345       EditCommentEvent();\r
7346       return TRUE;\r
7347 \r
7348     default:\r
7349       break;\r
7350     }\r
7351     break;\r
7352 \r
7353   case WM_SIZE:\r
7354     newSizeX = LOWORD(lParam);\r
7355     newSizeY = HIWORD(lParam);\r
7356     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7357     sizeX = newSizeX;\r
7358     sizeY = newSizeY;\r
7359     break;\r
7360 \r
7361   case WM_GETMINMAXINFO:\r
7362     /* Prevent resizing window too small */\r
7363     mmi = (MINMAXINFO *) lParam;\r
7364     mmi->ptMinTrackSize.x = 100;\r
7365     mmi->ptMinTrackSize.y = 100;\r
7366     break;\r
7367   }\r
7368   return FALSE;\r
7369 }\r
7370 \r
7371 VOID\r
7372 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7373 {\r
7374   FARPROC lpProc;\r
7375   char *p, *q;\r
7376 \r
7377   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7378 \r
7379   if (str == NULL) str = "";\r
7380   p = (char *) malloc(2 * strlen(str) + 2);\r
7381   q = p;\r
7382   while (*str) {\r
7383     if (*str == '\n') *q++ = '\r';\r
7384     *q++ = *str++;\r
7385   }\r
7386   *q = NULLCHAR;\r
7387   if (commentText != NULL) free(commentText);\r
7388 \r
7389   commentIndex = index;\r
7390   commentTitle = title;\r
7391   commentText = p;\r
7392   editComment = edit;\r
7393 \r
7394   if (commentDialog) {\r
7395     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7396     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7397   } else {\r
7398     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7399     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7400                  hwndMain, (DLGPROC)lpProc);\r
7401     FreeProcInstance(lpProc);\r
7402   }\r
7403   commentDialogUp = TRUE;\r
7404 }\r
7405 \r
7406 \r
7407 /*---------------------------------------------------------------------------*\\r
7408  *\r
7409  * Type-in move dialog functions\r
7410  * \r
7411 \*---------------------------------------------------------------------------*/\r
7412 \r
7413 LRESULT CALLBACK\r
7414 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7415 {\r
7416   char move[MSG_SIZ];\r
7417   HWND hInput;\r
7418   ChessMove moveType;\r
7419   int fromX, fromY, toX, toY;\r
7420   char promoChar;\r
7421 \r
7422   switch (message) {\r
7423   case WM_INITDIALOG:\r
7424     move[0] = (char) lParam;\r
7425     move[1] = NULLCHAR;\r
7426     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7427     hInput = GetDlgItem(hDlg, OPT_Move);\r
7428     SetWindowText(hInput, move);\r
7429     SetFocus(hInput);\r
7430     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7431     return FALSE;\r
7432 \r
7433   case WM_COMMAND:\r
7434     switch (LOWORD(wParam)) {\r
7435     case IDOK:\r
7436       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7437       { int n; Board board;\r
7438         // [HGM] FENedit\r
7439         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7440                 EditPositionPasteFEN(move);\r
7441                 EndDialog(hDlg, TRUE);\r
7442                 return TRUE;\r
7443         }\r
7444         // [HGM] movenum: allow move number to be typed in any mode\r
7445         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7446           currentMove = 2*n-1;\r
7447           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7448           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7449           EndDialog(hDlg, TRUE);\r
7450           DrawPosition(TRUE, boards[currentMove]);\r
7451           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7452           else DisplayMessage("", "");\r
7453           return TRUE;\r
7454         }\r
7455       }\r
7456       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7457         gameMode != Training) {\r
7458         DisplayMoveError("Displayed move is not current");\r
7459       } else {\r
7460 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7461         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7462           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7463         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7464         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7465           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7466           if (gameMode != Training)\r
7467               forwardMostMove = currentMove;\r
7468           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7469         } else {\r
7470           DisplayMoveError("Could not parse move");\r
7471         }\r
7472       }\r
7473       EndDialog(hDlg, TRUE);\r
7474       return TRUE;\r
7475     case IDCANCEL:\r
7476       EndDialog(hDlg, FALSE);\r
7477       return TRUE;\r
7478     default:\r
7479       break;\r
7480     }\r
7481     break;\r
7482   }\r
7483   return FALSE;\r
7484 }\r
7485 \r
7486 VOID\r
7487 PopUpMoveDialog(char firstchar)\r
7488 {\r
7489     FARPROC lpProc;\r
7490     \r
7491     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7492         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7493         gameMode == AnalyzeMode || gameMode == EditGame || \r
7494         gameMode == EditPosition || gameMode == IcsExamining ||\r
7495         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7496         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7497                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7498                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7499         gameMode == Training) {\r
7500       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7501       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7502         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7503       FreeProcInstance(lpProc);\r
7504     }\r
7505 }\r
7506 \r
7507 /*---------------------------------------------------------------------------*\\r
7508  *\r
7509  * Type-in name dialog functions\r
7510  * \r
7511 \*---------------------------------------------------------------------------*/\r
7512 \r
7513 LRESULT CALLBACK\r
7514 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7515 {\r
7516   char move[MSG_SIZ];\r
7517   HWND hInput;\r
7518 \r
7519   switch (message) {\r
7520   case WM_INITDIALOG:\r
7521     move[0] = (char) lParam;\r
7522     move[1] = NULLCHAR;\r
7523     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7524     hInput = GetDlgItem(hDlg, OPT_Name);\r
7525     SetWindowText(hInput, move);\r
7526     SetFocus(hInput);\r
7527     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7528     return FALSE;\r
7529 \r
7530   case WM_COMMAND:\r
7531     switch (LOWORD(wParam)) {\r
7532     case IDOK:\r
7533       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7534       appData.userName = strdup(move);\r
7535       SetUserLogo();\r
7536 \r
7537       EndDialog(hDlg, TRUE);\r
7538       return TRUE;\r
7539     case IDCANCEL:\r
7540       EndDialog(hDlg, FALSE);\r
7541       return TRUE;\r
7542     default:\r
7543       break;\r
7544     }\r
7545     break;\r
7546   }\r
7547   return FALSE;\r
7548 }\r
7549 \r
7550 VOID\r
7551 PopUpNameDialog(char firstchar)\r
7552 {\r
7553     FARPROC lpProc;\r
7554     \r
7555       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7556       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7557         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7558       FreeProcInstance(lpProc);\r
7559 }\r
7560 \r
7561 /*---------------------------------------------------------------------------*\\r
7562  *\r
7563  *  Error dialogs\r
7564  * \r
7565 \*---------------------------------------------------------------------------*/\r
7566 \r
7567 /* Nonmodal error box */\r
7568 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7569                              WPARAM wParam, LPARAM lParam);\r
7570 \r
7571 VOID\r
7572 ErrorPopUp(char *title, char *content)\r
7573 {\r
7574   FARPROC lpProc;\r
7575   char *p, *q;\r
7576   BOOLEAN modal = hwndMain == NULL;\r
7577 \r
7578   p = content;\r
7579   q = errorMessage;\r
7580   while (*p) {\r
7581     if (*p == '\n') {\r
7582       if (modal) {\r
7583         *q++ = ' ';\r
7584         p++;\r
7585       } else {\r
7586         *q++ = '\r';\r
7587         *q++ = *p++;\r
7588       }\r
7589     } else {\r
7590       *q++ = *p++;\r
7591     }\r
7592   }\r
7593   *q = NULLCHAR;\r
7594   strncpy(errorTitle, title, sizeof(errorTitle));\r
7595   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7596   \r
7597   if (modal) {\r
7598     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7599   } else {\r
7600     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7601     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7602                  hwndMain, (DLGPROC)lpProc);\r
7603     FreeProcInstance(lpProc);\r
7604   }\r
7605 }\r
7606 \r
7607 VOID\r
7608 ErrorPopDown()\r
7609 {\r
7610   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7611   if (errorDialog == NULL) return;\r
7612   DestroyWindow(errorDialog);\r
7613   errorDialog = NULL;\r
7614 }\r
7615 \r
7616 LRESULT CALLBACK\r
7617 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7618 {\r
7619   HANDLE hwndText;\r
7620   RECT rChild;\r
7621 \r
7622   switch (message) {\r
7623   case WM_INITDIALOG:\r
7624     GetWindowRect(hDlg, &rChild);\r
7625 \r
7626     /*\r
7627     SetWindowPos(hDlg, NULL, rChild.left,\r
7628       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7629       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7630     */\r
7631 \r
7632     /* \r
7633         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7634         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7635         and it doesn't work when you resize the dialog.\r
7636         For now, just give it a default position.\r
7637     */\r
7638     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7639 \r
7640     errorDialog = hDlg;\r
7641     SetWindowText(hDlg, errorTitle);\r
7642     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7643     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7644     return FALSE;\r
7645 \r
7646   case WM_COMMAND:\r
7647     switch (LOWORD(wParam)) {\r
7648     case IDOK:\r
7649     case IDCANCEL:\r
7650       if (errorDialog == hDlg) errorDialog = NULL;\r
7651       DestroyWindow(hDlg);\r
7652       return TRUE;\r
7653 \r
7654     default:\r
7655       break;\r
7656     }\r
7657     break;\r
7658   }\r
7659   return FALSE;\r
7660 }\r
7661 \r
7662 #ifdef GOTHIC\r
7663 HWND gothicDialog = NULL;\r
7664 \r
7665 LRESULT CALLBACK\r
7666 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7667 {\r
7668   HANDLE hwndText;\r
7669   RECT rChild;\r
7670   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7671 \r
7672   switch (message) {\r
7673   case WM_INITDIALOG:\r
7674     GetWindowRect(hDlg, &rChild);\r
7675 \r
7676     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7677                                                              SWP_NOZORDER);\r
7678 \r
7679     /* \r
7680         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7681         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7682         and it doesn't work when you resize the dialog.\r
7683         For now, just give it a default position.\r
7684     */\r
7685     gothicDialog = hDlg;\r
7686     SetWindowText(hDlg, errorTitle);\r
7687     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7688     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7689     return FALSE;\r
7690 \r
7691   case WM_COMMAND:\r
7692     switch (LOWORD(wParam)) {\r
7693     case IDOK:\r
7694     case IDCANCEL:\r
7695       if (errorDialog == hDlg) errorDialog = NULL;\r
7696       DestroyWindow(hDlg);\r
7697       return TRUE;\r
7698 \r
7699     default:\r
7700       break;\r
7701     }\r
7702     break;\r
7703   }\r
7704   return FALSE;\r
7705 }\r
7706 \r
7707 VOID\r
7708 GothicPopUp(char *title, VariantClass variant)\r
7709 {\r
7710   FARPROC lpProc;\r
7711   static char *lastTitle;\r
7712 \r
7713   strncpy(errorTitle, title, sizeof(errorTitle));\r
7714   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7715 \r
7716   if(lastTitle != title && gothicDialog != NULL) {\r
7717     DestroyWindow(gothicDialog);\r
7718     gothicDialog = NULL;\r
7719   }\r
7720   if(variant != VariantNormal && gothicDialog == NULL) {\r
7721     title = lastTitle;\r
7722     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7723     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7724                  hwndMain, (DLGPROC)lpProc);\r
7725     FreeProcInstance(lpProc);\r
7726   }\r
7727 }\r
7728 #endif\r
7729 \r
7730 /*---------------------------------------------------------------------------*\\r
7731  *\r
7732  *  Ics Interaction console functions\r
7733  *\r
7734 \*---------------------------------------------------------------------------*/\r
7735 \r
7736 #define HISTORY_SIZE 64\r
7737 static char *history[HISTORY_SIZE];\r
7738 int histIn = 0, histP = 0;\r
7739 \r
7740 VOID\r
7741 SaveInHistory(char *cmd)\r
7742 {\r
7743   if (history[histIn] != NULL) {\r
7744     free(history[histIn]);\r
7745     history[histIn] = NULL;\r
7746   }\r
7747   if (*cmd == NULLCHAR) return;\r
7748   history[histIn] = StrSave(cmd);\r
7749   histIn = (histIn + 1) % HISTORY_SIZE;\r
7750   if (history[histIn] != NULL) {\r
7751     free(history[histIn]);\r
7752     history[histIn] = NULL;\r
7753   }\r
7754   histP = histIn;\r
7755 }\r
7756 \r
7757 char *\r
7758 PrevInHistory(char *cmd)\r
7759 {\r
7760   int newhp;\r
7761   if (histP == histIn) {\r
7762     if (history[histIn] != NULL) free(history[histIn]);\r
7763     history[histIn] = StrSave(cmd);\r
7764   }\r
7765   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7766   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7767   histP = newhp;\r
7768   return history[histP];\r
7769 }\r
7770 \r
7771 char *\r
7772 NextInHistory()\r
7773 {\r
7774   if (histP == histIn) return NULL;\r
7775   histP = (histP + 1) % HISTORY_SIZE;\r
7776   return history[histP];\r
7777 }\r
7778 \r
7779 typedef struct {\r
7780   char *item;\r
7781   char *command;\r
7782   BOOLEAN getname;\r
7783   BOOLEAN immediate;\r
7784 } IcsTextMenuEntry;\r
7785 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7786 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7787 \r
7788 void\r
7789 ParseIcsTextMenu(char *icsTextMenuString)\r
7790 {\r
7791 //  int flags = 0;\r
7792   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7793   char *p = icsTextMenuString;\r
7794   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7795     free(e->item);\r
7796     e->item = NULL;\r
7797     if (e->command != NULL) {\r
7798       free(e->command);\r
7799       e->command = NULL;\r
7800     }\r
7801     e++;\r
7802   }\r
7803   e = icsTextMenuEntry;\r
7804   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7805     if (*p == ';' || *p == '\n') {\r
7806       e->item = strdup("-");\r
7807       e->command = NULL;\r
7808       p++;\r
7809     } else if (*p == '-') {\r
7810       e->item = strdup("-");\r
7811       e->command = NULL;\r
7812       p++;\r
7813       if (*p) p++;\r
7814     } else {\r
7815       char *q, *r, *s, *t;\r
7816       char c;\r
7817       q = strchr(p, ',');\r
7818       if (q == NULL) break;\r
7819       *q = NULLCHAR;\r
7820       r = strchr(q + 1, ',');\r
7821       if (r == NULL) break;\r
7822       *r = NULLCHAR;\r
7823       s = strchr(r + 1, ',');\r
7824       if (s == NULL) break;\r
7825       *s = NULLCHAR;\r
7826       c = ';';\r
7827       t = strchr(s + 1, c);\r
7828       if (t == NULL) {\r
7829         c = '\n';\r
7830         t = strchr(s + 1, c);\r
7831       }\r
7832       if (t != NULL) *t = NULLCHAR;\r
7833       e->item = strdup(p);\r
7834       e->command = strdup(q + 1);\r
7835       e->getname = *(r + 1) != '0';\r
7836       e->immediate = *(s + 1) != '0';\r
7837       *q = ',';\r
7838       *r = ',';\r
7839       *s = ',';\r
7840       if (t == NULL) break;\r
7841       *t = c;\r
7842       p = t + 1;\r
7843     }\r
7844     e++;\r
7845   } \r
7846 }\r
7847 \r
7848 HMENU\r
7849 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7850 {\r
7851   HMENU hmenu, h;\r
7852   int i = 0;\r
7853   hmenu = LoadMenu(hInst, "TextMenu");\r
7854   h = GetSubMenu(hmenu, 0);\r
7855   while (e->item) {\r
7856     if (strcmp(e->item, "-") == 0) {\r
7857       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7858     } else {\r
7859       if (e->item[0] == '|') {\r
7860         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7861                    IDM_CommandX + i, &e->item[1]);\r
7862       } else {\r
7863         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7864       }\r
7865     }\r
7866     e++;\r
7867     i++;\r
7868   } \r
7869   return hmenu;\r
7870 }\r
7871 \r
7872 WNDPROC consoleTextWindowProc;\r
7873 \r
7874 void\r
7875 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7876 {\r
7877   char buf[MSG_SIZ], name[MSG_SIZ];\r
7878   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7879   CHARRANGE sel;\r
7880 \r
7881   if (!getname) {\r
7882     SetWindowText(hInput, command);\r
7883     if (immediate) {\r
7884       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7885     } else {\r
7886       sel.cpMin = 999999;\r
7887       sel.cpMax = 999999;\r
7888       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7889       SetFocus(hInput);\r
7890     }\r
7891     return;\r
7892   }    \r
7893   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7894   if (sel.cpMin == sel.cpMax) {\r
7895     /* Expand to surrounding word */\r
7896     TEXTRANGE tr;\r
7897     do {\r
7898       tr.chrg.cpMax = sel.cpMin;\r
7899       tr.chrg.cpMin = --sel.cpMin;\r
7900       if (sel.cpMin < 0) break;\r
7901       tr.lpstrText = name;\r
7902       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7903     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7904     sel.cpMin++;\r
7905 \r
7906     do {\r
7907       tr.chrg.cpMin = sel.cpMax;\r
7908       tr.chrg.cpMax = ++sel.cpMax;\r
7909       tr.lpstrText = name;\r
7910       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7911     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7912     sel.cpMax--;\r
7913 \r
7914     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7915       MessageBeep(MB_ICONEXCLAMATION);\r
7916       return;\r
7917     }\r
7918     tr.chrg = sel;\r
7919     tr.lpstrText = name;\r
7920     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7921   } else {\r
7922     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7923       MessageBeep(MB_ICONEXCLAMATION);\r
7924       return;\r
7925     }\r
7926     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7927   }\r
7928   if (immediate) {\r
7929     sprintf(buf, "%s %s", command, name);\r
7930     SetWindowText(hInput, buf);\r
7931     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7932   } else {\r
7933     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7934     SetWindowText(hInput, buf);\r
7935     sel.cpMin = 999999;\r
7936     sel.cpMax = 999999;\r
7937     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7938     SetFocus(hInput);\r
7939   }\r
7940 }\r
7941 \r
7942 LRESULT CALLBACK \r
7943 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7944 {\r
7945   HWND hInput;\r
7946   CHARRANGE sel;\r
7947 \r
7948   switch (message) {\r
7949   case WM_KEYDOWN:\r
7950     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7951     switch (wParam) {\r
7952     case VK_PRIOR:\r
7953       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7954       return 0;\r
7955     case VK_NEXT:\r
7956       sel.cpMin = 999999;\r
7957       sel.cpMax = 999999;\r
7958       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7959       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7960       return 0;\r
7961     }\r
7962     break;\r
7963   case WM_CHAR:\r
7964    if(wParam != '\022') {\r
7965     if (wParam == '\t') {\r
7966       if (GetKeyState(VK_SHIFT) < 0) {\r
7967         /* shifted */\r
7968         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7969         if (buttonDesc[0].hwnd) {\r
7970           SetFocus(buttonDesc[0].hwnd);\r
7971         } else {\r
7972           SetFocus(hwndMain);\r
7973         }\r
7974       } else {\r
7975         /* unshifted */\r
7976         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7977       }\r
7978     } else {\r
7979       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7980       JAWS_DELETE( SetFocus(hInput); )\r
7981       SendMessage(hInput, message, wParam, lParam);\r
7982     }\r
7983     return 0;\r
7984    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7985   case WM_RBUTTONUP:\r
7986     if (GetKeyState(VK_SHIFT) & ~1) {\r
7987       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7988         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7989     } else {\r
7990       POINT pt;\r
7991       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7992       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7993       if (sel.cpMin == sel.cpMax) {\r
7994         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7995         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7996       }\r
7997       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7998         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7999       }\r
8000       pt.x = LOWORD(lParam);\r
8001       pt.y = HIWORD(lParam);\r
8002       MenuPopup(hwnd, pt, hmenu, -1);\r
8003     }\r
8004     return 0;\r
8005   case WM_PASTE:\r
8006     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8007     SetFocus(hInput);\r
8008     return SendMessage(hInput, message, wParam, lParam);\r
8009   case WM_MBUTTONDOWN:\r
8010     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8011   case WM_RBUTTONDOWN:\r
8012     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
8013       /* Move selection here if it was empty */\r
8014       POINT pt;\r
8015       pt.x = LOWORD(lParam);\r
8016       pt.y = HIWORD(lParam);\r
8017       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8018       if (sel.cpMin == sel.cpMax) {\r
8019         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
8020         sel.cpMax = sel.cpMin;\r
8021         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8022       }\r
8023       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
8024     }\r
8025     return 0;\r
8026   case WM_COMMAND:\r
8027     switch (LOWORD(wParam)) {\r
8028     case IDM_QuickPaste:\r
8029       {\r
8030         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8031         if (sel.cpMin == sel.cpMax) {\r
8032           MessageBeep(MB_ICONEXCLAMATION);\r
8033           return 0;\r
8034         }\r
8035         SendMessage(hwnd, WM_COPY, 0, 0);\r
8036         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8037         SendMessage(hInput, WM_PASTE, 0, 0);\r
8038         SetFocus(hInput);\r
8039         return 0;\r
8040       }\r
8041     case IDM_Cut:\r
8042       SendMessage(hwnd, WM_CUT, 0, 0);\r
8043       return 0;\r
8044     case IDM_Paste:\r
8045       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8046       return 0;\r
8047     case IDM_Copy:\r
8048       SendMessage(hwnd, WM_COPY, 0, 0);\r
8049       return 0;\r
8050     default:\r
8051       {\r
8052         int i = LOWORD(wParam) - IDM_CommandX;\r
8053         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
8054             icsTextMenuEntry[i].command != NULL) {\r
8055           CommandX(hwnd, icsTextMenuEntry[i].command,\r
8056                    icsTextMenuEntry[i].getname,\r
8057                    icsTextMenuEntry[i].immediate);\r
8058           return 0;\r
8059         }\r
8060       }\r
8061       break;\r
8062     }\r
8063     break;\r
8064   }\r
8065   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
8066 }\r
8067 \r
8068 WNDPROC consoleInputWindowProc;\r
8069 \r
8070 LRESULT CALLBACK\r
8071 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8072 {\r
8073   char buf[MSG_SIZ];\r
8074   char *p;\r
8075   static BOOL sendNextChar = FALSE;\r
8076   static BOOL quoteNextChar = FALSE;\r
8077   InputSource *is = consoleInputSource;\r
8078   CHARFORMAT cf;\r
8079   CHARRANGE sel;\r
8080 \r
8081   switch (message) {\r
8082   case WM_CHAR:\r
8083     if (!appData.localLineEditing || sendNextChar) {\r
8084       is->buf[0] = (CHAR) wParam;\r
8085       is->count = 1;\r
8086       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8087       sendNextChar = FALSE;\r
8088       return 0;\r
8089     }\r
8090     if (quoteNextChar) {\r
8091       buf[0] = (char) wParam;\r
8092       buf[1] = NULLCHAR;\r
8093       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
8094       quoteNextChar = FALSE;\r
8095       return 0;\r
8096     }\r
8097     switch (wParam) {\r
8098     case '\r':   /* Enter key */\r
8099       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
8100       if (consoleEcho) SaveInHistory(is->buf);\r
8101       is->buf[is->count++] = '\n';\r
8102       is->buf[is->count] = NULLCHAR;\r
8103       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8104       if (consoleEcho) {\r
8105         ConsoleOutput(is->buf, is->count, TRUE);\r
8106       } else if (appData.localLineEditing) {\r
8107         ConsoleOutput("\n", 1, TRUE);\r
8108       }\r
8109       /* fall thru */\r
8110     case '\033': /* Escape key */\r
8111       SetWindowText(hwnd, "");\r
8112       cf.cbSize = sizeof(CHARFORMAT);\r
8113       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8114       if (consoleEcho) {\r
8115         cf.crTextColor = textAttribs[ColorNormal].color;\r
8116       } else {\r
8117         cf.crTextColor = COLOR_ECHOOFF;\r
8118       }\r
8119       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8120       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8121       return 0;\r
8122     case '\t':   /* Tab key */\r
8123       if (GetKeyState(VK_SHIFT) < 0) {\r
8124         /* shifted */\r
8125         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8126       } else {\r
8127         /* unshifted */\r
8128         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8129         if (buttonDesc[0].hwnd) {\r
8130           SetFocus(buttonDesc[0].hwnd);\r
8131         } else {\r
8132           SetFocus(hwndMain);\r
8133         }\r
8134       }\r
8135       return 0;\r
8136     case '\023': /* Ctrl+S */\r
8137       sendNextChar = TRUE;\r
8138       return 0;\r
8139     case '\021': /* Ctrl+Q */\r
8140       quoteNextChar = TRUE;\r
8141       return 0;\r
8142     JAWS_REPLAY\r
8143     default:\r
8144       break;\r
8145     }\r
8146     break;\r
8147   case WM_KEYDOWN:\r
8148     switch (wParam) {\r
8149     case VK_UP:\r
8150       GetWindowText(hwnd, buf, MSG_SIZ);\r
8151       p = PrevInHistory(buf);\r
8152       if (p != NULL) {\r
8153         SetWindowText(hwnd, p);\r
8154         sel.cpMin = 999999;\r
8155         sel.cpMax = 999999;\r
8156         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8157         return 0;\r
8158       }\r
8159       break;\r
8160     case VK_DOWN:\r
8161       p = NextInHistory();\r
8162       if (p != NULL) {\r
8163         SetWindowText(hwnd, p);\r
8164         sel.cpMin = 999999;\r
8165         sel.cpMax = 999999;\r
8166         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8167         return 0;\r
8168       }\r
8169       break;\r
8170     case VK_HOME:\r
8171     case VK_END:\r
8172       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8173       /* fall thru */\r
8174     case VK_PRIOR:\r
8175     case VK_NEXT:\r
8176       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8177       return 0;\r
8178     }\r
8179     break;\r
8180   case WM_MBUTTONDOWN:\r
8181     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8182       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8183     break;\r
8184   case WM_RBUTTONUP:\r
8185     if (GetKeyState(VK_SHIFT) & ~1) {\r
8186       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8187         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8188     } else {\r
8189       POINT pt;\r
8190       HMENU hmenu;\r
8191       hmenu = LoadMenu(hInst, "InputMenu");\r
8192       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8193       if (sel.cpMin == sel.cpMax) {\r
8194         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8195         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8196       }\r
8197       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8198         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8199       }\r
8200       pt.x = LOWORD(lParam);\r
8201       pt.y = HIWORD(lParam);\r
8202       MenuPopup(hwnd, pt, hmenu, -1);\r
8203     }\r
8204     return 0;\r
8205   case WM_COMMAND:\r
8206     switch (LOWORD(wParam)) { \r
8207     case IDM_Undo:\r
8208       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8209       return 0;\r
8210     case IDM_SelectAll:\r
8211       sel.cpMin = 0;\r
8212       sel.cpMax = -1; /*999999?*/\r
8213       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8214       return 0;\r
8215     case IDM_Cut:\r
8216       SendMessage(hwnd, WM_CUT, 0, 0);\r
8217       return 0;\r
8218     case IDM_Paste:\r
8219       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8220       return 0;\r
8221     case IDM_Copy:\r
8222       SendMessage(hwnd, WM_COPY, 0, 0);\r
8223       return 0;\r
8224     }\r
8225     break;\r
8226   }\r
8227   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8228 }\r
8229 \r
8230 #define CO_MAX  100000\r
8231 #define CO_TRIM   1000\r
8232 \r
8233 LRESULT CALLBACK\r
8234 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8235 {\r
8236   static SnapData sd;\r
8237   static HWND hText, hInput /*, hFocus*/;\r
8238 //  InputSource *is = consoleInputSource;\r
8239   RECT rect;\r
8240   static int sizeX, sizeY;\r
8241   int newSizeX, newSizeY;\r
8242   MINMAXINFO *mmi;\r
8243 \r
8244   switch (message) {\r
8245   case WM_INITDIALOG: /* message: initialize dialog box */\r
8246     hwndConsole = hDlg;\r
8247     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8248     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8249     SetFocus(hInput);\r
8250     consoleTextWindowProc = (WNDPROC)\r
8251       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8252     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8253     consoleInputWindowProc = (WNDPROC)\r
8254       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8255     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8256     Colorize(ColorNormal, TRUE);\r
8257     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8258     ChangedConsoleFont();\r
8259     GetClientRect(hDlg, &rect);\r
8260     sizeX = rect.right;\r
8261     sizeY = rect.bottom;\r
8262     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8263         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8264       WINDOWPLACEMENT wp;\r
8265       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8266       wp.length = sizeof(WINDOWPLACEMENT);\r
8267       wp.flags = 0;\r
8268       wp.showCmd = SW_SHOW;\r
8269       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8270       wp.rcNormalPosition.left = wpConsole.x;\r
8271       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8272       wp.rcNormalPosition.top = wpConsole.y;\r
8273       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8274       SetWindowPlacement(hDlg, &wp);\r
8275     }\r
8276 #if 1\r
8277    // [HGM] Chessknight's change 2004-07-13\r
8278    else { /* Determine Defaults */\r
8279        WINDOWPLACEMENT wp;\r
8280        wpConsole.x = winWidth + 1;\r
8281        wpConsole.y = boardY;\r
8282        wpConsole.width = screenWidth -  winWidth;\r
8283        wpConsole.height = winHeight;\r
8284        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8285        wp.length = sizeof(WINDOWPLACEMENT);\r
8286        wp.flags = 0;\r
8287        wp.showCmd = SW_SHOW;\r
8288        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8289        wp.rcNormalPosition.left = wpConsole.x;\r
8290        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8291        wp.rcNormalPosition.top = wpConsole.y;\r
8292        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8293        SetWindowPlacement(hDlg, &wp);\r
8294     }\r
8295 #endif\r
8296     return FALSE;\r
8297 \r
8298   case WM_SETFOCUS:\r
8299     SetFocus(hInput);\r
8300     return 0;\r
8301 \r
8302   case WM_CLOSE:\r
8303     ExitEvent(0);\r
8304     /* not reached */\r
8305     break;\r
8306 \r
8307   case WM_SIZE:\r
8308     if (IsIconic(hDlg)) break;\r
8309     newSizeX = LOWORD(lParam);\r
8310     newSizeY = HIWORD(lParam);\r
8311     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8312       RECT rectText, rectInput;\r
8313       POINT pt;\r
8314       int newTextHeight, newTextWidth;\r
8315       GetWindowRect(hText, &rectText);\r
8316       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8317       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8318       if (newTextHeight < 0) {\r
8319         newSizeY += -newTextHeight;\r
8320         newTextHeight = 0;\r
8321       }\r
8322       SetWindowPos(hText, NULL, 0, 0,\r
8323         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8324       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8325       pt.x = rectInput.left;\r
8326       pt.y = rectInput.top + newSizeY - sizeY;\r
8327       ScreenToClient(hDlg, &pt);\r
8328       SetWindowPos(hInput, NULL, \r
8329         pt.x, pt.y, /* needs client coords */   \r
8330         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8331         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8332     }\r
8333     sizeX = newSizeX;\r
8334     sizeY = newSizeY;\r
8335     break;\r
8336 \r
8337   case WM_GETMINMAXINFO:\r
8338     /* Prevent resizing window too small */\r
8339     mmi = (MINMAXINFO *) lParam;\r
8340     mmi->ptMinTrackSize.x = 100;\r
8341     mmi->ptMinTrackSize.y = 100;\r
8342     break;\r
8343 \r
8344   /* [AS] Snapping */\r
8345   case WM_ENTERSIZEMOVE:\r
8346     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8347 \r
8348   case WM_SIZING:\r
8349     return OnSizing( &sd, hDlg, wParam, lParam );\r
8350 \r
8351   case WM_MOVING:\r
8352     return OnMoving( &sd, hDlg, wParam, lParam );\r
8353 \r
8354   case WM_EXITSIZEMOVE:\r
8355     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8356   }\r
8357 \r
8358   return DefWindowProc(hDlg, message, wParam, lParam);\r
8359 }\r
8360 \r
8361 \r
8362 VOID\r
8363 ConsoleCreate()\r
8364 {\r
8365   HWND hCons;\r
8366   if (hwndConsole) return;\r
8367   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8368   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8369 }\r
8370 \r
8371 \r
8372 VOID\r
8373 ConsoleOutput(char* data, int length, int forceVisible)\r
8374 {\r
8375   HWND hText;\r
8376   int trim, exlen;\r
8377   char *p, *q;\r
8378   char buf[CO_MAX+1];\r
8379   POINT pEnd;\r
8380   RECT rect;\r
8381   static int delayLF = 0;\r
8382   CHARRANGE savesel, sel;\r
8383 \r
8384   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8385   p = data;\r
8386   q = buf;\r
8387   if (delayLF) {\r
8388     *q++ = '\r';\r
8389     *q++ = '\n';\r
8390     delayLF = 0;\r
8391   }\r
8392   while (length--) {\r
8393     if (*p == '\n') {\r
8394       if (*++p) {\r
8395         *q++ = '\r';\r
8396         *q++ = '\n';\r
8397       } else {\r
8398         delayLF = 1;\r
8399       }\r
8400     } else if (*p == '\007') {\r
8401        MyPlaySound(&sounds[(int)SoundBell]);\r
8402        p++;\r
8403     } else {\r
8404       *q++ = *p++;\r
8405     }\r
8406   }\r
8407   *q = NULLCHAR;\r
8408   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8409   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8410   /* Save current selection */\r
8411   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8412   exlen = GetWindowTextLength(hText);\r
8413   /* Find out whether current end of text is visible */\r
8414   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8415   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8416   /* Trim existing text if it's too long */\r
8417   if (exlen + (q - buf) > CO_MAX) {\r
8418     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8419     sel.cpMin = 0;\r
8420     sel.cpMax = trim;\r
8421     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8422     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8423     exlen -= trim;\r
8424     savesel.cpMin -= trim;\r
8425     savesel.cpMax -= trim;\r
8426     if (exlen < 0) exlen = 0;\r
8427     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8428     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8429   }\r
8430   /* Append the new text */\r
8431   sel.cpMin = exlen;\r
8432   sel.cpMax = exlen;\r
8433   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8434   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8435   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8436   if (forceVisible || exlen == 0 ||\r
8437       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8438        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8439     /* Scroll to make new end of text visible if old end of text\r
8440        was visible or new text is an echo of user typein */\r
8441     sel.cpMin = 9999999;\r
8442     sel.cpMax = 9999999;\r
8443     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8444     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8445     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8446     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8447   }\r
8448   if (savesel.cpMax == exlen || forceVisible) {\r
8449     /* Move insert point to new end of text if it was at the old\r
8450        end of text or if the new text is an echo of user typein */\r
8451     sel.cpMin = 9999999;\r
8452     sel.cpMax = 9999999;\r
8453     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8454   } else {\r
8455     /* Restore previous selection */\r
8456     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8457   }\r
8458   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8459 }\r
8460 \r
8461 /*---------*/\r
8462 \r
8463 \r
8464 void\r
8465 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8466 {\r
8467   char buf[100];\r
8468   char *str;\r
8469   COLORREF oldFg, oldBg;\r
8470   HFONT oldFont;\r
8471   RECT rect;\r
8472 \r
8473   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8474 \r
8475   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8476   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8477   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8478 \r
8479   rect.left = x;\r
8480   rect.right = x + squareSize;\r
8481   rect.top  = y;\r
8482   rect.bottom = y + squareSize;\r
8483   str = buf;\r
8484 \r
8485   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8486                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8487              y, ETO_CLIPPED|ETO_OPAQUE,\r
8488              &rect, str, strlen(str), NULL);\r
8489 \r
8490   (void) SetTextColor(hdc, oldFg);\r
8491   (void) SetBkColor(hdc, oldBg);\r
8492   (void) SelectObject(hdc, oldFont);\r
8493 }\r
8494 \r
8495 void\r
8496 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8497               RECT *rect, char *color, char *flagFell)\r
8498 {\r
8499   char buf[100];\r
8500   char *str;\r
8501   COLORREF oldFg, oldBg;\r
8502   HFONT oldFont;\r
8503 \r
8504   if (appData.clockMode) {\r
8505     if (tinyLayout)\r
8506       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8507     else\r
8508       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8509     str = buf;\r
8510   } else {\r
8511     str = color;\r
8512   }\r
8513 \r
8514   if (highlight) {\r
8515     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8516     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8517   } else {\r
8518     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8519     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8520   }\r
8521   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8522 \r
8523   JAWS_SILENCE\r
8524 \r
8525   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8526              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8527              rect, str, strlen(str), NULL);\r
8528   if(logoHeight > 0 && appData.clockMode) {\r
8529       RECT r;\r
8530       sprintf(buf, "%s %s", buf+7, flagFell);\r
8531       r.top = rect->top + logoHeight/2;\r
8532       r.left = rect->left;\r
8533       r.right = rect->right;\r
8534       r.bottom = rect->bottom;\r
8535       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8536                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8537                  &r, str, strlen(str), NULL);\r
8538   }\r
8539   (void) SetTextColor(hdc, oldFg);\r
8540   (void) SetBkColor(hdc, oldBg);\r
8541   (void) SelectObject(hdc, oldFont);\r
8542 }\r
8543 \r
8544 \r
8545 int\r
8546 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8547            OVERLAPPED *ovl)\r
8548 {\r
8549   int ok, err;\r
8550 \r
8551   /* [AS]  */\r
8552   if( count <= 0 ) {\r
8553     if (appData.debugMode) {\r
8554       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8555     }\r
8556 \r
8557     return ERROR_INVALID_USER_BUFFER;\r
8558   }\r
8559 \r
8560   ResetEvent(ovl->hEvent);\r
8561   ovl->Offset = ovl->OffsetHigh = 0;\r
8562   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8563   if (ok) {\r
8564     err = NO_ERROR;\r
8565   } else {\r
8566     err = GetLastError();\r
8567     if (err == ERROR_IO_PENDING) {\r
8568       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8569       if (ok)\r
8570         err = NO_ERROR;\r
8571       else\r
8572         err = GetLastError();\r
8573     }\r
8574   }\r
8575   return err;\r
8576 }\r
8577 \r
8578 int\r
8579 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8580             OVERLAPPED *ovl)\r
8581 {\r
8582   int ok, err;\r
8583 \r
8584   ResetEvent(ovl->hEvent);\r
8585   ovl->Offset = ovl->OffsetHigh = 0;\r
8586   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8587   if (ok) {\r
8588     err = NO_ERROR;\r
8589   } else {\r
8590     err = GetLastError();\r
8591     if (err == ERROR_IO_PENDING) {\r
8592       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8593       if (ok)\r
8594         err = NO_ERROR;\r
8595       else\r
8596         err = GetLastError();\r
8597     }\r
8598   }\r
8599   return err;\r
8600 }\r
8601 \r
8602 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8603 void CheckForInputBufferFull( InputSource * is )\r
8604 {\r
8605     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8606         /* Look for end of line */\r
8607         char * p = is->buf;\r
8608         \r
8609         while( p < is->next && *p != '\n' ) {\r
8610             p++;\r
8611         }\r
8612 \r
8613         if( p >= is->next ) {\r
8614             if (appData.debugMode) {\r
8615                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8616             }\r
8617 \r
8618             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8619             is->count = (DWORD) -1;\r
8620             is->next = is->buf;\r
8621         }\r
8622     }\r
8623 }\r
8624 \r
8625 DWORD\r
8626 InputThread(LPVOID arg)\r
8627 {\r
8628   InputSource *is;\r
8629   OVERLAPPED ovl;\r
8630 \r
8631   is = (InputSource *) arg;\r
8632   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8633   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8634   while (is->hThread != NULL) {\r
8635     is->error = DoReadFile(is->hFile, is->next,\r
8636                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8637                            &is->count, &ovl);\r
8638     if (is->error == NO_ERROR) {\r
8639       is->next += is->count;\r
8640     } else {\r
8641       if (is->error == ERROR_BROKEN_PIPE) {\r
8642         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8643         is->count = 0;\r
8644       } else {\r
8645         is->count = (DWORD) -1;\r
8646         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8647         break; \r
8648       }\r
8649     }\r
8650 \r
8651     CheckForInputBufferFull( is );\r
8652 \r
8653     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8654 \r
8655     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8656 \r
8657     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8658   }\r
8659 \r
8660   CloseHandle(ovl.hEvent);\r
8661   CloseHandle(is->hFile);\r
8662 \r
8663   if (appData.debugMode) {\r
8664     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8665   }\r
8666 \r
8667   return 0;\r
8668 }\r
8669 \r
8670 \r
8671 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8672 DWORD\r
8673 NonOvlInputThread(LPVOID arg)\r
8674 {\r
8675   InputSource *is;\r
8676   char *p, *q;\r
8677   int i;\r
8678   char prev;\r
8679 \r
8680   is = (InputSource *) arg;\r
8681   while (is->hThread != NULL) {\r
8682     is->error = ReadFile(is->hFile, is->next,\r
8683                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8684                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8685     if (is->error == NO_ERROR) {\r
8686       /* Change CRLF to LF */\r
8687       if (is->next > is->buf) {\r
8688         p = is->next - 1;\r
8689         i = is->count + 1;\r
8690       } else {\r
8691         p = is->next;\r
8692         i = is->count;\r
8693       }\r
8694       q = p;\r
8695       prev = NULLCHAR;\r
8696       while (i > 0) {\r
8697         if (prev == '\r' && *p == '\n') {\r
8698           *(q-1) = '\n';\r
8699           is->count--;\r
8700         } else { \r
8701           *q++ = *p;\r
8702         }\r
8703         prev = *p++;\r
8704         i--;\r
8705       }\r
8706       *q = NULLCHAR;\r
8707       is->next = q;\r
8708     } else {\r
8709       if (is->error == ERROR_BROKEN_PIPE) {\r
8710         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8711         is->count = 0; \r
8712       } else {\r
8713         is->count = (DWORD) -1;\r
8714       }\r
8715     }\r
8716 \r
8717     CheckForInputBufferFull( is );\r
8718 \r
8719     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8720 \r
8721     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8722 \r
8723     if (is->count < 0) break;  /* Quit on error */\r
8724   }\r
8725   CloseHandle(is->hFile);\r
8726   return 0;\r
8727 }\r
8728 \r
8729 DWORD\r
8730 SocketInputThread(LPVOID arg)\r
8731 {\r
8732   InputSource *is;\r
8733 \r
8734   is = (InputSource *) arg;\r
8735   while (is->hThread != NULL) {\r
8736     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8737     if ((int)is->count == SOCKET_ERROR) {\r
8738       is->count = (DWORD) -1;\r
8739       is->error = WSAGetLastError();\r
8740     } else {\r
8741       is->error = NO_ERROR;\r
8742       is->next += is->count;\r
8743       if (is->count == 0 && is->second == is) {\r
8744         /* End of file on stderr; quit with no message */\r
8745         break;\r
8746       }\r
8747     }\r
8748     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8749 \r
8750     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8751 \r
8752     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8753   }\r
8754   return 0;\r
8755 }\r
8756 \r
8757 VOID\r
8758 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8759 {\r
8760   InputSource *is;\r
8761 \r
8762   is = (InputSource *) lParam;\r
8763   if (is->lineByLine) {\r
8764     /* Feed in lines one by one */\r
8765     char *p = is->buf;\r
8766     char *q = p;\r
8767     while (q < is->next) {\r
8768       if (*q++ == '\n') {\r
8769         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8770         p = q;\r
8771       }\r
8772     }\r
8773     \r
8774     /* Move any partial line to the start of the buffer */\r
8775     q = is->buf;\r
8776     while (p < is->next) {\r
8777       *q++ = *p++;\r
8778     }\r
8779     is->next = q;\r
8780 \r
8781     if (is->error != NO_ERROR || is->count == 0) {\r
8782       /* Notify backend of the error.  Note: If there was a partial\r
8783          line at the end, it is not flushed through. */\r
8784       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8785     }\r
8786   } else {\r
8787     /* Feed in the whole chunk of input at once */\r
8788     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8789     is->next = is->buf;\r
8790   }\r
8791 }\r
8792 \r
8793 /*---------------------------------------------------------------------------*\\r
8794  *\r
8795  *  Menu enables. Used when setting various modes.\r
8796  *\r
8797 \*---------------------------------------------------------------------------*/\r
8798 \r
8799 typedef struct {\r
8800   int item;\r
8801   int flags;\r
8802 } Enables;\r
8803 \r
8804 VOID\r
8805 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8806 {\r
8807   while (enab->item > 0) {\r
8808     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8809     enab++;\r
8810   }\r
8811 }\r
8812 \r
8813 Enables gnuEnables[] = {\r
8814   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8815   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8816   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8817   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8818   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8819   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8820   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8821   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8822   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8823   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8824   { -1, -1 }\r
8825 };\r
8826 \r
8827 Enables icsEnables[] = {\r
8828   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8829   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8830   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8831   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8832   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8833   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8834   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8835   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8836   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8837   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8838   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8839   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8840   { -1, -1 }\r
8841 };\r
8842 \r
8843 #ifdef ZIPPY\r
8844 Enables zippyEnables[] = {\r
8845   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8846   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8847   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8848   { -1, -1 }\r
8849 };\r
8850 #endif\r
8851 \r
8852 Enables ncpEnables[] = {\r
8853   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8854   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8855   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8856   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8857   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8858   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8859   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8860   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8861   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8862   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8863   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8864   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8865   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8866   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8867   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8868   { -1, -1 }\r
8869 };\r
8870 \r
8871 Enables trainingOnEnables[] = {\r
8872   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8873   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8874   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8875   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8876   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8877   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8878   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8879   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8880   { -1, -1 }\r
8881 };\r
8882 \r
8883 Enables trainingOffEnables[] = {\r
8884   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8885   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8886   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8887   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8888   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8889   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8890   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8891   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8892   { -1, -1 }\r
8893 };\r
8894 \r
8895 /* These modify either ncpEnables or gnuEnables */\r
8896 Enables cmailEnables[] = {\r
8897   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8898   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8899   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8900   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8901   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8902   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8903   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8904   { -1, -1 }\r
8905 };\r
8906 \r
8907 Enables machineThinkingEnables[] = {\r
8908   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8909   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8910   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8911   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8912   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8913   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8914   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8915   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8916   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8917   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8918   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8919   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8920   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8921   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8922   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8923   { -1, -1 }\r
8924 };\r
8925 \r
8926 Enables userThinkingEnables[] = {\r
8927   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8928   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8929   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8930   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8931   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8932   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8933   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8934   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8935   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8936   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8937   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8938   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8939   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8940   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8941   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8942   { -1, -1 }\r
8943 };\r
8944 \r
8945 /*---------------------------------------------------------------------------*\\r
8946  *\r
8947  *  Front-end interface functions exported by XBoard.\r
8948  *  Functions appear in same order as prototypes in frontend.h.\r
8949  * \r
8950 \*---------------------------------------------------------------------------*/\r
8951 VOID\r
8952 ModeHighlight()\r
8953 {\r
8954   static UINT prevChecked = 0;\r
8955   static int prevPausing = 0;\r
8956   UINT nowChecked;\r
8957 \r
8958   if (pausing != prevPausing) {\r
8959     prevPausing = pausing;\r
8960     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8961                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8962     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8963   }\r
8964 \r
8965   switch (gameMode) {\r
8966   case BeginningOfGame:\r
8967     if (appData.icsActive)\r
8968       nowChecked = IDM_IcsClient;\r
8969     else if (appData.noChessProgram)\r
8970       nowChecked = IDM_EditGame;\r
8971     else\r
8972       nowChecked = IDM_MachineBlack;\r
8973     break;\r
8974   case MachinePlaysBlack:\r
8975     nowChecked = IDM_MachineBlack;\r
8976     break;\r
8977   case MachinePlaysWhite:\r
8978     nowChecked = IDM_MachineWhite;\r
8979     break;\r
8980   case TwoMachinesPlay:\r
8981     nowChecked = IDM_TwoMachines;\r
8982     break;\r
8983   case AnalyzeMode:\r
8984     nowChecked = IDM_AnalysisMode;\r
8985     break;\r
8986   case AnalyzeFile:\r
8987     nowChecked = IDM_AnalyzeFile;\r
8988     break;\r
8989   case EditGame:\r
8990     nowChecked = IDM_EditGame;\r
8991     break;\r
8992   case PlayFromGameFile:\r
8993     nowChecked = IDM_LoadGame;\r
8994     break;\r
8995   case EditPosition:\r
8996     nowChecked = IDM_EditPosition;\r
8997     break;\r
8998   case Training:\r
8999     nowChecked = IDM_Training;\r
9000     break;\r
9001   case IcsPlayingWhite:\r
9002   case IcsPlayingBlack:\r
9003   case IcsObserving:\r
9004   case IcsIdle:\r
9005     nowChecked = IDM_IcsClient;\r
9006     break;\r
9007   default:\r
9008   case EndOfGame:\r
9009     nowChecked = 0;\r
9010     break;\r
9011   }\r
9012   if (prevChecked != 0)\r
9013     (void) CheckMenuItem(GetMenu(hwndMain),\r
9014                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
9015   if (nowChecked != 0)\r
9016     (void) CheckMenuItem(GetMenu(hwndMain),\r
9017                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
9018 \r
9019   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
9020     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
9021                           MF_BYCOMMAND|MF_ENABLED);\r
9022   } else {\r
9023     (void) EnableMenuItem(GetMenu(hwndMain), \r
9024                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
9025   }\r
9026 \r
9027   prevChecked = nowChecked;\r
9028 \r
9029   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
9030   if (appData.icsActive) {\r
9031        if (appData.icsEngineAnalyze) {\r
9032                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9033                        MF_BYCOMMAND|MF_CHECKED);\r
9034        } else {\r
9035                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9036                        MF_BYCOMMAND|MF_UNCHECKED);\r
9037        }\r
9038   }\r
9039 }\r
9040 \r
9041 VOID\r
9042 SetICSMode()\r
9043 {\r
9044   HMENU hmenu = GetMenu(hwndMain);\r
9045   SetMenuEnables(hmenu, icsEnables);\r
9046   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
9047     MF_BYPOSITION|MF_ENABLED);\r
9048 #ifdef ZIPPY\r
9049   if (appData.zippyPlay) {\r
9050     SetMenuEnables(hmenu, zippyEnables);\r
9051     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
9052          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9053           MF_BYCOMMAND|MF_ENABLED);\r
9054   }\r
9055 #endif\r
9056 }\r
9057 \r
9058 VOID\r
9059 SetGNUMode()\r
9060 {\r
9061   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
9062 }\r
9063 \r
9064 VOID\r
9065 SetNCPMode()\r
9066 {\r
9067   HMENU hmenu = GetMenu(hwndMain);\r
9068   SetMenuEnables(hmenu, ncpEnables);\r
9069   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
9070     MF_BYPOSITION|MF_GRAYED);\r
9071     DrawMenuBar(hwndMain);\r
9072 }\r
9073 \r
9074 VOID\r
9075 SetCmailMode()\r
9076 {\r
9077   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9078 }\r
9079 \r
9080 VOID \r
9081 SetTrainingModeOn()\r
9082 {\r
9083   int i;\r
9084   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9085   for (i = 0; i < N_BUTTONS; i++) {\r
9086     if (buttonDesc[i].hwnd != NULL)\r
9087       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9088   }\r
9089   CommentPopDown();\r
9090 }\r
9091 \r
9092 VOID SetTrainingModeOff()\r
9093 {\r
9094   int i;\r
9095   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9096   for (i = 0; i < N_BUTTONS; i++) {\r
9097     if (buttonDesc[i].hwnd != NULL)\r
9098       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9099   }\r
9100 }\r
9101 \r
9102 \r
9103 VOID\r
9104 SetUserThinkingEnables()\r
9105 {\r
9106   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9107 }\r
9108 \r
9109 VOID\r
9110 SetMachineThinkingEnables()\r
9111 {\r
9112   HMENU hMenu = GetMenu(hwndMain);\r
9113   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9114 \r
9115   SetMenuEnables(hMenu, machineThinkingEnables);\r
9116 \r
9117   if (gameMode == MachinePlaysBlack) {\r
9118     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9119   } else if (gameMode == MachinePlaysWhite) {\r
9120     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9121   } else if (gameMode == TwoMachinesPlay) {\r
9122     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9123   }\r
9124 }\r
9125 \r
9126 \r
9127 VOID\r
9128 DisplayTitle(char *str)\r
9129 {\r
9130   char title[MSG_SIZ], *host;\r
9131   if (str[0] != NULLCHAR) {\r
9132     strcpy(title, str);\r
9133   } else if (appData.icsActive) {\r
9134     if (appData.icsCommPort[0] != NULLCHAR)\r
9135       host = "ICS";\r
9136     else \r
9137       host = appData.icsHost;\r
9138     sprintf(title, "%s: %s", szTitle, host);\r
9139   } else if (appData.noChessProgram) {\r
9140     strcpy(title, szTitle);\r
9141   } else {\r
9142     strcpy(title, szTitle);\r
9143     strcat(title, ": ");\r
9144     strcat(title, first.tidy);\r
9145   }\r
9146   SetWindowText(hwndMain, title);\r
9147 }\r
9148 \r
9149 \r
9150 VOID\r
9151 DisplayMessage(char *str1, char *str2)\r
9152 {\r
9153   HDC hdc;\r
9154   HFONT oldFont;\r
9155   int remain = MESSAGE_TEXT_MAX - 1;\r
9156   int len;\r
9157 \r
9158   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9159   messageText[0] = NULLCHAR;\r
9160   if (*str1) {\r
9161     len = strlen(str1);\r
9162     if (len > remain) len = remain;\r
9163     strncpy(messageText, str1, len);\r
9164     messageText[len] = NULLCHAR;\r
9165     remain -= len;\r
9166   }\r
9167   if (*str2 && remain >= 2) {\r
9168     if (*str1) {\r
9169       strcat(messageText, "  ");\r
9170       remain -= 2;\r
9171     }\r
9172     len = strlen(str2);\r
9173     if (len > remain) len = remain;\r
9174     strncat(messageText, str2, len);\r
9175   }\r
9176   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9177 \r
9178   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9179 \r
9180   SAYMACHINEMOVE();\r
9181 \r
9182   hdc = GetDC(hwndMain);\r
9183   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9184   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9185              &messageRect, messageText, strlen(messageText), NULL);\r
9186   (void) SelectObject(hdc, oldFont);\r
9187   (void) ReleaseDC(hwndMain, hdc);\r
9188 }\r
9189 \r
9190 VOID\r
9191 DisplayError(char *str, int error)\r
9192 {\r
9193   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9194   int len;\r
9195 \r
9196   if (error == 0) {\r
9197     strcpy(buf, str);\r
9198   } else {\r
9199     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9200                         NULL, error, LANG_NEUTRAL,\r
9201                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9202     if (len > 0) {\r
9203       sprintf(buf, "%s:\n%s", str, buf2);\r
9204     } else {\r
9205       ErrorMap *em = errmap;\r
9206       while (em->err != 0 && em->err != error) em++;\r
9207       if (em->err != 0) {\r
9208         sprintf(buf, "%s:\n%s", str, em->msg);\r
9209       } else {\r
9210         sprintf(buf, "%s:\nError code %d", str, error);\r
9211       }\r
9212     }\r
9213   }\r
9214   \r
9215   ErrorPopUp("Error", buf);\r
9216 }\r
9217 \r
9218 \r
9219 VOID\r
9220 DisplayMoveError(char *str)\r
9221 {\r
9222   fromX = fromY = -1;\r
9223   ClearHighlights();\r
9224   DrawPosition(FALSE, NULL);\r
9225   if (appData.popupMoveErrors) {\r
9226     ErrorPopUp("Error", str);\r
9227   } else {\r
9228     DisplayMessage(str, "");\r
9229     moveErrorMessageUp = TRUE;\r
9230   }\r
9231 }\r
9232 \r
9233 VOID\r
9234 DisplayFatalError(char *str, int error, int exitStatus)\r
9235 {\r
9236   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9237   int len;\r
9238   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9239 \r
9240   if (error != 0) {\r
9241     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9242                         NULL, error, LANG_NEUTRAL,\r
9243                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9244     if (len > 0) {\r
9245       sprintf(buf, "%s:\n%s", str, buf2);\r
9246     } else {\r
9247       ErrorMap *em = errmap;\r
9248       while (em->err != 0 && em->err != error) em++;\r
9249       if (em->err != 0) {\r
9250         sprintf(buf, "%s:\n%s", str, em->msg);\r
9251       } else {\r
9252         sprintf(buf, "%s:\nError code %d", str, error);\r
9253       }\r
9254     }\r
9255     str = buf;\r
9256   }\r
9257   if (appData.debugMode) {\r
9258     fprintf(debugFP, "%s: %s\n", label, str);\r
9259   }\r
9260   if (appData.popupExitMessage) {\r
9261     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9262                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9263   }\r
9264   ExitEvent(exitStatus);\r
9265 }\r
9266 \r
9267 \r
9268 VOID\r
9269 DisplayInformation(char *str)\r
9270 {\r
9271   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9272 }\r
9273 \r
9274 \r
9275 VOID\r
9276 DisplayNote(char *str)\r
9277 {\r
9278   ErrorPopUp("Note", str);\r
9279 }\r
9280 \r
9281 \r
9282 typedef struct {\r
9283   char *title, *question, *replyPrefix;\r
9284   ProcRef pr;\r
9285 } QuestionParams;\r
9286 \r
9287 LRESULT CALLBACK\r
9288 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9289 {\r
9290   static QuestionParams *qp;\r
9291   char reply[MSG_SIZ];\r
9292   int len, err;\r
9293 \r
9294   switch (message) {\r
9295   case WM_INITDIALOG:\r
9296     qp = (QuestionParams *) lParam;\r
9297     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9298     SetWindowText(hDlg, qp->title);\r
9299     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9300     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9301     return FALSE;\r
9302 \r
9303   case WM_COMMAND:\r
9304     switch (LOWORD(wParam)) {\r
9305     case IDOK:\r
9306       strcpy(reply, qp->replyPrefix);\r
9307       if (*reply) strcat(reply, " ");\r
9308       len = strlen(reply);\r
9309       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9310       strcat(reply, "\n");\r
9311       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9312       EndDialog(hDlg, TRUE);\r
9313       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9314       return TRUE;\r
9315     case IDCANCEL:\r
9316       EndDialog(hDlg, FALSE);\r
9317       return TRUE;\r
9318     default:\r
9319       break;\r
9320     }\r
9321     break;\r
9322   }\r
9323   return FALSE;\r
9324 }\r
9325 \r
9326 VOID\r
9327 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9328 {\r
9329     QuestionParams qp;\r
9330     FARPROC lpProc;\r
9331     \r
9332     qp.title = title;\r
9333     qp.question = question;\r
9334     qp.replyPrefix = replyPrefix;\r
9335     qp.pr = pr;\r
9336     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9337     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9338       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9339     FreeProcInstance(lpProc);\r
9340 }\r
9341 \r
9342 /* [AS] Pick FRC position */\r
9343 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9344 {\r
9345     static int * lpIndexFRC;\r
9346     BOOL index_is_ok;\r
9347     char buf[16];\r
9348 \r
9349     switch( message )\r
9350     {\r
9351     case WM_INITDIALOG:\r
9352         lpIndexFRC = (int *) lParam;\r
9353 \r
9354         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9355 \r
9356         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9357         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9358         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9359         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9360 \r
9361         break;\r
9362 \r
9363     case WM_COMMAND:\r
9364         switch( LOWORD(wParam) ) {\r
9365         case IDOK:\r
9366             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9367             EndDialog( hDlg, 0 );\r
9368             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9369             return TRUE;\r
9370         case IDCANCEL:\r
9371             EndDialog( hDlg, 1 );   \r
9372             return TRUE;\r
9373         case IDC_NFG_Edit:\r
9374             if( HIWORD(wParam) == EN_CHANGE ) {\r
9375                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9376 \r
9377                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9378             }\r
9379             return TRUE;\r
9380         case IDC_NFG_Random:\r
9381             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9382             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9383             return TRUE;\r
9384         }\r
9385 \r
9386         break;\r
9387     }\r
9388 \r
9389     return FALSE;\r
9390 }\r
9391 \r
9392 int NewGameFRC()\r
9393 {\r
9394     int result;\r
9395     int index = appData.defaultFrcPosition;\r
9396     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9397 \r
9398     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9399 \r
9400     if( result == 0 ) {\r
9401         appData.defaultFrcPosition = index;\r
9402     }\r
9403 \r
9404     return result;\r
9405 }\r
9406 \r
9407 /* [AS] Game list options */\r
9408 typedef struct {\r
9409     char id;\r
9410     char * name;\r
9411 } GLT_Item;\r
9412 \r
9413 static GLT_Item GLT_ItemInfo[] = {\r
9414     { GLT_EVENT,      "Event" },\r
9415     { GLT_SITE,       "Site" },\r
9416     { GLT_DATE,       "Date" },\r
9417     { GLT_ROUND,      "Round" },\r
9418     { GLT_PLAYERS,    "Players" },\r
9419     { GLT_RESULT,     "Result" },\r
9420     { GLT_WHITE_ELO,  "White Rating" },\r
9421     { GLT_BLACK_ELO,  "Black Rating" },\r
9422     { GLT_TIME_CONTROL,"Time Control" },\r
9423     { GLT_VARIANT,    "Variant" },\r
9424     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9425     { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
9426     { 0, 0 }\r
9427 };\r
9428 \r
9429 const char * GLT_FindItem( char id )\r
9430 {\r
9431     const char * result = 0;\r
9432 \r
9433     GLT_Item * list = GLT_ItemInfo;\r
9434 \r
9435     while( list->id != 0 ) {\r
9436         if( list->id == id ) {\r
9437             result = list->name;\r
9438             break;\r
9439         }\r
9440 \r
9441         list++;\r
9442     }\r
9443 \r
9444     return result;\r
9445 }\r
9446 \r
9447 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9448 {\r
9449     const char * name = GLT_FindItem( id );\r
9450 \r
9451     if( name != 0 ) {\r
9452         if( index >= 0 ) {\r
9453             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9454         }\r
9455         else {\r
9456             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9457         }\r
9458     }\r
9459 }\r
9460 \r
9461 void GLT_TagsToList( HWND hDlg, char * tags )\r
9462 {\r
9463     char * pc = tags;\r
9464 \r
9465     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9466 \r
9467     while( *pc ) {\r
9468         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9469         pc++;\r
9470     }\r
9471 \r
9472     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9473 \r
9474     pc = GLT_ALL_TAGS;\r
9475 \r
9476     while( *pc ) {\r
9477         if( strchr( tags, *pc ) == 0 ) {\r
9478             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9479         }\r
9480         pc++;\r
9481     }\r
9482 \r
9483     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9484 }\r
9485 \r
9486 char GLT_ListItemToTag( HWND hDlg, int index )\r
9487 {\r
9488     char result = '\0';\r
9489     char name[128];\r
9490 \r
9491     GLT_Item * list = GLT_ItemInfo;\r
9492 \r
9493     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9494         while( list->id != 0 ) {\r
9495             if( strcmp( list->name, name ) == 0 ) {\r
9496                 result = list->id;\r
9497                 break;\r
9498             }\r
9499 \r
9500             list++;\r
9501         }\r
9502     }\r
9503 \r
9504     return result;\r
9505 }\r
9506 \r
9507 void GLT_MoveSelection( HWND hDlg, int delta )\r
9508 {\r
9509     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9510     int idx2 = idx1 + delta;\r
9511     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9512 \r
9513     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9514         char buf[128];\r
9515 \r
9516         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9517         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9518         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9519         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9520     }\r
9521 }\r
9522 \r
9523 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9524 {\r
9525     static char glt[64];\r
9526     static char * lpUserGLT;\r
9527 \r
9528     switch( message )\r
9529     {\r
9530     case WM_INITDIALOG:\r
9531         lpUserGLT = (char *) lParam;\r
9532         \r
9533         strcpy( glt, lpUserGLT );\r
9534 \r
9535         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9536 \r
9537         /* Initialize list */\r
9538         GLT_TagsToList( hDlg, glt );\r
9539 \r
9540         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9541 \r
9542         break;\r
9543 \r
9544     case WM_COMMAND:\r
9545         switch( LOWORD(wParam) ) {\r
9546         case IDOK:\r
9547             {\r
9548                 char * pc = lpUserGLT;\r
9549                 int idx = 0;\r
9550 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9551                 char id;\r
9552 \r
9553                 do {\r
9554                     id = GLT_ListItemToTag( hDlg, idx );\r
9555 \r
9556                     *pc++ = id;\r
9557                     idx++;\r
9558                 } while( id != '\0' );\r
9559             }\r
9560             EndDialog( hDlg, 0 );\r
9561             return TRUE;\r
9562         case IDCANCEL:\r
9563             EndDialog( hDlg, 1 );\r
9564             return TRUE;\r
9565 \r
9566         case IDC_GLT_Default:\r
9567             strcpy( glt, GLT_DEFAULT_TAGS );\r
9568             GLT_TagsToList( hDlg, glt );\r
9569             return TRUE;\r
9570 \r
9571         case IDC_GLT_Restore:\r
9572             strcpy( glt, lpUserGLT );\r
9573             GLT_TagsToList( hDlg, glt );\r
9574             return TRUE;\r
9575 \r
9576         case IDC_GLT_Up:\r
9577             GLT_MoveSelection( hDlg, -1 );\r
9578             return TRUE;\r
9579 \r
9580         case IDC_GLT_Down:\r
9581             GLT_MoveSelection( hDlg, +1 );\r
9582             return TRUE;\r
9583         }\r
9584 \r
9585         break;\r
9586     }\r
9587 \r
9588     return FALSE;\r
9589 }\r
9590 \r
9591 int GameListOptions()\r
9592 {\r
9593     char glt[64];\r
9594     int result;\r
9595     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9596 \r
9597     strcpy( glt, appData.gameListTags );\r
9598 \r
9599     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9600 \r
9601     if( result == 0 ) {\r
9602         /* [AS] Memory leak here! */\r
9603         appData.gameListTags = strdup( glt ); \r
9604     }\r
9605 \r
9606     return result;\r
9607 }\r
9608 \r
9609 \r
9610 VOID\r
9611 DisplayIcsInteractionTitle(char *str)\r
9612 {\r
9613   char consoleTitle[MSG_SIZ];\r
9614 \r
9615   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9616   SetWindowText(hwndConsole, consoleTitle);\r
9617 }\r
9618 \r
9619 void\r
9620 DrawPosition(int fullRedraw, Board board)\r
9621 {\r
9622   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9623 }\r
9624 \r
9625 \r
9626 VOID\r
9627 ResetFrontEnd()\r
9628 {\r
9629   fromX = fromY = -1;\r
9630   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9631     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9632     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9633     dragInfo.lastpos = dragInfo.pos;\r
9634     dragInfo.start.x = dragInfo.start.y = -1;\r
9635     dragInfo.from = dragInfo.start;\r
9636     ReleaseCapture();\r
9637     DrawPosition(TRUE, NULL);\r
9638   }\r
9639 }\r
9640 \r
9641 \r
9642 VOID\r
9643 CommentPopUp(char *title, char *str)\r
9644 {\r
9645   HWND hwnd = GetActiveWindow();\r
9646   EitherCommentPopUp(0, title, str, FALSE);\r
9647   SetActiveWindow(hwnd);\r
9648 }\r
9649 \r
9650 VOID\r
9651 CommentPopDown(void)\r
9652 {\r
9653   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9654   if (commentDialog) {\r
9655     ShowWindow(commentDialog, SW_HIDE);\r
9656   }\r
9657   commentDialogUp = FALSE;\r
9658 }\r
9659 \r
9660 VOID\r
9661 EditCommentPopUp(int index, char *title, char *str)\r
9662 {\r
9663   EitherCommentPopUp(index, title, str, TRUE);\r
9664 }\r
9665 \r
9666 \r
9667 VOID\r
9668 RingBell()\r
9669 {\r
9670   MyPlaySound(&sounds[(int)SoundMove]);\r
9671 }\r
9672 \r
9673 VOID PlayIcsWinSound()\r
9674 {\r
9675   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9676 }\r
9677 \r
9678 VOID PlayIcsLossSound()\r
9679 {\r
9680   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9681 }\r
9682 \r
9683 VOID PlayIcsDrawSound()\r
9684 {\r
9685   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9686 }\r
9687 \r
9688 VOID PlayIcsUnfinishedSound()\r
9689 {\r
9690   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9691 }\r
9692 \r
9693 VOID\r
9694 PlayAlarmSound()\r
9695 {\r
9696   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9697 }\r
9698 \r
9699 \r
9700 VOID\r
9701 EchoOn()\r
9702 {\r
9703   HWND hInput;\r
9704   consoleEcho = TRUE;\r
9705   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9706   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9707   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9708 }\r
9709 \r
9710 \r
9711 VOID\r
9712 EchoOff()\r
9713 {\r
9714   CHARFORMAT cf;\r
9715   HWND hInput;\r
9716   consoleEcho = FALSE;\r
9717   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9718   /* This works OK: set text and background both to the same color */\r
9719   cf = consoleCF;\r
9720   cf.crTextColor = COLOR_ECHOOFF;\r
9721   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9722   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9723 }\r
9724 \r
9725 /* No Raw()...? */\r
9726 \r
9727 void Colorize(ColorClass cc, int continuation)\r
9728 {\r
9729   currentColorClass = cc;\r
9730   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9731   consoleCF.crTextColor = textAttribs[cc].color;\r
9732   consoleCF.dwEffects = textAttribs[cc].effects;\r
9733   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9734 }\r
9735 \r
9736 char *\r
9737 UserName()\r
9738 {\r
9739   static char buf[MSG_SIZ];\r
9740   DWORD bufsiz = MSG_SIZ;\r
9741 \r
9742   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9743         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9744   }\r
9745   if (!GetUserName(buf, &bufsiz)) {\r
9746     /*DisplayError("Error getting user name", GetLastError());*/\r
9747     strcpy(buf, "User");\r
9748   }\r
9749   return buf;\r
9750 }\r
9751 \r
9752 char *\r
9753 HostName()\r
9754 {\r
9755   static char buf[MSG_SIZ];\r
9756   DWORD bufsiz = MSG_SIZ;\r
9757 \r
9758   if (!GetComputerName(buf, &bufsiz)) {\r
9759     /*DisplayError("Error getting host name", GetLastError());*/\r
9760     strcpy(buf, "Unknown");\r
9761   }\r
9762   return buf;\r
9763 }\r
9764 \r
9765 \r
9766 int\r
9767 ClockTimerRunning()\r
9768 {\r
9769   return clockTimerEvent != 0;\r
9770 }\r
9771 \r
9772 int\r
9773 StopClockTimer()\r
9774 {\r
9775   if (clockTimerEvent == 0) return FALSE;\r
9776   KillTimer(hwndMain, clockTimerEvent);\r
9777   clockTimerEvent = 0;\r
9778   return TRUE;\r
9779 }\r
9780 \r
9781 void\r
9782 StartClockTimer(long millisec)\r
9783 {\r
9784   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9785                              (UINT) millisec, NULL);\r
9786 }\r
9787 \r
9788 void\r
9789 DisplayWhiteClock(long timeRemaining, int highlight)\r
9790 {\r
9791   HDC hdc;\r
9792   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9793 \r
9794   if(appData.noGUI) return;\r
9795   hdc = GetDC(hwndMain);\r
9796   if (!IsIconic(hwndMain)) {\r
9797     DisplayAClock(hdc, timeRemaining, highlight, \r
9798                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9799   }\r
9800   if (highlight && iconCurrent == iconBlack) {\r
9801     iconCurrent = iconWhite;\r
9802     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9803     if (IsIconic(hwndMain)) {\r
9804       DrawIcon(hdc, 2, 2, iconCurrent);\r
9805     }\r
9806   }\r
9807   (void) ReleaseDC(hwndMain, hdc);\r
9808   if (hwndConsole)\r
9809     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9810 }\r
9811 \r
9812 void\r
9813 DisplayBlackClock(long timeRemaining, int highlight)\r
9814 {\r
9815   HDC hdc;\r
9816   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9817 \r
9818   if(appData.noGUI) return;\r
9819   hdc = GetDC(hwndMain);\r
9820   if (!IsIconic(hwndMain)) {\r
9821     DisplayAClock(hdc, timeRemaining, highlight, \r
9822                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9823   }\r
9824   if (highlight && iconCurrent == iconWhite) {\r
9825     iconCurrent = iconBlack;\r
9826     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9827     if (IsIconic(hwndMain)) {\r
9828       DrawIcon(hdc, 2, 2, iconCurrent);\r
9829     }\r
9830   }\r
9831   (void) ReleaseDC(hwndMain, hdc);\r
9832   if (hwndConsole)\r
9833     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9834 }\r
9835 \r
9836 \r
9837 int\r
9838 LoadGameTimerRunning()\r
9839 {\r
9840   return loadGameTimerEvent != 0;\r
9841 }\r
9842 \r
9843 int\r
9844 StopLoadGameTimer()\r
9845 {\r
9846   if (loadGameTimerEvent == 0) return FALSE;\r
9847   KillTimer(hwndMain, loadGameTimerEvent);\r
9848   loadGameTimerEvent = 0;\r
9849   return TRUE;\r
9850 }\r
9851 \r
9852 void\r
9853 StartLoadGameTimer(long millisec)\r
9854 {\r
9855   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9856                                 (UINT) millisec, NULL);\r
9857 }\r
9858 \r
9859 void\r
9860 AutoSaveGame()\r
9861 {\r
9862   char *defName;\r
9863   FILE *f;\r
9864   char fileTitle[MSG_SIZ];\r
9865 \r
9866   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9867   f = OpenFileDialog(hwndMain, "a", defName,\r
9868                      appData.oldSaveStyle ? "gam" : "pgn",\r
9869                      GAME_FILT, \r
9870                      "Save Game to File", NULL, fileTitle, NULL);\r
9871   if (f != NULL) {\r
9872     SaveGame(f, 0, "");\r
9873     fclose(f);\r
9874   }\r
9875 }\r
9876 \r
9877 \r
9878 void\r
9879 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9880 {\r
9881   if (delayedTimerEvent != 0) {\r
9882     if (appData.debugMode && cb != delayedTimerCallback) { // [HGM] alive: not too much debug\r
9883       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9884     }\r
9885     KillTimer(hwndMain, delayedTimerEvent);\r
9886     delayedTimerEvent = 0;\r
9887     if(delayedTimerCallback != cb) // [HGM] alive: do not "flush" same event, just postpone it\r
9888     delayedTimerCallback();\r
9889   }\r
9890   delayedTimerCallback = cb;\r
9891   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9892                                 (UINT) millisec, NULL);\r
9893 }\r
9894 \r
9895 DelayedEventCallback\r
9896 GetDelayedEvent()\r
9897 {\r
9898   if (delayedTimerEvent) {\r
9899     return delayedTimerCallback;\r
9900   } else {\r
9901     return NULL;\r
9902   }\r
9903 }\r
9904 \r
9905 void\r
9906 CancelDelayedEvent()\r
9907 {\r
9908   if (delayedTimerEvent) {\r
9909     KillTimer(hwndMain, delayedTimerEvent);\r
9910     delayedTimerEvent = 0;\r
9911   }\r
9912 }\r
9913 \r
9914 DWORD GetWin32Priority(int nice)\r
9915 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9916 /*\r
9917 REALTIME_PRIORITY_CLASS     0x00000100\r
9918 HIGH_PRIORITY_CLASS         0x00000080\r
9919 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9920 NORMAL_PRIORITY_CLASS       0x00000020\r
9921 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9922 IDLE_PRIORITY_CLASS         0x00000040\r
9923 */\r
9924         if (nice < -15) return 0x00000080;\r
9925         if (nice < 0)   return 0x00008000;\r
9926         if (nice == 0)  return 0x00000020;\r
9927         if (nice < 15)  return 0x00004000;\r
9928         return 0x00000040;\r
9929 }\r
9930 \r
9931 /* Start a child process running the given program.\r
9932    The process's standard output can be read from "from", and its\r
9933    standard input can be written to "to".\r
9934    Exit with fatal error if anything goes wrong.\r
9935    Returns an opaque pointer that can be used to destroy the process\r
9936    later.\r
9937 */\r
9938 int\r
9939 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9940 {\r
9941 #define BUFSIZE 4096\r
9942 \r
9943   HANDLE hChildStdinRd, hChildStdinWr,\r
9944     hChildStdoutRd, hChildStdoutWr;\r
9945   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9946   SECURITY_ATTRIBUTES saAttr;\r
9947   BOOL fSuccess;\r
9948   PROCESS_INFORMATION piProcInfo;\r
9949   STARTUPINFO siStartInfo;\r
9950   ChildProc *cp;\r
9951   char buf[MSG_SIZ];\r
9952   DWORD err;\r
9953 \r
9954   if (appData.debugMode) {\r
9955     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9956   }\r
9957 \r
9958   *pr = NoProc;\r
9959 \r
9960   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9961   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9962   saAttr.bInheritHandle = TRUE;\r
9963   saAttr.lpSecurityDescriptor = NULL;\r
9964 \r
9965   /*\r
9966    * The steps for redirecting child's STDOUT:\r
9967    *     1. Create anonymous pipe to be STDOUT for child.\r
9968    *     2. Create a noninheritable duplicate of read handle,\r
9969    *         and close the inheritable read handle.\r
9970    */\r
9971 \r
9972   /* Create a pipe for the child's STDOUT. */\r
9973   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9974     return GetLastError();\r
9975   }\r
9976 \r
9977   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9978   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9979                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9980                              FALSE,     /* not inherited */\r
9981                              DUPLICATE_SAME_ACCESS);\r
9982   if (! fSuccess) {\r
9983     return GetLastError();\r
9984   }\r
9985   CloseHandle(hChildStdoutRd);\r
9986 \r
9987   /*\r
9988    * The steps for redirecting child's STDIN:\r
9989    *     1. Create anonymous pipe to be STDIN for child.\r
9990    *     2. Create a noninheritable duplicate of write handle,\r
9991    *         and close the inheritable write handle.\r
9992    */\r
9993 \r
9994   /* Create a pipe for the child's STDIN. */\r
9995   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9996     return GetLastError();\r
9997   }\r
9998 \r
9999   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
10000   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
10001                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
10002                              FALSE,     /* not inherited */\r
10003                              DUPLICATE_SAME_ACCESS);\r
10004   if (! fSuccess) {\r
10005     return GetLastError();\r
10006   }\r
10007   CloseHandle(hChildStdinWr);\r
10008 \r
10009   /* Arrange to (1) look in dir for the child .exe file, and\r
10010    * (2) have dir be the child's working directory.  Interpret\r
10011    * dir relative to the directory WinBoard loaded from. */\r
10012   GetCurrentDirectory(MSG_SIZ, buf);\r
10013   SetCurrentDirectory(installDir);\r
10014   SetCurrentDirectory(dir);\r
10015 \r
10016   /* Now create the child process. */\r
10017 \r
10018   siStartInfo.cb = sizeof(STARTUPINFO);\r
10019   siStartInfo.lpReserved = NULL;\r
10020   siStartInfo.lpDesktop = NULL;\r
10021   siStartInfo.lpTitle = NULL;\r
10022   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
10023   siStartInfo.cbReserved2 = 0;\r
10024   siStartInfo.lpReserved2 = NULL;\r
10025   siStartInfo.hStdInput = hChildStdinRd;\r
10026   siStartInfo.hStdOutput = hChildStdoutWr;\r
10027   siStartInfo.hStdError = hChildStdoutWr;\r
10028 \r
10029   fSuccess = CreateProcess(NULL,\r
10030                            cmdLine,        /* command line */\r
10031                            NULL,           /* process security attributes */\r
10032                            NULL,           /* primary thread security attrs */\r
10033                            TRUE,           /* handles are inherited */\r
10034                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
10035                            NULL,           /* use parent's environment */\r
10036                            NULL,\r
10037                            &siStartInfo, /* STARTUPINFO pointer */\r
10038                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
10039 \r
10040   err = GetLastError();\r
10041   SetCurrentDirectory(buf); /* return to prev directory */\r
10042   if (! fSuccess) {\r
10043     return err;\r
10044   }\r
10045 \r
10046   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
10047     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
10048     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
10049   }\r
10050 \r
10051   /* Close the handles we don't need in the parent */\r
10052   CloseHandle(piProcInfo.hThread);\r
10053   CloseHandle(hChildStdinRd);\r
10054   CloseHandle(hChildStdoutWr);\r
10055 \r
10056   /* Prepare return value */\r
10057   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10058   cp->kind = CPReal;\r
10059   cp->hProcess = piProcInfo.hProcess;\r
10060   cp->pid = piProcInfo.dwProcessId;\r
10061   cp->hFrom = hChildStdoutRdDup;\r
10062   cp->hTo = hChildStdinWrDup;\r
10063 \r
10064   *pr = (void *) cp;\r
10065 \r
10066   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10067      2000 where engines sometimes don't see the initial command(s)\r
10068      from WinBoard and hang.  I don't understand how that can happen,\r
10069      but the Sleep is harmless, so I've put it in.  Others have also\r
10070      reported what may be the same problem, so hopefully this will fix\r
10071      it for them too.  */\r
10072   Sleep(500);\r
10073 \r
10074   return NO_ERROR;\r
10075 }\r
10076 \r
10077 \r
10078 void\r
10079 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10080 {\r
10081   ChildProc *cp; int result;\r
10082 \r
10083   cp = (ChildProc *) pr;\r
10084   if (cp == NULL) return;\r
10085 \r
10086   switch (cp->kind) {\r
10087   case CPReal:\r
10088     /* TerminateProcess is considered harmful, so... */\r
10089     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10090     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10091     /* The following doesn't work because the chess program\r
10092        doesn't "have the same console" as WinBoard.  Maybe\r
10093        we could arrange for this even though neither WinBoard\r
10094        nor the chess program uses a console for stdio? */\r
10095     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10096 \r
10097     /* [AS] Special termination modes for misbehaving programs... */\r
10098     if( signal == 9 ) { \r
10099         result = TerminateProcess( cp->hProcess, 0 );\r
10100 \r
10101         if ( appData.debugMode) {\r
10102             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10103         }\r
10104     }\r
10105     else if( signal == 10 ) {\r
10106         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10107 \r
10108         if( dw != WAIT_OBJECT_0 ) {\r
10109             result = TerminateProcess( cp->hProcess, 0 );\r
10110 \r
10111             if ( appData.debugMode) {\r
10112                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10113             }\r
10114 \r
10115         }\r
10116     }\r
10117 \r
10118     CloseHandle(cp->hProcess);\r
10119     break;\r
10120 \r
10121   case CPComm:\r
10122     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10123     break;\r
10124 \r
10125   case CPSock:\r
10126     closesocket(cp->sock);\r
10127     WSACleanup();\r
10128     break;\r
10129 \r
10130   case CPRcmd:\r
10131     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10132     closesocket(cp->sock);\r
10133     closesocket(cp->sock2);\r
10134     WSACleanup();\r
10135     break;\r
10136   }\r
10137   free(cp);\r
10138 }\r
10139 \r
10140 void\r
10141 InterruptChildProcess(ProcRef pr)\r
10142 {\r
10143   ChildProc *cp;\r
10144 \r
10145   cp = (ChildProc *) pr;\r
10146   if (cp == NULL) return;\r
10147   switch (cp->kind) {\r
10148   case CPReal:\r
10149     /* The following doesn't work because the chess program\r
10150        doesn't "have the same console" as WinBoard.  Maybe\r
10151        we could arrange for this even though neither WinBoard\r
10152        nor the chess program uses a console for stdio */\r
10153     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10154     break;\r
10155 \r
10156   case CPComm:\r
10157   case CPSock:\r
10158     /* Can't interrupt */\r
10159     break;\r
10160 \r
10161   case CPRcmd:\r
10162     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10163     break;\r
10164   }\r
10165 }\r
10166 \r
10167 \r
10168 int\r
10169 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10170 {\r
10171   char cmdLine[MSG_SIZ];\r
10172 \r
10173   if (port[0] == NULLCHAR) {\r
10174     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10175   } else {\r
10176     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10177   }\r
10178   return StartChildProcess(cmdLine, "", pr);\r
10179 }\r
10180 \r
10181 \r
10182 /* Code to open TCP sockets */\r
10183 \r
10184 int\r
10185 OpenTCP(char *host, char *port, ProcRef *pr)\r
10186 {\r
10187   ChildProc *cp;\r
10188   int err;\r
10189   SOCKET s;\r
10190   struct sockaddr_in sa, mysa;\r
10191   struct hostent FAR *hp;\r
10192   unsigned short uport;\r
10193   WORD wVersionRequested;\r
10194   WSADATA wsaData;\r
10195 \r
10196   /* Initialize socket DLL */\r
10197   wVersionRequested = MAKEWORD(1, 1);\r
10198   err = WSAStartup(wVersionRequested, &wsaData);\r
10199   if (err != 0) return err;\r
10200 \r
10201   /* Make socket */\r
10202   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10203     err = WSAGetLastError();\r
10204     WSACleanup();\r
10205     return err;\r
10206   }\r
10207 \r
10208   /* Bind local address using (mostly) don't-care values.\r
10209    */\r
10210   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10211   mysa.sin_family = AF_INET;\r
10212   mysa.sin_addr.s_addr = INADDR_ANY;\r
10213   uport = (unsigned short) 0;\r
10214   mysa.sin_port = htons(uport);\r
10215   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10216       == SOCKET_ERROR) {\r
10217     err = WSAGetLastError();\r
10218     WSACleanup();\r
10219     return err;\r
10220   }\r
10221 \r
10222   /* Resolve remote host name */\r
10223   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10224   if (!(hp = gethostbyname(host))) {\r
10225     unsigned int b0, b1, b2, b3;\r
10226 \r
10227     err = WSAGetLastError();\r
10228 \r
10229     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10230       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10231       hp->h_addrtype = AF_INET;\r
10232       hp->h_length = 4;\r
10233       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10234       hp->h_addr_list[0] = (char *) malloc(4);\r
10235       hp->h_addr_list[0][0] = (char) b0;\r
10236       hp->h_addr_list[0][1] = (char) b1;\r
10237       hp->h_addr_list[0][2] = (char) b2;\r
10238       hp->h_addr_list[0][3] = (char) b3;\r
10239     } else {\r
10240       WSACleanup();\r
10241       return err;\r
10242     }\r
10243   }\r
10244   sa.sin_family = hp->h_addrtype;\r
10245   uport = (unsigned short) atoi(port);\r
10246   sa.sin_port = htons(uport);\r
10247   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10248 \r
10249   /* Make connection */\r
10250   if (connect(s, (struct sockaddr *) &sa,\r
10251               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10252     err = WSAGetLastError();\r
10253     WSACleanup();\r
10254     return err;\r
10255   }\r
10256 \r
10257   /* Prepare return value */\r
10258   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10259   cp->kind = CPSock;\r
10260   cp->sock = s;\r
10261   *pr = (ProcRef *) cp;\r
10262 \r
10263   return NO_ERROR;\r
10264 }\r
10265 \r
10266 int\r
10267 OpenCommPort(char *name, ProcRef *pr)\r
10268 {\r
10269   HANDLE h;\r
10270   COMMTIMEOUTS ct;\r
10271   ChildProc *cp;\r
10272   char fullname[MSG_SIZ];\r
10273 \r
10274   if (*name != '\\')\r
10275     sprintf(fullname, "\\\\.\\%s", name);\r
10276   else\r
10277     strcpy(fullname, name);\r
10278 \r
10279   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10280                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10281   if (h == (HANDLE) -1) {\r
10282     return GetLastError();\r
10283   }\r
10284   hCommPort = h;\r
10285 \r
10286   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10287 \r
10288   /* Accumulate characters until a 100ms pause, then parse */\r
10289   ct.ReadIntervalTimeout = 100;\r
10290   ct.ReadTotalTimeoutMultiplier = 0;\r
10291   ct.ReadTotalTimeoutConstant = 0;\r
10292   ct.WriteTotalTimeoutMultiplier = 0;\r
10293   ct.WriteTotalTimeoutConstant = 0;\r
10294   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10295 \r
10296   /* Prepare return value */\r
10297   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10298   cp->kind = CPComm;\r
10299   cp->hFrom = h;\r
10300   cp->hTo = h;\r
10301   *pr = (ProcRef *) cp;\r
10302 \r
10303   return NO_ERROR;\r
10304 }\r
10305 \r
10306 int\r
10307 OpenLoopback(ProcRef *pr)\r
10308 {\r
10309   DisplayFatalError("Not implemented", 0, 1);\r
10310   return NO_ERROR;\r
10311 }\r
10312 \r
10313 \r
10314 int\r
10315 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10316 {\r
10317   ChildProc *cp;\r
10318   int err;\r
10319   SOCKET s, s2, s3;\r
10320   struct sockaddr_in sa, mysa;\r
10321   struct hostent FAR *hp;\r
10322   unsigned short uport;\r
10323   WORD wVersionRequested;\r
10324   WSADATA wsaData;\r
10325   int fromPort;\r
10326   char stderrPortStr[MSG_SIZ];\r
10327 \r
10328   /* Initialize socket DLL */\r
10329   wVersionRequested = MAKEWORD(1, 1);\r
10330   err = WSAStartup(wVersionRequested, &wsaData);\r
10331   if (err != 0) return err;\r
10332 \r
10333   /* Resolve remote host name */\r
10334   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10335   if (!(hp = gethostbyname(host))) {\r
10336     unsigned int b0, b1, b2, b3;\r
10337 \r
10338     err = WSAGetLastError();\r
10339 \r
10340     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10341       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10342       hp->h_addrtype = AF_INET;\r
10343       hp->h_length = 4;\r
10344       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10345       hp->h_addr_list[0] = (char *) malloc(4);\r
10346       hp->h_addr_list[0][0] = (char) b0;\r
10347       hp->h_addr_list[0][1] = (char) b1;\r
10348       hp->h_addr_list[0][2] = (char) b2;\r
10349       hp->h_addr_list[0][3] = (char) b3;\r
10350     } else {\r
10351       WSACleanup();\r
10352       return err;\r
10353     }\r
10354   }\r
10355   sa.sin_family = hp->h_addrtype;\r
10356   uport = (unsigned short) 514;\r
10357   sa.sin_port = htons(uport);\r
10358   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10359 \r
10360   /* Bind local socket to unused "privileged" port address\r
10361    */\r
10362   s = INVALID_SOCKET;\r
10363   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10364   mysa.sin_family = AF_INET;\r
10365   mysa.sin_addr.s_addr = INADDR_ANY;\r
10366   for (fromPort = 1023;; fromPort--) {\r
10367     if (fromPort < 0) {\r
10368       WSACleanup();\r
10369       return WSAEADDRINUSE;\r
10370     }\r
10371     if (s == INVALID_SOCKET) {\r
10372       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10373         err = WSAGetLastError();\r
10374         WSACleanup();\r
10375         return err;\r
10376       }\r
10377     }\r
10378     uport = (unsigned short) fromPort;\r
10379     mysa.sin_port = htons(uport);\r
10380     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10381         == SOCKET_ERROR) {\r
10382       err = WSAGetLastError();\r
10383       if (err == WSAEADDRINUSE) continue;\r
10384       WSACleanup();\r
10385       return err;\r
10386     }\r
10387     if (connect(s, (struct sockaddr *) &sa,\r
10388       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10389       err = WSAGetLastError();\r
10390       if (err == WSAEADDRINUSE) {\r
10391         closesocket(s);\r
10392         s = -1;\r
10393         continue;\r
10394       }\r
10395       WSACleanup();\r
10396       return err;\r
10397     }\r
10398     break;\r
10399   }\r
10400 \r
10401   /* Bind stderr local socket to unused "privileged" port address\r
10402    */\r
10403   s2 = INVALID_SOCKET;\r
10404   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10405   mysa.sin_family = AF_INET;\r
10406   mysa.sin_addr.s_addr = INADDR_ANY;\r
10407   for (fromPort = 1023;; fromPort--) {\r
10408     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10409     if (fromPort < 0) {\r
10410       (void) closesocket(s);\r
10411       WSACleanup();\r
10412       return WSAEADDRINUSE;\r
10413     }\r
10414     if (s2 == INVALID_SOCKET) {\r
10415       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10416         err = WSAGetLastError();\r
10417         closesocket(s);\r
10418         WSACleanup();\r
10419         return err;\r
10420       }\r
10421     }\r
10422     uport = (unsigned short) fromPort;\r
10423     mysa.sin_port = htons(uport);\r
10424     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10425         == SOCKET_ERROR) {\r
10426       err = WSAGetLastError();\r
10427       if (err == WSAEADDRINUSE) continue;\r
10428       (void) closesocket(s);\r
10429       WSACleanup();\r
10430       return err;\r
10431     }\r
10432     if (listen(s2, 1) == SOCKET_ERROR) {\r
10433       err = WSAGetLastError();\r
10434       if (err == WSAEADDRINUSE) {\r
10435         closesocket(s2);\r
10436         s2 = INVALID_SOCKET;\r
10437         continue;\r
10438       }\r
10439       (void) closesocket(s);\r
10440       (void) closesocket(s2);\r
10441       WSACleanup();\r
10442       return err;\r
10443     }\r
10444     break;\r
10445   }\r
10446   prevStderrPort = fromPort; // remember port used\r
10447   sprintf(stderrPortStr, "%d", fromPort);\r
10448 \r
10449   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10450     err = WSAGetLastError();\r
10451     (void) closesocket(s);\r
10452     (void) closesocket(s2);\r
10453     WSACleanup();\r
10454     return err;\r
10455   }\r
10456 \r
10457   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10458     err = WSAGetLastError();\r
10459     (void) closesocket(s);\r
10460     (void) closesocket(s2);\r
10461     WSACleanup();\r
10462     return err;\r
10463   }\r
10464   if (*user == NULLCHAR) user = UserName();\r
10465   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10466     err = WSAGetLastError();\r
10467     (void) closesocket(s);\r
10468     (void) closesocket(s2);\r
10469     WSACleanup();\r
10470     return err;\r
10471   }\r
10472   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10473     err = WSAGetLastError();\r
10474     (void) closesocket(s);\r
10475     (void) closesocket(s2);\r
10476     WSACleanup();\r
10477     return err;\r
10478   }\r
10479 \r
10480   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\r
10481     err = WSAGetLastError();\r
10482     (void) closesocket(s);\r
10483     (void) closesocket(s2);\r
10484     WSACleanup();\r
10485     return err;\r
10486   }\r
10487   (void) closesocket(s2);  /* Stop listening */\r
10488 \r
10489   /* Prepare return value */\r
10490   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10491   cp->kind = CPRcmd;\r
10492   cp->sock = s;\r
10493   cp->sock2 = s3;\r
10494   *pr = (ProcRef *) cp;\r
10495 \r
10496   return NO_ERROR;\r
10497 }\r
10498 \r
10499 \r
10500 InputSourceRef\r
10501 AddInputSource(ProcRef pr, int lineByLine,\r
10502                InputCallback func, VOIDSTAR closure)\r
10503 {\r
10504   InputSource *is, *is2 = NULL;\r
10505   ChildProc *cp = (ChildProc *) pr;\r
10506 \r
10507   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10508   is->lineByLine = lineByLine;\r
10509   is->func = func;\r
10510   is->closure = closure;\r
10511   is->second = NULL;\r
10512   is->next = is->buf;\r
10513   if (pr == NoProc) {\r
10514     is->kind = CPReal;\r
10515     consoleInputSource = is;\r
10516   } else {\r
10517     is->kind = cp->kind;\r
10518     /* \r
10519         [AS] Try to avoid a race condition if the thread is given control too early:\r
10520         we create all threads suspended so that the is->hThread variable can be\r
10521         safely assigned, then let the threads start with ResumeThread.\r
10522     */\r
10523     switch (cp->kind) {\r
10524     case CPReal:\r
10525       is->hFile = cp->hFrom;\r
10526       cp->hFrom = NULL; /* now owned by InputThread */\r
10527       is->hThread =\r
10528         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10529                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10530       break;\r
10531 \r
10532     case CPComm:\r
10533       is->hFile = cp->hFrom;\r
10534       cp->hFrom = NULL; /* now owned by InputThread */\r
10535       is->hThread =\r
10536         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10537                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10538       break;\r
10539 \r
10540     case CPSock:\r
10541       is->sock = cp->sock;\r
10542       is->hThread =\r
10543         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10544                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10545       break;\r
10546 \r
10547     case CPRcmd:\r
10548       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10549       *is2 = *is;\r
10550       is->sock = cp->sock;\r
10551       is->second = is2;\r
10552       is2->sock = cp->sock2;\r
10553       is2->second = is2;\r
10554       is->hThread =\r
10555         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10556                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10557       is2->hThread =\r
10558         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10559                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10560       break;\r
10561     }\r
10562 \r
10563     if( is->hThread != NULL ) {\r
10564         ResumeThread( is->hThread );\r
10565     }\r
10566 \r
10567     if( is2 != NULL && is2->hThread != NULL ) {\r
10568         ResumeThread( is2->hThread );\r
10569     }\r
10570   }\r
10571 \r
10572   return (InputSourceRef) is;\r
10573 }\r
10574 \r
10575 void\r
10576 RemoveInputSource(InputSourceRef isr)\r
10577 {\r
10578   InputSource *is;\r
10579 \r
10580   is = (InputSource *) isr;\r
10581   is->hThread = NULL;  /* tell thread to stop */\r
10582   CloseHandle(is->hThread);\r
10583   if (is->second != NULL) {\r
10584     is->second->hThread = NULL;\r
10585     CloseHandle(is->second->hThread);\r
10586   }\r
10587 }\r
10588 \r
10589 \r
10590 int\r
10591 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10592 {\r
10593   DWORD dOutCount;\r
10594   int outCount = SOCKET_ERROR;\r
10595   ChildProc *cp = (ChildProc *) pr;\r
10596   static OVERLAPPED ovl;\r
10597 \r
10598   if (pr == NoProc) {\r
10599     ConsoleOutput(message, count, FALSE);\r
10600     return count;\r
10601   } \r
10602 \r
10603   if (ovl.hEvent == NULL) {\r
10604     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10605   }\r
10606   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10607 \r
10608   switch (cp->kind) {\r
10609   case CPSock:\r
10610   case CPRcmd:\r
10611     outCount = send(cp->sock, message, count, 0);\r
10612     if (outCount == SOCKET_ERROR) {\r
10613       *outError = WSAGetLastError();\r
10614     } else {\r
10615       *outError = NO_ERROR;\r
10616     }\r
10617     break;\r
10618 \r
10619   case CPReal:\r
10620     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10621                   &dOutCount, NULL)) {\r
10622       *outError = NO_ERROR;\r
10623       outCount = (int) dOutCount;\r
10624     } else {\r
10625       *outError = GetLastError();\r
10626     }\r
10627     break;\r
10628 \r
10629   case CPComm:\r
10630     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10631                             &dOutCount, &ovl);\r
10632     if (*outError == NO_ERROR) {\r
10633       outCount = (int) dOutCount;\r
10634     }\r
10635     break;\r
10636   }\r
10637   return outCount;\r
10638 }\r
10639 \r
10640 int\r
10641 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10642                        long msdelay)\r
10643 {\r
10644   /* Ignore delay, not implemented for WinBoard */\r
10645   return OutputToProcess(pr, message, count, outError);\r
10646 }\r
10647 \r
10648 \r
10649 void\r
10650 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10651                         char *buf, int count, int error)\r
10652 {\r
10653   DisplayFatalError("Not implemented", 0, 1);\r
10654 }\r
10655 \r
10656 /* see wgamelist.c for Game List functions */\r
10657 /* see wedittags.c for Edit Tags functions */\r
10658 \r
10659 \r
10660 VOID\r
10661 ICSInitScript()\r
10662 {\r
10663   FILE *f;\r
10664   char buf[MSG_SIZ];\r
10665   char *dummy;\r
10666 \r
10667   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10668     f = fopen(buf, "r");\r
10669     if (f != NULL) {\r
10670       ProcessICSInitScript(f);\r
10671       fclose(f);\r
10672     }\r
10673   }\r
10674 }\r
10675 \r
10676 \r
10677 VOID\r
10678 StartAnalysisClock()\r
10679 {\r
10680   if (analysisTimerEvent) return;\r
10681   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10682                                         (UINT) 2000, NULL);\r
10683 }\r
10684 \r
10685 LRESULT CALLBACK\r
10686 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10687 {\r
10688   static HANDLE hwndText;\r
10689   RECT rect;\r
10690   static int sizeX, sizeY;\r
10691   int newSizeX, newSizeY, flags;\r
10692   MINMAXINFO *mmi;\r
10693 \r
10694   switch (message) {\r
10695   case WM_INITDIALOG: /* message: initialize dialog box */\r
10696     /* Initialize the dialog items */\r
10697     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10698     SetWindowText(hDlg, analysisTitle);\r
10699     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10700     /* Size and position the dialog */\r
10701     if (!analysisDialog) {\r
10702       analysisDialog = hDlg;\r
10703       flags = SWP_NOZORDER;\r
10704       GetClientRect(hDlg, &rect);\r
10705       sizeX = rect.right;\r
10706       sizeY = rect.bottom;\r
10707       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10708           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10709         WINDOWPLACEMENT wp;\r
10710         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10711         wp.length = sizeof(WINDOWPLACEMENT);\r
10712         wp.flags = 0;\r
10713         wp.showCmd = SW_SHOW;\r
10714         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10715         wp.rcNormalPosition.left = analysisX;\r
10716         wp.rcNormalPosition.right = analysisX + analysisW;\r
10717         wp.rcNormalPosition.top = analysisY;\r
10718         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10719         SetWindowPlacement(hDlg, &wp);\r
10720 \r
10721         GetClientRect(hDlg, &rect);\r
10722         newSizeX = rect.right;\r
10723         newSizeY = rect.bottom;\r
10724         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10725                               newSizeX, newSizeY);\r
10726         sizeX = newSizeX;\r
10727         sizeY = newSizeY;\r
10728       }\r
10729     }\r
10730     return FALSE;\r
10731 \r
10732   case WM_COMMAND: /* message: received a command */\r
10733     switch (LOWORD(wParam)) {\r
10734     case IDCANCEL:\r
10735       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10736           ExitAnalyzeMode();\r
10737           ModeHighlight();\r
10738           return TRUE;\r
10739       }\r
10740       EditGameEvent();\r
10741       return TRUE;\r
10742     default:\r
10743       break;\r
10744     }\r
10745     break;\r
10746 \r
10747   case WM_SIZE:\r
10748     newSizeX = LOWORD(lParam);\r
10749     newSizeY = HIWORD(lParam);\r
10750     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10751     sizeX = newSizeX;\r
10752     sizeY = newSizeY;\r
10753     break;\r
10754 \r
10755   case WM_GETMINMAXINFO:\r
10756     /* Prevent resizing window too small */\r
10757     mmi = (MINMAXINFO *) lParam;\r
10758     mmi->ptMinTrackSize.x = 100;\r
10759     mmi->ptMinTrackSize.y = 100;\r
10760     break;\r
10761   }\r
10762   return FALSE;\r
10763 }\r
10764 \r
10765 VOID\r
10766 AnalysisPopUp(char* title, char* str)\r
10767 {\r
10768   FARPROC lpProc;\r
10769   char *p, *q;\r
10770 \r
10771   /* [AS] */\r
10772   EngineOutputPopUp();\r
10773   return;\r
10774 \r
10775   if (str == NULL) str = "";\r
10776   p = (char *) malloc(2 * strlen(str) + 2);\r
10777   q = p;\r
10778   while (*str) {\r
10779     if (*str == '\n') *q++ = '\r';\r
10780     *q++ = *str++;\r
10781   }\r
10782   *q = NULLCHAR;\r
10783   if (analysisText != NULL) free(analysisText);\r
10784   analysisText = p;\r
10785 \r
10786   if (analysisDialog) {\r
10787     SetWindowText(analysisDialog, title);\r
10788     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10789     ShowWindow(analysisDialog, SW_SHOW);\r
10790   } else {\r
10791     analysisTitle = title;\r
10792     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10793     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10794                  hwndMain, (DLGPROC)lpProc);\r
10795     FreeProcInstance(lpProc);\r
10796   }\r
10797   analysisDialogUp = TRUE;  \r
10798 }\r
10799 \r
10800 VOID\r
10801 AnalysisPopDown()\r
10802 {\r
10803   if (analysisDialog) {\r
10804     ShowWindow(analysisDialog, SW_HIDE);\r
10805   }\r
10806   analysisDialogUp = FALSE;  \r
10807 }\r
10808 \r
10809 \r
10810 VOID\r
10811 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10812 {\r
10813   highlightInfo.sq[0].x = fromX;\r
10814   highlightInfo.sq[0].y = fromY;\r
10815   highlightInfo.sq[1].x = toX;\r
10816   highlightInfo.sq[1].y = toY;\r
10817 }\r
10818 \r
10819 VOID\r
10820 ClearHighlights()\r
10821 {\r
10822   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10823     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10824 }\r
10825 \r
10826 VOID\r
10827 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10828 {\r
10829   premoveHighlightInfo.sq[0].x = fromX;\r
10830   premoveHighlightInfo.sq[0].y = fromY;\r
10831   premoveHighlightInfo.sq[1].x = toX;\r
10832   premoveHighlightInfo.sq[1].y = toY;\r
10833 }\r
10834 \r
10835 VOID\r
10836 ClearPremoveHighlights()\r
10837 {\r
10838   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10839     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10840 }\r
10841 \r
10842 VOID\r
10843 ShutDownFrontEnd()\r
10844 {\r
10845   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10846   DeleteClipboardTempFiles();\r
10847 }\r
10848 \r
10849 void\r
10850 BoardToTop()\r
10851 {\r
10852     if (IsIconic(hwndMain))\r
10853       ShowWindow(hwndMain, SW_RESTORE);\r
10854 \r
10855     SetActiveWindow(hwndMain);\r
10856 }\r
10857 \r
10858 /*\r
10859  * Prototypes for animation support routines\r
10860  */\r
10861 static void ScreenSquare(int column, int row, POINT * pt);\r
10862 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10863      POINT frames[], int * nFrames);\r
10864 \r
10865 \r
10866 void\r
10867 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10868 {       // [HGM] atomic: animate blast wave\r
10869         int i;\r
10870 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10871         explodeInfo.fromX = fromX;\r
10872         explodeInfo.fromY = fromY;\r
10873         explodeInfo.toX = toX;\r
10874         explodeInfo.toY = toY;\r
10875         for(i=1; i<nFrames; i++) {\r
10876             explodeInfo.radius = (i*180)/(nFrames-1);\r
10877             DrawPosition(FALSE, NULL);\r
10878             Sleep(appData.animSpeed);\r
10879         }\r
10880         explodeInfo.radius = 0;\r
10881         DrawPosition(TRUE, NULL);\r
10882 }\r
10883 \r
10884 #define kFactor 4\r
10885 \r
10886 void\r
10887 AnimateMove(board, fromX, fromY, toX, toY)\r
10888      Board board;\r
10889      int fromX;\r
10890      int fromY;\r
10891      int toX;\r
10892      int toY;\r
10893 {\r
10894   ChessSquare piece;\r
10895   POINT start, finish, mid;\r
10896   POINT frames[kFactor * 2 + 1];\r
10897   int nFrames, n;\r
10898 \r
10899   if (!appData.animate) return;\r
10900   if (doingSizing) return;\r
10901   if (fromY < 0 || fromX < 0) return;\r
10902   piece = board[fromY][fromX];\r
10903   if (piece >= EmptySquare) return;\r
10904 \r
10905   ScreenSquare(fromX, fromY, &start);\r
10906   ScreenSquare(toX, toY, &finish);\r
10907 \r
10908   /* All pieces except knights move in straight line */\r
10909   if (piece != WhiteKnight && piece != BlackKnight) {\r
10910     mid.x = start.x + (finish.x - start.x) / 2;\r
10911     mid.y = start.y + (finish.y - start.y) / 2;\r
10912   } else {\r
10913     /* Knight: make diagonal movement then straight */\r
10914     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10915        mid.x = start.x + (finish.x - start.x) / 2;\r
10916        mid.y = finish.y;\r
10917      } else {\r
10918        mid.x = finish.x;\r
10919        mid.y = start.y + (finish.y - start.y) / 2;\r
10920      }\r
10921   }\r
10922   \r
10923   /* Don't use as many frames for very short moves */\r
10924   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10925     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10926   else\r
10927     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10928 \r
10929   animInfo.from.x = fromX;\r
10930   animInfo.from.y = fromY;\r
10931   animInfo.to.x = toX;\r
10932   animInfo.to.y = toY;\r
10933   animInfo.lastpos = start;\r
10934   animInfo.piece = piece;\r
10935   for (n = 0; n < nFrames; n++) {\r
10936     animInfo.pos = frames[n];\r
10937     DrawPosition(FALSE, NULL);\r
10938     animInfo.lastpos = animInfo.pos;\r
10939     Sleep(appData.animSpeed);\r
10940   }\r
10941   animInfo.pos = finish;\r
10942   DrawPosition(FALSE, NULL);\r
10943   animInfo.piece = EmptySquare;\r
10944   if(gameInfo.variant == VariantAtomic && \r
10945      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10946         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10947 }\r
10948 \r
10949 /*      Convert board position to corner of screen rect and color       */\r
10950 \r
10951 static void\r
10952 ScreenSquare(column, row, pt)\r
10953      int column; int row; POINT * pt;\r
10954 {\r
10955   if (flipView) {\r
10956     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10957     pt->y = lineGap + row * (squareSize + lineGap);\r
10958   } else {\r
10959     pt->x = lineGap + column * (squareSize + lineGap);\r
10960     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10961   }\r
10962 }\r
10963 \r
10964 /*      Generate a series of frame coords from start->mid->finish.\r
10965         The movement rate doubles until the half way point is\r
10966         reached, then halves back down to the final destination,\r
10967         which gives a nice slow in/out effect. The algorithmn\r
10968         may seem to generate too many intermediates for short\r
10969         moves, but remember that the purpose is to attract the\r
10970         viewers attention to the piece about to be moved and\r
10971         then to where it ends up. Too few frames would be less\r
10972         noticeable.                                             */\r
10973 \r
10974 static void\r
10975 Tween(start, mid, finish, factor, frames, nFrames)\r
10976      POINT * start; POINT * mid;\r
10977      POINT * finish; int factor;\r
10978      POINT frames[]; int * nFrames;\r
10979 {\r
10980   int n, fraction = 1, count = 0;\r
10981 \r
10982   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10983   for (n = 0; n < factor; n++)\r
10984     fraction *= 2;\r
10985   for (n = 0; n < factor; n++) {\r
10986     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10987     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10988     count ++;\r
10989     fraction = fraction / 2;\r
10990   }\r
10991   \r
10992   /* Midpoint */\r
10993   frames[count] = *mid;\r
10994   count ++;\r
10995   \r
10996   /* Slow out, stepping 1/2, then 1/4, ... */\r
10997   fraction = 2;\r
10998   for (n = 0; n < factor; n++) {\r
10999     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
11000     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
11001     count ++;\r
11002     fraction = fraction * 2;\r
11003   }\r
11004   *nFrames = count;\r
11005 }\r
11006 \r
11007 void\r
11008 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
11009 {\r
11010 #if 0\r
11011     char buf[256];\r
11012 \r
11013     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
11014         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
11015 \r
11016     OutputDebugString( buf );\r
11017 #endif\r
11018 \r
11019     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
11020 \r
11021     EvalGraphSet( first, last, current, pvInfoList );\r
11022 }\r
11023 \r
11024 void SetProgramStats( FrontEndProgramStats * stats )\r
11025 {\r
11026 #if 0\r
11027     char buf[1024];\r
11028 \r
11029     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
11030         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
11031 \r
11032     OutputDebugString( buf );\r
11033 #endif\r
11034 \r
11035     EngineOutputUpdate( stats );\r
11036 }\r