trigger a focus switch to ICS input field when typing
[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 \r
104 void DisplayHoldingsCount(HDC hdc, int x, int y, int align, int copyNumber);\r
105 VOID NewVariantPopup(HWND hwnd);\r
106 int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,\r
107                    /*char*/int promoChar));\r
108 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
109 void DisplayMove P((int moveNumber));\r
110 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
111 typedef struct {\r
112   ChessSquare piece;  \r
113   POINT pos;      /* window coordinates of current pos */\r
114   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
115   POINT from;     /* board coordinates of the piece's orig pos */\r
116   POINT to;       /* board coordinates of the piece's new pos */\r
117 } AnimInfo;\r
118 \r
119 static AnimInfo animInfo = { EmptySquare, {-1,-1}, {-1,-1}, {-1,-1} };\r
120 \r
121 typedef struct {\r
122   POINT start;    /* window coordinates of start pos */\r
123   POINT pos;      /* window coordinates of current pos */\r
124   POINT lastpos;  /* window coordinates of last pos - used for clipping */\r
125   POINT from;     /* board coordinates of the piece's orig pos */\r
126 } DragInfo;\r
127 \r
128 static DragInfo dragInfo = { {-1,-1}, {-1,-1}, {-1,-1}, {-1,-1} };\r
129 \r
130 typedef struct {\r
131   POINT sq[2];    /* board coordinates of from, to squares */\r
132 } HighlightInfo;\r
133 \r
134 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
135 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
136 \r
137 typedef struct { // [HGM] atomic\r
138   int fromX, fromY, toX, toY, radius;\r
139 } ExplodeInfo;\r
140 \r
141 static ExplodeInfo explodeInfo;\r
142 \r
143 /* Window class names */\r
144 char szAppName[] = "WinBoard";\r
145 char szConsoleName[] = "WBConsole";\r
146 \r
147 /* Title bar text */\r
148 char szTitle[] = "WinBoard";\r
149 char szConsoleTitle[] = "I C S Interaction";\r
150 \r
151 char *programName;\r
152 char *settingsFileName;\r
153 BOOLEAN saveSettingsOnExit;\r
154 char installDir[MSG_SIZ];\r
155 \r
156 BoardSize boardSize;\r
157 BOOLEAN chessProgram;\r
158 static int boardX, boardY;\r
159 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
160 static int squareSize, lineGap, minorSize;\r
161 static int winWidth, winHeight, winW, winH;\r
162 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
163 static int logoHeight = 0;\r
164 static char messageText[MESSAGE_TEXT_MAX];\r
165 static int clockTimerEvent = 0;\r
166 static int loadGameTimerEvent = 0;\r
167 static int analysisTimerEvent = 0;\r
168 static DelayedEventCallback delayedTimerCallback;\r
169 static int delayedTimerEvent = 0;\r
170 static int buttonCount = 2;\r
171 char *icsTextMenuString;\r
172 char *icsNames;\r
173 char *firstChessProgramNames;\r
174 char *secondChessProgramNames;\r
175 \r
176 #define ARG_MAX 128*1024 /* [AS] For Roger Brown's very long list! */\r
177 \r
178 #define PALETTESIZE 256\r
179 \r
180 HINSTANCE hInst;          /* current instance */\r
181 HWND hwndMain = NULL;        /* root window*/\r
182 HWND hwndConsole = NULL;\r
183 BOOLEAN alwaysOnTop = FALSE;\r
184 RECT boardRect;\r
185 COLORREF lightSquareColor, darkSquareColor, whitePieceColor, \r
186   blackPieceColor, highlightSquareColor, premoveHighlightColor;\r
187 HPALETTE hPal;\r
188 ColorClass currentColorClass;\r
189 \r
190 HWND hCommPort = NULL;    /* currently open comm port */\r
191 static HWND hwndPause;    /* pause button */\r
192 static HBITMAP pieceBitmap[3][(int) BlackPawn]; /* [HGM] nr of bitmaps referred to bP in stead of wK */\r
193 static HBRUSH lightSquareBrush, darkSquareBrush,\r
194   blackSquareBrush, /* [HGM] for band between board and holdings */\r
195   explodeBrush,     /* [HGM] atomic */\r
196   whitePieceBrush, blackPieceBrush, iconBkgndBrush /*, outlineBrush*/;\r
197 static POINT gridEndpoints[(BOARD_SIZE + 1) * 4];\r
198 static DWORD gridVertexCounts[(BOARD_SIZE + 1) * 2];\r
199 static HPEN gridPen = NULL;\r
200 static HPEN highlightPen = NULL;\r
201 static HPEN premovePen = NULL;\r
202 static NPLOGPALETTE pLogPal;\r
203 static BOOL paletteChanged = FALSE;\r
204 static HICON iconWhite, iconBlack, iconCurrent;\r
205 static int doingSizing = FALSE;\r
206 static int lastSizing = 0;\r
207 static int prevStderrPort;\r
208 static HBITMAP userLogo;\r
209 \r
210 /* [AS] Support for background textures */\r
211 #define BACK_TEXTURE_MODE_DISABLED      0\r
212 #define BACK_TEXTURE_MODE_PLAIN         1\r
213 #define BACK_TEXTURE_MODE_FULL_RANDOM   2\r
214 \r
215 static HBITMAP liteBackTexture = NULL;\r
216 static HBITMAP darkBackTexture = NULL;\r
217 static int liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
218 static int darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
219 static int backTextureSquareSize = 0;\r
220 static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_SIZE][BOARD_SIZE];\r
221 \r
222 #if __GNUC__ && !defined(_winmajor)\r
223 #define oldDialog 0 /* cygwin doesn't define _winmajor; mingw does */\r
224 #else\r
225 #define oldDialog (_winmajor < 4)\r
226 #endif\r
227 \r
228 char *defaultTextAttribs[] = \r
229 {\r
230   COLOR_SHOUT, COLOR_SSHOUT, COLOR_CHANNEL1, COLOR_CHANNEL, COLOR_KIBITZ,\r
231   COLOR_TELL, COLOR_CHALLENGE, COLOR_REQUEST, COLOR_SEEK, COLOR_NORMAL,\r
232   COLOR_NONE\r
233 };\r
234 \r
235 typedef struct {\r
236   char *name;\r
237   int squareSize;\r
238   int lineGap;\r
239   int smallLayout;\r
240   int tinyLayout;\r
241   int cliWidth, cliHeight;\r
242 } SizeInfo;\r
243 \r
244 SizeInfo sizeInfo[] = \r
245 {\r
246   { "tiny",     21, 0, 1, 1, 0, 0 },\r
247   { "teeny",    25, 1, 1, 1, 0, 0 },\r
248   { "dinky",    29, 1, 1, 1, 0, 0 },\r
249   { "petite",   33, 1, 1, 1, 0, 0 },\r
250   { "slim",     37, 2, 1, 0, 0, 0 },\r
251   { "small",    40, 2, 1, 0, 0, 0 },\r
252   { "mediocre", 45, 2, 1, 0, 0, 0 },\r
253   { "middling", 49, 2, 0, 0, 0, 0 },\r
254   { "average",  54, 2, 0, 0, 0, 0 },\r
255   { "moderate", 58, 3, 0, 0, 0, 0 },\r
256   { "medium",   64, 3, 0, 0, 0, 0 },\r
257   { "bulky",    72, 3, 0, 0, 0, 0 },\r
258   { "large",    80, 3, 0, 0, 0, 0 },\r
259   { "big",      87, 3, 0, 0, 0, 0 },\r
260   { "huge",     95, 3, 0, 0, 0, 0 },\r
261   { "giant",    108, 3, 0, 0, 0, 0 },\r
262   { "colossal", 116, 4, 0, 0, 0, 0 },\r
263   { "titanic",  129, 4, 0, 0, 0, 0 },\r
264   { NULL, 0, 0, 0, 0, 0, 0 }\r
265 };\r
266 \r
267 #define MF(x) {x, {{0,}, 0. }, {0, }, 0}\r
268 MyFont fontRec[NUM_SIZES][NUM_FONTS] =\r
269 {\r
270   { 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
271   { 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
272   { 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
273   { 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
274   { 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
275   { 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
276   { 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
277   { 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
278   { 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
279   { 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
280   { 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
281   { 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
282   { 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
283   { 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
284   { 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
285   { 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
286   { 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
287   { 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
288 };\r
289 \r
290 MyFont *font[NUM_SIZES][NUM_FONTS];\r
291 \r
292 typedef struct {\r
293   char *label;\r
294   int id;\r
295   HWND hwnd;\r
296   WNDPROC wndproc;\r
297 } MyButtonDesc;\r
298 \r
299 #define BUTTON_WIDTH (tinyLayout ? 16 : 32)\r
300 #define N_BUTTONS 5\r
301 \r
302 MyButtonDesc buttonDesc[N_BUTTONS] =\r
303 {\r
304   {"<<", IDM_ToStart, NULL, NULL},\r
305   {"<", IDM_Backward, NULL, NULL},\r
306   {"P", IDM_Pause, NULL, NULL},\r
307   {">", IDM_Forward, NULL, NULL},\r
308   {">>", IDM_ToEnd, NULL, NULL},\r
309 };\r
310 \r
311 int tinyLayout = 0, smallLayout = 0;\r
312 #define MENU_BAR_ITEMS 7\r
313 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
314   { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
315   { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
316 };\r
317 \r
318 \r
319 MySound sounds[(int)NSoundClasses];\r
320 MyTextAttribs textAttribs[(int)NColorClasses];\r
321 \r
322 MyColorizeAttribs colorizeAttribs[] = {\r
323   { (COLORREF)0, 0, "Shout Text" },\r
324   { (COLORREF)0, 0, "SShout/CShout" },\r
325   { (COLORREF)0, 0, "Channel 1 Text" },\r
326   { (COLORREF)0, 0, "Channel Text" },\r
327   { (COLORREF)0, 0, "Kibitz Text" },\r
328   { (COLORREF)0, 0, "Tell Text" },\r
329   { (COLORREF)0, 0, "Challenge Text" },\r
330   { (COLORREF)0, 0, "Request Text" },\r
331   { (COLORREF)0, 0, "Seek Text" },\r
332   { (COLORREF)0, 0, "Normal Text" },\r
333   { (COLORREF)0, 0, "None" }\r
334 };\r
335 \r
336 \r
337 \r
338 static char *commentTitle;\r
339 static char *commentText;\r
340 static int commentIndex;\r
341 static Boolean editComment = FALSE;\r
342 HWND commentDialog = NULL;\r
343 BOOLEAN commentDialogUp = FALSE;\r
344 static int commentX, commentY, commentH, commentW;\r
345 \r
346 static char *analysisTitle;\r
347 static char *analysisText;\r
348 HWND analysisDialog = NULL;\r
349 BOOLEAN analysisDialogUp = FALSE;\r
350 static int analysisX, analysisY, analysisH, analysisW;\r
351 \r
352 char errorTitle[MSG_SIZ];\r
353 char errorMessage[2*MSG_SIZ];\r
354 HWND errorDialog = NULL;\r
355 BOOLEAN moveErrorMessageUp = FALSE;\r
356 BOOLEAN consoleEcho = TRUE;\r
357 CHARFORMAT consoleCF;\r
358 COLORREF consoleBackgroundColor;\r
359 \r
360 char *programVersion;\r
361 \r
362 #define CPReal 1\r
363 #define CPComm 2\r
364 #define CPSock 3\r
365 #define CPRcmd 4\r
366 typedef int CPKind;\r
367 \r
368 typedef struct {\r
369   CPKind kind;\r
370   HANDLE hProcess;\r
371   DWORD pid;\r
372   HANDLE hTo;\r
373   HANDLE hFrom;\r
374   SOCKET sock;\r
375   SOCKET sock2;  /* stderr socket for OpenRcmd */\r
376 } ChildProc;\r
377 \r
378 #define INPUT_SOURCE_BUF_SIZE 4096\r
379 \r
380 typedef struct _InputSource {\r
381   CPKind kind;\r
382   HANDLE hFile;\r
383   SOCKET sock;\r
384   int lineByLine;\r
385   HANDLE hThread;\r
386   DWORD id;\r
387   char buf[INPUT_SOURCE_BUF_SIZE];\r
388   char *next;\r
389   DWORD count;\r
390   int error;\r
391   InputCallback func;\r
392   struct _InputSource *second;  /* for stderr thread on CPRcmd */\r
393   VOIDSTAR closure;\r
394 } InputSource;\r
395 \r
396 InputSource *consoleInputSource;\r
397 \r
398 DCB dcb;\r
399 \r
400 /* forward */\r
401 VOID ConsoleOutput(char* data, int length, int forceVisible);\r
402 VOID ConsoleCreate();\r
403 LRESULT CALLBACK\r
404   ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
405 VOID ColorizeTextPopup(HWND hwnd, ColorClass cc);\r
406 VOID PrintCommSettings(FILE *f, char *name, DCB *dcb);\r
407 VOID ParseCommSettings(char *arg, DCB *dcb);\r
408 LRESULT CALLBACK\r
409   StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);\r
410 VOID APIENTRY MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def);\r
411 void ParseIcsTextMenu(char *icsTextMenuString);\r
412 VOID PopUpMoveDialog(char firstchar);\r
413 VOID PopUpNameDialog(char firstchar);\r
414 VOID UpdateSampleText(HWND hDlg, int id, MyColorizeAttribs *mca);\r
415 \r
416 /* [AS] */\r
417 int NewGameFRC();\r
418 int GameListOptions();\r
419 \r
420 HWND moveHistoryDialog = NULL;\r
421 BOOLEAN moveHistoryDialogUp = FALSE;\r
422 \r
423 WindowPlacement wpMoveHistory;\r
424 \r
425 HWND evalGraphDialog = NULL;\r
426 BOOLEAN evalGraphDialogUp = FALSE;\r
427 \r
428 WindowPlacement wpEvalGraph;\r
429 \r
430 HWND engineOutputDialog = NULL;\r
431 BOOLEAN engineOutputDialogUp = FALSE;\r
432 \r
433 WindowPlacement wpEngineOutput;\r
434 WindowPlacement wpGameList;\r
435 WindowPlacement wpConsole;\r
436 \r
437 VOID MoveHistoryPopUp();\r
438 VOID MoveHistoryPopDown();\r
439 VOID MoveHistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
440 BOOL MoveHistoryIsUp();\r
441 \r
442 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );\r
443 VOID EvalGraphPopUp();\r
444 VOID EvalGraphPopDown();\r
445 BOOL EvalGraphIsUp();\r
446 \r
447 VOID EngineOutputPopUp();\r
448 VOID EngineOutputPopDown();\r
449 BOOL EngineOutputIsUp();\r
450 VOID EngineOutputUpdate( FrontEndProgramStats * stats );\r
451 \r
452 VOID EngineOptionsPopup(); // [HGM] settings\r
453 \r
454 VOID GothicPopUp(char *title, VariantClass variant);\r
455 /*\r
456  * Setting "frozen" should disable all user input other than deleting\r
457  * the window.  We do this while engines are initializing themselves.\r
458  */\r
459 static int frozen = 0;\r
460 static int oldMenuItemState[MENU_BAR_ITEMS];\r
461 void FreezeUI()\r
462 {\r
463   HMENU hmenu;\r
464   int i;\r
465 \r
466   if (frozen) return;\r
467   frozen = 1;\r
468   hmenu = GetMenu(hwndMain);\r
469   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
470     oldMenuItemState[i] = EnableMenuItem(hmenu, i, MF_BYPOSITION|MF_GRAYED);\r
471   }\r
472   DrawMenuBar(hwndMain);\r
473 }\r
474 \r
475 /* Undo a FreezeUI */\r
476 void ThawUI()\r
477 {\r
478   HMENU hmenu;\r
479   int i;\r
480 \r
481   if (!frozen) return;\r
482   frozen = 0;\r
483   hmenu = GetMenu(hwndMain);\r
484   for (i=0; i<MENU_BAR_ITEMS; i++) {\r
485     EnableMenuItem(hmenu, i, MF_BYPOSITION|oldMenuItemState[i]);\r
486   }\r
487   DrawMenuBar(hwndMain);\r
488 }\r
489 \r
490 static int fromX = -1, fromY = -1, toX, toY; // [HGM] moved upstream, so JAWS can use them\r
491 \r
492 /* JAWS preparation patch (WinBoard for the sight impaired). Define required insertions as empty */\r
493 #ifdef JAWS\r
494 #include "jaws.c"\r
495 #else\r
496 #define JAWS_INIT\r
497 #define JAWS_ALT_INTERCEPT\r
498 #define JAWS_KB_NAVIGATION\r
499 #define JAWS_MENU_ITEMS\r
500 #define JAWS_SILENCE\r
501 #define JAWS_REPLAY\r
502 #define JAWS_ACCEL\r
503 #define JAWS_COPYRIGHT\r
504 #define JAWS_DELETE(X) X\r
505 #define SAYMACHINEMOVE()\r
506 #define SAY(X)\r
507 #endif\r
508 \r
509 /*---------------------------------------------------------------------------*\\r
510  *\r
511  * WinMain\r
512  *\r
513 \*---------------------------------------------------------------------------*/\r
514 \r
515 int APIENTRY\r
516 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,\r
517         LPSTR lpCmdLine, int nCmdShow)\r
518 {\r
519   MSG msg;\r
520   HANDLE hAccelMain, hAccelNoAlt, hAccelNoICS;\r
521 //  INITCOMMONCONTROLSEX ex;\r
522 \r
523   debugFP = stderr;\r
524 \r
525   LoadLibrary("RICHED32.DLL");\r
526   consoleCF.cbSize = sizeof(CHARFORMAT);\r
527 \r
528   if (!InitApplication(hInstance)) {\r
529     return (FALSE);\r
530   }\r
531   if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) {\r
532     return (FALSE);\r
533   }\r
534 \r
535   JAWS_INIT\r
536 \r
537 //  InitCommonControlsEx(&ex);\r
538   InitCommonControls();\r
539 \r
540   hAccelMain = LoadAccelerators (hInstance, szAppName);\r
541   hAccelNoAlt = LoadAccelerators (hInstance, "NO_ALT");\r
542   hAccelNoICS = LoadAccelerators( hInstance, "NO_ICS"); /* [AS] No Ctrl-V on ICS!!! */\r
543 \r
544   /* Acquire and dispatch messages until a WM_QUIT message is received. */\r
545 \r
546   while (GetMessage(&msg, /* message structure */\r
547                     NULL, /* handle of window receiving the message */\r
548                     0,    /* lowest message to examine */\r
549                     0))   /* highest message to examine */\r
550     {\r
551 \r
552       if(msg.message == WM_CHAR && msg.wParam == '\t') {\r
553         // [HGM] navigate: switch between all windows with tab\r
554         HWND e1 = NULL, e2 = NULL, mh = NULL, hInput = NULL, hText = NULL;\r
555         int i, currentElement = 0;\r
556 \r
557         // first determine what element of the chain we come from (if any)\r
558         if(appData.icsActive) {\r
559             hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
560             hText  = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
561         }\r
562         if(engineOutputDialog && EngineOutputIsUp()) {\r
563             e1 = GetDlgItem(engineOutputDialog, IDC_EngineMemo1);\r
564             e2 = GetDlgItem(engineOutputDialog, IDC_EngineMemo2);\r
565         }\r
566         if(moveHistoryDialog && MoveHistoryIsUp()) {\r
567             mh = GetDlgItem(moveHistoryDialog, IDC_MoveHistory);\r
568         }\r
569         if(msg.hwnd == hwndMain) currentElement = 7 ; else\r
570         if(msg.hwnd == engineOutputDialog) currentElement = 2; else\r
571         if(msg.hwnd == e1)                 currentElement = 2; else\r
572         if(msg.hwnd == e2)                 currentElement = 3; else\r
573         if(msg.hwnd == moveHistoryDialog) currentElement = 4; else\r
574         if(msg.hwnd == mh)                currentElement = 4; else\r
575         if(msg.hwnd == evalGraphDialog)    currentElement = 7; else\r
576         if(msg.hwnd == hText)  currentElement = 5; else\r
577         if(msg.hwnd == hInput) currentElement = 6; else\r
578         for (i = 0; i < N_BUTTONS; i++) {\r
579             if (buttonDesc[i].hwnd == msg.hwnd) { currentElement = 1; break; }\r
580         }\r
581 \r
582         // determine where to go to\r
583         if(currentElement) { HWND h = NULL; int direction = GetKeyState(VK_SHIFT) < 0 ? -1 : 1;\r
584           do {\r
585             currentElement = (currentElement + direction) % 7;\r
586             switch(currentElement) {\r
587                 case 0:\r
588                   h = hwndMain; break; // passing this case always makes the loop exit\r
589                 case 1:\r
590                   h = buttonDesc[0].hwnd; break; // could be NULL\r
591                 case 2:\r
592                   if(!EngineOutputIsUp()) continue; // skip closed auxiliary windows\r
593                   h = e1; break;\r
594                 case 3:\r
595                   if(!EngineOutputIsUp()) continue;\r
596                   h = e2; break;\r
597                 case 4:\r
598                   if(!MoveHistoryIsUp()) continue;\r
599                   h = mh; break;\r
600 //              case 5: // input to eval graph does not seem to get here!\r
601 //                if(!EvalGraphIsUp()) continue;\r
602 //                h = evalGraphDialog; break;\r
603                 case 5:\r
604                   if(!appData.icsActive) continue;\r
605                   SAY("display");\r
606                   h = hText; break;\r
607                 case 6:\r
608                   if(!appData.icsActive) continue;\r
609                   SAY("input");\r
610                   h = hInput; break;\r
611             }\r
612           } while(h == 0);\r
613 \r
614           if(currentElement > 4 && IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
615           if(currentElement < 5 && IsIconic(hwndMain))    ShowWindow(hwndMain, SW_RESTORE); // all open together\r
616           SetFocus(h);\r
617 \r
618           continue; // this message now has been processed\r
619         }\r
620       }\r
621 \r
622       if (!(commentDialog && IsDialogMessage(commentDialog, &msg)) &&\r
623           !(moveHistoryDialog && IsDialogMessage(moveHistoryDialog, &msg)) &&\r
624           !(evalGraphDialog && IsDialogMessage(evalGraphDialog, &msg)) &&\r
625           !(engineOutputDialog && IsDialogMessage(engineOutputDialog, &msg)) &&\r
626           !(editTagsDialog && IsDialogMessage(editTagsDialog, &msg)) &&\r
627           !(gameListDialog && IsDialogMessage(gameListDialog, &msg)) &&\r
628           !(errorDialog && IsDialogMessage(errorDialog, &msg)) &&\r
629           !(!frozen && TranslateAccelerator(hwndMain, hAccelMain, &msg)) && JAWS_ACCEL\r
630           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoICS, &msg)) &&\r
631           !(!hwndConsole && TranslateAccelerator(hwndMain, hAccelNoAlt, &msg))) {\r
632         TranslateMessage(&msg); /* Translates virtual key codes */\r
633         DispatchMessage(&msg);  /* Dispatches message to window */\r
634       }\r
635     }\r
636 \r
637 \r
638   return (msg.wParam);  /* Returns the value from PostQuitMessage */\r
639 }\r
640 \r
641 /*---------------------------------------------------------------------------*\\r
642  *\r
643  * Initialization functions\r
644  *\r
645 \*---------------------------------------------------------------------------*/\r
646 \r
647 void\r
648 SetUserLogo()\r
649 {   // update user logo if necessary\r
650     static char oldUserName[MSG_SIZ], *curName;\r
651 \r
652     if(appData.autoLogo) {\r
653           curName = UserName();\r
654           if(strcmp(curName, oldUserName)) {\r
655                 sprintf(oldUserName, "logos\\%s.bmp", curName);\r
656                 userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
657                 strcpy(oldUserName, curName);\r
658           }\r
659     }\r
660 }\r
661 \r
662 BOOL\r
663 InitApplication(HINSTANCE hInstance)\r
664 {\r
665   WNDCLASS wc;\r
666 \r
667   /* Fill in window class structure with parameters that describe the */\r
668   /* main window. */\r
669 \r
670   wc.style         = CS_HREDRAW | CS_VREDRAW; /* Class style(s). */\r
671   wc.lpfnWndProc   = (WNDPROC)WndProc;  /* Window Procedure */\r
672   wc.cbClsExtra    = 0;                 /* No per-class extra data. */\r
673   wc.cbWndExtra    = 0;                 /* No per-window extra data. */\r
674   wc.hInstance     = hInstance;         /* Owner of this class */\r
675   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
676   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);       /* Cursor */\r
677   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);  /* Default color */\r
678   wc.lpszMenuName  = szAppName;                 /* Menu name from .RC */\r
679   wc.lpszClassName = szAppName;                 /* Name to register as */\r
680 \r
681   /* Register the window class and return success/failure code. */\r
682   if (!RegisterClass(&wc)) return FALSE;\r
683 \r
684   wc.style         = CS_HREDRAW | CS_VREDRAW;\r
685   wc.lpfnWndProc   = (WNDPROC)ConsoleWndProc;\r
686   wc.cbClsExtra    = 0;\r
687   wc.cbWndExtra    = DLGWINDOWEXTRA;\r
688   wc.hInstance     = hInstance;\r
689   wc.hIcon         = LoadIcon(hInstance, "icon_white");\r
690   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);\r
691   wc.hbrBackground = (HBRUSH)(COLOR_MENU+1);\r
692   wc.lpszMenuName  = NULL;\r
693   wc.lpszClassName = szConsoleName;\r
694 \r
695   if (!RegisterClass(&wc)) return FALSE;\r
696   return TRUE;\r
697 }\r
698 \r
699 \r
700 /* Set by InitInstance, used by EnsureOnScreen */\r
701 int screenHeight, screenWidth;\r
702 \r
703 void\r
704 EnsureOnScreen(int *x, int *y, int minX, int minY)\r
705 {\r
706 //  int gap = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);\r
707   /* Be sure window at (x,y) is not off screen (or even mostly off screen) */\r
708   if (*x > screenWidth - 32) *x = 0;\r
709   if (*y > screenHeight - 32) *y = 0;\r
710   if (*x < minX) *x = minX;\r
711   if (*y < minY) *y = minY;\r
712 }\r
713 \r
714 BOOL\r
715 InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)\r
716 {\r
717   HWND hwnd; /* Main window handle. */\r
718   int ibs;\r
719   WINDOWPLACEMENT wp;\r
720   char *filepart;\r
721 \r
722   hInst = hInstance;    /* Store instance handle in our global variable */\r
723 \r
724   if (SearchPath(NULL, "WinBoard.exe", NULL, MSG_SIZ, installDir, &filepart)) {\r
725     *filepart = NULLCHAR;\r
726   } else {\r
727     GetCurrentDirectory(MSG_SIZ, installDir);\r
728   }\r
729   gameInfo.boardWidth = gameInfo.boardHeight = 8; // [HGM] won't have open window otherwise\r
730   screenWidth = screenHeight = 1000; // [HGM] placement: kludge to allow calling EnsureOnScreen from InitAppData\r
731   InitAppData(lpCmdLine);      /* Get run-time parameters */\r
732   if (appData.debugMode) {\r
733     debugFP = fopen(appData.nameOfDebugFile, "w");\r
734     setbuf(debugFP, NULL);\r
735   }\r
736 \r
737   InitBackEnd1();\r
738 \r
739 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
740 //  InitEngineUCI( installDir, &second );\r
741 \r
742   /* Create a main window for this application instance. */\r
743   hwnd = CreateWindow(szAppName, szTitle,\r
744                       (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX),\r
745                       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,\r
746                       NULL, NULL, hInstance, NULL);\r
747   hwndMain = hwnd;\r
748 \r
749   /* If window could not be created, return "failure" */\r
750   if (!hwnd) {\r
751     return (FALSE);\r
752   }\r
753 \r
754   /* [HGM] logo: Load logos if specified (must be done before InitDrawingSizes) */\r
755   if( appData.firstLogo && appData.firstLogo[0] != NULLCHAR) {\r
756       first.programLogo = LoadImage( 0, appData.firstLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
757 \r
758       if (first.programLogo == NULL && appData.debugMode) {\r
759           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.firstLogo );\r
760       }\r
761   } else if(appData.autoLogo) {\r
762       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
763         char buf[MSG_SIZ];\r
764         sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
765         first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
766       }\r
767   }\r
768 \r
769   if( appData.secondLogo && appData.secondLogo[0] != NULLCHAR) {\r
770       second.programLogo = LoadImage( 0, appData.secondLogo, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
771 \r
772       if (second.programLogo == NULL && appData.debugMode) {\r
773           fprintf( debugFP, "Unable to load logo bitmap '%s'\n", appData.secondLogo );\r
774       }\r
775   } else if(appData.autoLogo) {\r
776       char buf[MSG_SIZ];\r
777       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
778         sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
779         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
780       } else\r
781       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
782         sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
783         second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
784       }\r
785   }\r
786 \r
787   SetUserLogo();\r
788 \r
789   iconWhite = LoadIcon(hInstance, "icon_white");\r
790   iconBlack = LoadIcon(hInstance, "icon_black");\r
791   iconCurrent = iconWhite;\r
792   InitDrawingColors();\r
793   screenHeight = GetSystemMetrics(SM_CYSCREEN);\r
794   screenWidth = GetSystemMetrics(SM_CXSCREEN);\r
795   for (ibs = (int) NUM_SIZES - 1; ibs >= 0; ibs--) {\r
796     /* Compute window size for each board size, and use the largest\r
797        size that fits on this screen as the default. */\r
798     InitDrawingSizes((BoardSize)(ibs+1000), 0);\r
799     if (boardSize == (BoardSize)-1 &&\r
800         winH <= screenHeight\r
801            - GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYCAPTION) - 10\r
802         && winW <= screenWidth) {\r
803       boardSize = (BoardSize)ibs;\r
804     }\r
805   }\r
806 \r
807   InitDrawingSizes(boardSize, 0);\r
808   InitMenuChecks();\r
809   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
810 \r
811   /* [AS] Load textures if specified */\r
812   ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
813   \r
814   if( appData.liteBackTextureFile && appData.liteBackTextureFile[0] != NULLCHAR && appData.liteBackTextureFile[0] != '*' ) {\r
815       liteBackTexture = LoadImage( 0, appData.liteBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
816       liteBackTextureMode = appData.liteBackTextureMode;\r
817 \r
818       if (liteBackTexture == NULL && appData.debugMode) {\r
819           fprintf( debugFP, "Unable to load lite texture bitmap '%s'\n", appData.liteBackTextureFile );\r
820       }\r
821   }\r
822   \r
823   if( appData.darkBackTextureFile && appData.darkBackTextureFile[0] != NULLCHAR && appData.darkBackTextureFile[0] != '*' ) {\r
824       darkBackTexture = LoadImage( 0, appData.darkBackTextureFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
825       darkBackTextureMode = appData.darkBackTextureMode;\r
826 \r
827       if (darkBackTexture == NULL && appData.debugMode) {\r
828           fprintf( debugFP, "Unable to load dark texture bitmap '%s'\n", appData.darkBackTextureFile );\r
829       }\r
830   }\r
831 \r
832   mysrandom( (unsigned) time(NULL) );\r
833 \r
834   /* [AS] Restore layout */\r
835   if( wpMoveHistory.visible ) {\r
836       MoveHistoryPopUp();\r
837   }\r
838 \r
839   if( wpEvalGraph.visible ) {\r
840       EvalGraphPopUp();\r
841   }\r
842 \r
843   if( wpEngineOutput.visible ) {\r
844       EngineOutputPopUp();\r
845   }\r
846 \r
847   InitBackEnd2();\r
848 \r
849   /* Make the window visible; update its client area; and return "success" */\r
850   EnsureOnScreen(&boardX, &boardY, minX, minY);\r
851   wp.length = sizeof(WINDOWPLACEMENT);\r
852   wp.flags = 0;\r
853   wp.showCmd = nCmdShow;\r
854   wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
855   wp.rcNormalPosition.left = boardX;\r
856   wp.rcNormalPosition.right = boardX + winWidth;\r
857   wp.rcNormalPosition.top = boardY;\r
858   wp.rcNormalPosition.bottom = boardY + winHeight;\r
859   SetWindowPlacement(hwndMain, &wp);\r
860 \r
861   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
862                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
863 \r
864   if (hwndConsole) {\r
865 #if AOT_CONSOLE\r
866     SetWindowPos(hwndConsole, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
867                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
868 #endif\r
869     ShowWindow(hwndConsole, nCmdShow);\r
870   }\r
871   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
872 \r
873   return TRUE;\r
874 \r
875 }\r
876 \r
877 \r
878 typedef enum {\r
879   ArgString, ArgInt, ArgFloat, ArgBoolean, ArgTrue, ArgFalse, ArgNone, \r
880   ArgColor, ArgAttribs, ArgFilename, ArgBoardSize, ArgFont, ArgCommSettings,\r
881   ArgSettingsFilename,\r
882   ArgX, ArgY, ArgZ // [HGM] placement: for window-placement options stored relative to main window\r
883 } ArgType;\r
884 \r
885 typedef struct {\r
886   char *argName;\r
887   ArgType argType;\r
888   /***\r
889   union {\r
890     String *pString;       // ArgString\r
891     int *pInt;             // ArgInt\r
892     float *pFloat;         // ArgFloat\r
893     Boolean *pBoolean;     // ArgBoolean\r
894     COLORREF *pColor;      // ArgColor\r
895     ColorClass cc;         // ArgAttribs\r
896     String *pFilename;     // ArgFilename\r
897     BoardSize *pBoardSize; // ArgBoardSize\r
898     int whichFont;         // ArgFont\r
899     DCB *pDCB;             // ArgCommSettings\r
900     String *pFilename;     // ArgSettingsFilename\r
901   } argLoc;\r
902   ***/\r
903   LPVOID argLoc;\r
904   BOOL save;\r
905 } ArgDescriptor;\r
906 \r
907 int junk;\r
908 ArgDescriptor argDescriptors[] = {\r
909   /* positional arguments */\r
910   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
911   { "", ArgNone, NULL },\r
912   /* keyword arguments */\r
913   { "whitePieceColor", ArgColor, (LPVOID) &whitePieceColor, TRUE },\r
914   { "wpc", ArgColor, (LPVOID) &whitePieceColor, FALSE },\r
915   { "blackPieceColor", ArgColor, (LPVOID) &blackPieceColor, TRUE },\r
916   { "bpc", ArgColor, (LPVOID) &blackPieceColor, FALSE },\r
917   { "lightSquareColor", ArgColor, (LPVOID) &lightSquareColor, TRUE },\r
918   { "lsc", ArgColor, (LPVOID) &lightSquareColor, FALSE },\r
919   { "darkSquareColor", ArgColor, (LPVOID) &darkSquareColor, TRUE },\r
920   { "dsc", ArgColor, (LPVOID) &darkSquareColor, FALSE },\r
921   { "highlightSquareColor", ArgColor, (LPVOID) &highlightSquareColor, TRUE },\r
922   { "hsc", ArgColor, (LPVOID) &highlightSquareColor, FALSE },\r
923   { "premoveHighlightColor", ArgColor, (LPVOID) &premoveHighlightColor, TRUE },\r
924   { "phc", ArgColor, (LPVOID) &premoveHighlightColor, FALSE },\r
925   { "movesPerSession", ArgInt, (LPVOID) &appData.movesPerSession, TRUE },\r
926   { "mps", ArgInt, (LPVOID) &appData.movesPerSession, FALSE },\r
927   { "initString", ArgString, (LPVOID) &appData.initString, FALSE },\r
928   { "firstInitString", ArgString, (LPVOID) &appData.initString, FALSE },\r
929   { "secondInitString", ArgString, (LPVOID) &appData.secondInitString, FALSE },\r
930   { "firstComputerString", ArgString, (LPVOID) &appData.firstComputerString,\r
931     FALSE },\r
932   { "secondComputerString", ArgString, (LPVOID) &appData.secondComputerString,\r
933     FALSE },\r
934   { "firstChessProgram", ArgFilename, (LPVOID) &appData.firstChessProgram,\r
935     FALSE },\r
936   { "fcp", ArgFilename, (LPVOID) &appData.firstChessProgram, FALSE },\r
937   { "secondChessProgram", ArgFilename, (LPVOID) &appData.secondChessProgram,\r
938     FALSE },\r
939   { "scp", ArgFilename, (LPVOID) &appData.secondChessProgram, FALSE },\r
940   { "firstPlaysBlack", ArgBoolean, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
941   { "fb", ArgTrue, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
942   { "xfb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
943   { "-fb", ArgFalse, (LPVOID) &appData.firstPlaysBlack, FALSE },\r
944   { "noChessProgram", ArgBoolean, (LPVOID) &appData.noChessProgram, FALSE },\r
945   { "ncp", ArgTrue, (LPVOID) &appData.noChessProgram, FALSE },\r
946   { "xncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
947   { "-ncp", ArgFalse, (LPVOID) &appData.noChessProgram, FALSE },\r
948   { "firstHost", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
949   { "fh", ArgString, (LPVOID) &appData.firstHost, FALSE },\r
950   { "secondHost", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
951   { "sh", ArgString, (LPVOID) &appData.secondHost, FALSE },\r
952   { "firstDirectory", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
953   { "fd", ArgFilename, (LPVOID) &appData.firstDirectory, FALSE },\r
954   { "secondDirectory", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
955   { "sd", ArgFilename, (LPVOID) &appData.secondDirectory, FALSE },\r
956   /*!!bitmapDirectory?*/\r
957   { "remoteShell", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
958   { "rsh", ArgFilename, (LPVOID) &appData.remoteShell, FALSE },\r
959   { "remoteUser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
960   { "ruser", ArgString, (LPVOID) &appData.remoteUser, FALSE },\r
961   { "timeDelay", ArgFloat, (LPVOID) &appData.timeDelay, TRUE },\r
962   { "td", ArgFloat, (LPVOID) &appData.timeDelay, FALSE },\r
963   { "timeControl", ArgString, (LPVOID) &appData.timeControl, TRUE },\r
964   { "tc", ArgString, (LPVOID) &appData.timeControl, FALSE },\r
965   { "timeIncrement", ArgInt, (LPVOID) &appData.timeIncrement, TRUE },\r
966   { "inc", ArgInt, (LPVOID) &appData.timeIncrement, FALSE },\r
967   { "internetChessServerMode", ArgBoolean, (LPVOID) &appData.icsActive, FALSE },\r
968   { "ics", ArgTrue, (LPVOID) &appData.icsActive, FALSE },\r
969   { "xics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
970   { "-ics", ArgFalse, (LPVOID) &appData.icsActive, FALSE },\r
971   { "internetChessServerHost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
972   { "icshost", ArgString, (LPVOID) &appData.icsHost, FALSE },\r
973   { "internetChessServerPort", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
974   { "icsport", ArgString, (LPVOID) &appData.icsPort, FALSE },\r
975   { "internetChessServerCommPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
976   { "icscomm", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
977   { "internetChessServerComPort", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
978   { "icscom", ArgString, (LPVOID) &appData.icsCommPort, FALSE },\r
979   { "internetChessServerLogonScript", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
980   { "icslogon", ArgFilename, (LPVOID) &appData.icsLogon, FALSE },\r
981   { "useTelnet", ArgBoolean, (LPVOID) &appData.useTelnet, FALSE },\r
982   { "telnet", ArgTrue, (LPVOID) &appData.useTelnet, FALSE },\r
983   { "xtelnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
984   { "-telnet", ArgFalse, (LPVOID) &appData.useTelnet, FALSE },\r
985   { "telnetProgram", ArgFilename, (LPVOID) &appData.telnetProgram, FALSE },\r
986   { "icshelper", ArgFilename, (LPVOID) &appData.icsHelper, FALSE },\r
987   { "gateway", ArgString, (LPVOID) &appData.gateway, FALSE },\r
988   { "loadGameFile", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
989   { "lgf", ArgFilename, (LPVOID) &appData.loadGameFile, FALSE },\r
990   { "loadGameIndex", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
991   { "lgi", ArgInt, (LPVOID) &appData.loadGameIndex, FALSE },\r
992   { "saveGameFile", ArgFilename, (LPVOID) &appData.saveGameFile, TRUE },\r
993   { "sgf", ArgFilename, (LPVOID) &appData.saveGameFile, FALSE },\r
994   { "autoSaveGames", ArgBoolean, (LPVOID) &appData.autoSaveGames, TRUE },\r
995   { "autosave", ArgTrue, (LPVOID) &appData.autoSaveGames, FALSE },\r
996   { "xautosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
997   { "-autosave", ArgFalse, (LPVOID) &appData.autoSaveGames, FALSE },\r
998   { "loadPositionFile", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
999   { "lpf", ArgFilename, (LPVOID) &appData.loadPositionFile, FALSE },\r
1000   { "loadPositionIndex", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1001   { "lpi", ArgInt, (LPVOID) &appData.loadPositionIndex, FALSE },\r
1002   { "savePositionFile", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1003   { "spf", ArgFilename, (LPVOID) &appData.savePositionFile, FALSE },\r
1004   { "matchMode", ArgBoolean, (LPVOID) &appData.matchMode, FALSE },\r
1005   { "mm", ArgTrue, (LPVOID) &appData.matchMode, FALSE },\r
1006   { "xmm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1007   { "-mm", ArgFalse, (LPVOID) &appData.matchMode, FALSE },\r
1008   { "matchGames", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1009   { "mg", ArgInt, (LPVOID) &appData.matchGames, FALSE },\r
1010   { "monoMode", ArgBoolean, (LPVOID) &appData.monoMode, TRUE },\r
1011   { "mono", ArgTrue, (LPVOID) &appData.monoMode, FALSE },\r
1012   { "xmono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1013   { "-mono", ArgFalse, (LPVOID) &appData.monoMode, FALSE },\r
1014   { "debugMode", ArgBoolean, (LPVOID) &appData.debugMode, FALSE },\r
1015   { "debug", ArgTrue, (LPVOID) &appData.debugMode, FALSE },\r
1016   { "xdebug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1017   { "-debug", ArgFalse, (LPVOID) &appData.debugMode, FALSE },\r
1018   { "clockMode", ArgBoolean, (LPVOID) &appData.clockMode, FALSE },\r
1019   { "clock", ArgTrue, (LPVOID) &appData.clockMode, FALSE },\r
1020   { "xclock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1021   { "-clock", ArgFalse, (LPVOID) &appData.clockMode, FALSE },\r
1022   { "searchTime", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1023   { "st", ArgString, (LPVOID) &appData.searchTime, FALSE },\r
1024   { "searchDepth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1025   { "depth", ArgInt, (LPVOID) &appData.searchDepth, FALSE },\r
1026   { "showCoords", ArgBoolean, (LPVOID) &appData.showCoords, TRUE },\r
1027   { "coords", ArgTrue, (LPVOID) &appData.showCoords, FALSE },\r
1028   { "xcoords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1029   { "-coords", ArgFalse, (LPVOID) &appData.showCoords, FALSE },\r
1030   { "showThinking", ArgBoolean, (LPVOID) &appData.showThinking, TRUE },\r
1031   { "thinking", ArgTrue, (LPVOID) &appData.showThinking, FALSE },\r
1032   { "xthinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1033   { "-thinking", ArgFalse, (LPVOID) &appData.showThinking, FALSE },\r
1034   { "ponderNextMove", ArgBoolean, (LPVOID) &appData.ponderNextMove, TRUE },\r
1035   { "ponder", ArgTrue, (LPVOID) &appData.ponderNextMove, FALSE },\r
1036   { "xponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1037   { "-ponder", ArgFalse, (LPVOID) &appData.ponderNextMove, FALSE },\r
1038   { "periodicUpdates", ArgBoolean, (LPVOID) &appData.periodicUpdates, TRUE },\r
1039   { "periodic", ArgTrue, (LPVOID) &appData.periodicUpdates, FALSE },\r
1040   { "xperiodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1041   { "-periodic", ArgFalse, (LPVOID) &appData.periodicUpdates, FALSE },\r
1042   { "popupExitMessage", ArgBoolean, (LPVOID) &appData.popupExitMessage, TRUE },\r
1043   { "exit", ArgTrue, (LPVOID) &appData.popupExitMessage, FALSE },\r
1044   { "xexit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1045   { "-exit", ArgFalse, (LPVOID) &appData.popupExitMessage, FALSE },\r
1046   { "popupMoveErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, TRUE },\r
1047   { "popup", ArgTrue, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1048   { "xpopup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1049   { "-popup", ArgFalse, (LPVOID) &appData.popupMoveErrors, FALSE },\r
1050   { "popUpErrors", ArgBoolean, (LPVOID) &appData.popupMoveErrors, \r
1051     FALSE }, /* only so that old WinBoard.ini files from betas can be read */\r
1052   { "clockFont", ArgFont, (LPVOID) CLOCK_FONT, TRUE },\r
1053   { "messageFont", ArgFont, (LPVOID) MESSAGE_FONT, TRUE },\r
1054   { "coordFont", ArgFont, (LPVOID) COORD_FONT, TRUE },\r
1055   { "tagsFont", ArgFont, (LPVOID) EDITTAGS_FONT, TRUE },\r
1056   { "commentFont", ArgFont, (LPVOID) COMMENT_FONT, TRUE },\r
1057   { "icsFont", ArgFont, (LPVOID) CONSOLE_FONT, TRUE },\r
1058   { "moveHistoryFont", ArgFont, (LPVOID) MOVEHISTORY_FONT, TRUE }, /* [AS] */\r
1059   { "boardSize", ArgBoardSize, (LPVOID) &boardSize,\r
1060     TRUE }, /* must come after all fonts */\r
1061   { "size", ArgBoardSize, (LPVOID) &boardSize, FALSE },\r
1062   { "ringBellAfterMoves", ArgBoolean, (LPVOID) &appData.ringBellAfterMoves,\r
1063     FALSE }, /* historical; kept only so old winboard.ini files will parse */\r
1064   { "alwaysOnTop", ArgBoolean, (LPVOID) &alwaysOnTop, TRUE },\r
1065   { "top", ArgTrue, (LPVOID) &alwaysOnTop, FALSE },\r
1066   { "xtop", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1067   { "-top", ArgFalse, (LPVOID) &alwaysOnTop, FALSE },\r
1068   { "autoCallFlag", ArgBoolean, (LPVOID) &appData.autoCallFlag, TRUE },\r
1069   { "autoflag", ArgTrue, (LPVOID) &appData.autoCallFlag, FALSE },\r
1070   { "xautoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1071   { "-autoflag", ArgFalse, (LPVOID) &appData.autoCallFlag, FALSE },\r
1072   { "autoComment", ArgBoolean, (LPVOID) &appData.autoComment, TRUE },\r
1073   { "autocomm", ArgTrue, (LPVOID) &appData.autoComment, FALSE },\r
1074   { "xautocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1075   { "-autocomm", ArgFalse, (LPVOID) &appData.autoComment, FALSE },\r
1076   { "autoObserve", ArgBoolean, (LPVOID) &appData.autoObserve, TRUE },\r
1077   { "autobs", ArgTrue, (LPVOID) &appData.autoObserve, FALSE },\r
1078   { "xautobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1079   { "-autobs", ArgFalse, (LPVOID) &appData.autoObserve, FALSE },\r
1080   { "flipView", ArgBoolean, (LPVOID) &appData.flipView, FALSE },\r
1081   { "flip", ArgTrue, (LPVOID) &appData.flipView, FALSE },\r
1082   { "xflip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1083   { "-flip", ArgFalse, (LPVOID) &appData.flipView, FALSE },\r
1084   { "autoFlipView", ArgBoolean, (LPVOID) &appData.autoFlipView, TRUE },\r
1085   { "autoflip", ArgTrue, (LPVOID) &appData.autoFlipView, FALSE },\r
1086   { "xautoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1087   { "-autoflip", ArgFalse, (LPVOID) &appData.autoFlipView, FALSE },\r
1088   { "autoRaiseBoard", ArgBoolean, (LPVOID) &appData.autoRaiseBoard, TRUE },\r
1089   { "autoraise", ArgTrue, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1090   { "xautoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1091   { "-autoraise", ArgFalse, (LPVOID) &appData.autoRaiseBoard, FALSE },\r
1092 #if 0\r
1093   { "cmailGameName", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1094   { "cmail", ArgString, (LPVOID) &appData.cmailGameName, FALSE },\r
1095 #endif\r
1096   { "alwaysPromoteToQueen", ArgBoolean, (LPVOID) &appData.alwaysPromoteToQueen, TRUE },\r
1097   { "queen", ArgTrue, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1098   { "xqueen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1099   { "-queen", ArgFalse, (LPVOID) &appData.alwaysPromoteToQueen, FALSE },\r
1100   { "oldSaveStyle", ArgBoolean, (LPVOID) &appData.oldSaveStyle, TRUE },\r
1101   { "oldsave", ArgTrue, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1102   { "xoldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1103   { "-oldsave", ArgFalse, (LPVOID) &appData.oldSaveStyle, FALSE },\r
1104   { "quietPlay", ArgBoolean, (LPVOID) &appData.quietPlay, TRUE },\r
1105   { "quiet", ArgTrue, (LPVOID) &appData.quietPlay, FALSE },\r
1106   { "xquiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1107   { "-quiet", ArgFalse, (LPVOID) &appData.quietPlay, FALSE },\r
1108   { "getMoveList", ArgBoolean, (LPVOID) &appData.getMoveList, TRUE },\r
1109   { "moves", ArgTrue, (LPVOID) &appData.getMoveList, FALSE },\r
1110   { "xmoves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1111   { "-moves", ArgFalse, (LPVOID) &appData.getMoveList, FALSE },\r
1112   { "testLegality", ArgBoolean, (LPVOID) &appData.testLegality, TRUE },\r
1113   { "legal", ArgTrue, (LPVOID) &appData.testLegality, FALSE },\r
1114   { "xlegal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1115   { "-legal", ArgFalse, (LPVOID) &appData.testLegality, FALSE },\r
1116   { "premove", ArgBoolean, (LPVOID) &appData.premove, TRUE },\r
1117   { "pre", ArgTrue, (LPVOID) &appData.premove, FALSE },\r
1118   { "xpre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1119   { "-pre", ArgFalse, (LPVOID) &appData.premove, FALSE },\r
1120   { "premoveWhite", ArgBoolean, (LPVOID) &appData.premoveWhite, TRUE },\r
1121   { "prewhite", ArgTrue, (LPVOID) &appData.premoveWhite, FALSE },\r
1122   { "xprewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1123   { "-prewhite", ArgFalse, (LPVOID) &appData.premoveWhite, FALSE },\r
1124   { "premoveWhiteText", ArgString, (LPVOID) &appData.premoveWhiteText, TRUE },\r
1125   { "premoveBlack", ArgBoolean, (LPVOID) &appData.premoveBlack, TRUE },\r
1126   { "preblack", ArgTrue, (LPVOID) &appData.premoveBlack, FALSE },\r
1127   { "xpreblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1128   { "-preblack", ArgFalse, (LPVOID) &appData.premoveBlack, FALSE },\r
1129   { "premoveBlackText", ArgString, (LPVOID) &appData.premoveBlackText, TRUE },\r
1130   { "icsAlarm", ArgBoolean, (LPVOID) &appData.icsAlarm, TRUE},\r
1131   { "alarm", ArgTrue, (LPVOID) &appData.icsAlarm, FALSE},\r
1132   { "xalarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1133   { "-alarm", ArgFalse, (LPVOID) &appData.icsAlarm, FALSE},\r
1134   { "icsAlarmTime", ArgInt, (LPVOID) &appData.icsAlarmTime, TRUE},\r
1135   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1136   { "localLineEditing", ArgBoolean, (LPVOID) &appData.localLineEditing, FALSE},\r
1137   { "edit", ArgTrue, (LPVOID) &appData.localLineEditing, FALSE },\r
1138   { "xedit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1139   { "-edit", ArgFalse, (LPVOID) &appData.localLineEditing, FALSE },\r
1140   { "animateMoving", ArgBoolean, (LPVOID) &appData.animate, TRUE },\r
1141   { "animate", ArgTrue, (LPVOID) &appData.animate, FALSE },\r
1142   { "xanimate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1143   { "-animate", ArgFalse, (LPVOID) &appData.animate, FALSE },\r
1144   { "animateSpeed", ArgInt, (LPVOID) &appData.animSpeed, TRUE },\r
1145   { "animateDragging", ArgBoolean, (LPVOID) &appData.animateDragging, TRUE },\r
1146   { "drag", ArgTrue, (LPVOID) &appData.animateDragging, FALSE },\r
1147   { "xdrag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1148   { "-drag", ArgFalse, (LPVOID) &appData.animateDragging, FALSE },\r
1149   { "blindfold", ArgBoolean, (LPVOID) &appData.blindfold, TRUE },\r
1150   { "blind", ArgTrue, (LPVOID) &appData.blindfold, FALSE },\r
1151   { "xblind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1152   { "-blind", ArgFalse, (LPVOID) &appData.blindfold, FALSE },\r
1153   { "highlightLastMove", ArgBoolean,\r
1154     (LPVOID) &appData.highlightLastMove, TRUE },\r
1155   { "highlight", ArgTrue, (LPVOID) &appData.highlightLastMove, FALSE },\r
1156   { "xhighlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1157   { "-highlight", ArgFalse, (LPVOID) &appData.highlightLastMove, FALSE },\r
1158   { "highlightDragging", ArgBoolean,\r
1159     (LPVOID) &appData.highlightDragging, TRUE },\r
1160   { "highdrag", ArgTrue, (LPVOID) &appData.highlightDragging, FALSE },\r
1161   { "xhighdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1162   { "-highdrag", ArgFalse, (LPVOID) &appData.highlightDragging, FALSE },\r
1163   { "colorizeMessages", ArgBoolean, (LPVOID) &appData.colorize, TRUE },\r
1164   { "colorize", ArgTrue, (LPVOID) &appData.colorize, FALSE },\r
1165   { "xcolorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1166   { "-colorize", ArgFalse, (LPVOID) &appData.colorize, FALSE },\r
1167   { "colorShout", ArgAttribs, (LPVOID) ColorShout, TRUE },\r
1168   { "colorSShout", ArgAttribs, (LPVOID) ColorSShout, TRUE },\r
1169   { "colorChannel1", ArgAttribs, (LPVOID) ColorChannel1, TRUE },\r
1170   { "colorChannel", ArgAttribs, (LPVOID) ColorChannel, TRUE },\r
1171   { "colorKibitz", ArgAttribs, (LPVOID) ColorKibitz, TRUE },\r
1172   { "colorTell", ArgAttribs, (LPVOID) ColorTell, TRUE },\r
1173   { "colorChallenge", ArgAttribs, (LPVOID) ColorChallenge, TRUE },\r
1174   { "colorRequest", ArgAttribs, (LPVOID) ColorRequest, TRUE },\r
1175   { "colorSeek", ArgAttribs, (LPVOID) ColorSeek, TRUE },\r
1176   { "colorNormal", ArgAttribs, (LPVOID) ColorNormal, TRUE },\r
1177   { "colorBackground", ArgColor, (LPVOID) &consoleBackgroundColor, TRUE },\r
1178   { "soundShout", ArgFilename,\r
1179     (LPVOID) &textAttribs[ColorShout].sound.name, TRUE },\r
1180   { "soundSShout", ArgFilename,\r
1181     (LPVOID) &textAttribs[ColorSShout].sound.name, TRUE },\r
1182   { "soundChannel1", ArgFilename,\r
1183     (LPVOID) &textAttribs[ColorChannel1].sound.name, TRUE },\r
1184   { "soundChannel", ArgFilename,\r
1185     (LPVOID) &textAttribs[ColorChannel].sound.name, TRUE },\r
1186   { "soundKibitz", ArgFilename,\r
1187     (LPVOID) &textAttribs[ColorKibitz].sound.name, TRUE },\r
1188   { "soundTell", ArgFilename,\r
1189     (LPVOID) &textAttribs[ColorTell].sound.name, TRUE },\r
1190   { "soundChallenge", ArgFilename,\r
1191     (LPVOID) &textAttribs[ColorChallenge].sound.name, TRUE },\r
1192   { "soundRequest", ArgFilename,\r
1193     (LPVOID) &textAttribs[ColorRequest].sound.name, TRUE },\r
1194   { "soundSeek", ArgFilename,\r
1195     (LPVOID) &textAttribs[ColorSeek].sound.name, TRUE },\r
1196   { "soundMove", ArgFilename, (LPVOID) &sounds[(int)SoundMove].name, TRUE },\r
1197   { "soundBell", ArgFilename, (LPVOID) &sounds[(int)SoundBell].name, TRUE },\r
1198   { "soundIcsWin", ArgFilename, (LPVOID) &sounds[(int)SoundIcsWin].name,TRUE },\r
1199   { "soundIcsLoss", ArgFilename, \r
1200     (LPVOID) &sounds[(int)SoundIcsLoss].name, TRUE },\r
1201   { "soundIcsDraw", ArgFilename, \r
1202     (LPVOID) &sounds[(int)SoundIcsDraw].name, TRUE },\r
1203   { "soundIcsUnfinished", ArgFilename, \r
1204     (LPVOID) &sounds[(int)SoundIcsUnfinished].name, TRUE},\r
1205   { "soundIcsAlarm", ArgFilename, \r
1206     (LPVOID) &sounds[(int)SoundAlarm].name, TRUE },\r
1207   { "reuseFirst", ArgBoolean, (LPVOID) &appData.reuseFirst, FALSE },\r
1208   { "reuse", ArgTrue, (LPVOID) &appData.reuseFirst, FALSE },\r
1209   { "xreuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1210   { "-reuse", ArgFalse, (LPVOID) &appData.reuseFirst, FALSE },\r
1211   { "reuseChessPrograms", ArgBoolean,\r
1212     (LPVOID) &appData.reuseFirst, FALSE }, /* backward compat only */\r
1213   { "reuseSecond", ArgBoolean, (LPVOID) &appData.reuseSecond, FALSE },\r
1214   { "reuse2", ArgTrue, (LPVOID) &appData.reuseSecond, FALSE },\r
1215   { "xreuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1216   { "-reuse2", ArgFalse, (LPVOID) &appData.reuseSecond, FALSE },\r
1217   { "comPortSettings", ArgCommSettings, (LPVOID) &dcb, TRUE },\r
1218   { "settingsFile", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1219   { "ini", ArgSettingsFilename, (LPVOID) &settingsFileName, FALSE },\r
1220   { "saveSettingsOnExit", ArgBoolean, (LPVOID) &saveSettingsOnExit, TRUE },\r
1221   { "chessProgram", ArgBoolean, (LPVOID) &chessProgram, FALSE },\r
1222   { "cp", ArgTrue, (LPVOID) &chessProgram, FALSE },\r
1223   { "xcp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1224   { "-cp", ArgFalse, (LPVOID) &chessProgram, FALSE },\r
1225   { "icsMenu", ArgString, (LPVOID) &icsTextMenuString, TRUE },\r
1226   { "icsNames", ArgString, (LPVOID) &icsNames, TRUE },\r
1227   { "firstChessProgramNames", ArgString, (LPVOID) &firstChessProgramNames,\r
1228     TRUE },\r
1229   { "secondChessProgramNames", ArgString, (LPVOID) &secondChessProgramNames,\r
1230     TRUE },\r
1231   { "initialMode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1232   { "mode", ArgString, (LPVOID) &appData.initialMode, FALSE },\r
1233   { "variant", ArgString, (LPVOID) &appData.variant, FALSE },\r
1234   { "firstProtocolVersion", ArgInt, (LPVOID) &appData.firstProtocolVersion, FALSE },\r
1235   { "secondProtocolVersion", ArgInt, (LPVOID) &appData.secondProtocolVersion,FALSE },\r
1236   { "showButtonBar", ArgBoolean, (LPVOID) &appData.showButtonBar, TRUE },\r
1237   { "buttons", ArgTrue, (LPVOID) &appData.showButtonBar, FALSE },\r
1238   { "xbuttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1239   { "-buttons", ArgFalse, (LPVOID) &appData.showButtonBar, FALSE },\r
1240   /* [AS] New features */\r
1241   { "firstScoreAbs", ArgBoolean, (LPVOID) &appData.firstScoreIsAbsolute, FALSE },\r
1242   { "secondScoreAbs", ArgBoolean, (LPVOID) &appData.secondScoreIsAbsolute, FALSE },\r
1243   { "pgnExtendedInfo", ArgBoolean, (LPVOID) &appData.saveExtendedInfoInPGN, TRUE },\r
1244   { "hideThinkingFromHuman", ArgBoolean, (LPVOID) &appData.hideThinkingFromHuman, TRUE },\r
1245   { "liteBackTextureFile", ArgString, (LPVOID) &appData.liteBackTextureFile, TRUE },\r
1246   { "darkBackTextureFile", ArgString, (LPVOID) &appData.darkBackTextureFile, TRUE },\r
1247   { "liteBackTextureMode", ArgInt, (LPVOID) &appData.liteBackTextureMode, TRUE },\r
1248   { "darkBackTextureMode", ArgInt, (LPVOID) &appData.darkBackTextureMode, TRUE },\r
1249   { "renderPiecesWithFont", ArgString, (LPVOID) &appData.renderPiecesWithFont, TRUE },\r
1250   { "fontPieceToCharTable", ArgString, (LPVOID) &appData.fontToPieceTable, TRUE },\r
1251   { "fontPieceBackColorWhite", ArgColor, (LPVOID) &appData.fontBackColorWhite, TRUE },\r
1252   { "fontPieceForeColorWhite", ArgColor, (LPVOID) &appData.fontForeColorWhite, TRUE },\r
1253   { "fontPieceBackColorBlack", ArgColor, (LPVOID) &appData.fontBackColorBlack, TRUE },\r
1254   { "fontPieceForeColorBlack", ArgColor, (LPVOID) &appData.fontForeColorBlack, TRUE },\r
1255   { "fontPieceSize", ArgInt, (LPVOID) &appData.fontPieceSize, TRUE },\r
1256   { "overrideLineGap", ArgInt, (LPVOID) &appData.overrideLineGap, TRUE },\r
1257   { "adjudicateLossThreshold", ArgInt, (LPVOID) &appData.adjudicateLossThreshold, TRUE },\r
1258   { "delayBeforeQuit", ArgInt, (LPVOID) &appData.delayBeforeQuit, TRUE },\r
1259   { "delayAfterQuit", ArgInt, (LPVOID) &appData.delayAfterQuit, TRUE },\r
1260   { "nameOfDebugFile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1261   { "debugfile", ArgFilename, (LPVOID) &appData.nameOfDebugFile, FALSE },\r
1262   { "pgnEventHeader", ArgString, (LPVOID) &appData.pgnEventHeader, TRUE },\r
1263   { "defaultFrcPosition", ArgInt, (LPVOID) &appData.defaultFrcPosition, TRUE },\r
1264   { "gameListTags", ArgString, (LPVOID) &appData.gameListTags, TRUE },\r
1265   { "saveOutOfBookInfo", ArgBoolean, (LPVOID) &appData.saveOutOfBookInfo, TRUE },\r
1266   { "showEvalInMoveHistory", ArgBoolean, (LPVOID) &appData.showEvalInMoveHistory, TRUE },\r
1267   { "evalHistColorWhite", ArgColor, (LPVOID) &appData.evalHistColorWhite, TRUE },\r
1268   { "evalHistColorBlack", ArgColor, (LPVOID) &appData.evalHistColorBlack, TRUE },\r
1269   { "highlightMoveWithArrow", ArgBoolean, (LPVOID) &appData.highlightMoveWithArrow, TRUE },\r
1270   { "highlightArrowColor", ArgColor, (LPVOID) &appData.highlightArrowColor, TRUE },\r
1271   { "stickyWindows", ArgBoolean, (LPVOID) &appData.useStickyWindows, TRUE },\r
1272   { "adjudicateDrawMoves", ArgInt, (LPVOID) &appData.adjudicateDrawMoves, TRUE },\r
1273   { "autoDisplayComment", ArgBoolean, (LPVOID) &appData.autoDisplayComment, TRUE },\r
1274   { "autoDisplayTags", ArgBoolean, (LPVOID) &appData.autoDisplayTags, TRUE },\r
1275   { "firstIsUCI", ArgBoolean, (LPVOID) &appData.firstIsUCI, FALSE },\r
1276   { "fUCI", ArgTrue, (LPVOID) &appData.firstIsUCI, FALSE },\r
1277   { "secondIsUCI", ArgBoolean, (LPVOID) &appData.secondIsUCI, FALSE },\r
1278   { "sUCI", ArgTrue, (LPVOID) &appData.secondIsUCI, FALSE },\r
1279   { "firstHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1280   { "fNoOwnBookUCI", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1281   { "firstXBook", ArgFalse, (LPVOID) &appData.firstHasOwnBookUCI, FALSE },\r
1282   { "secondHasOwnBookUCI", ArgBoolean, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1283   { "sNoOwnBookUCI", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1284   { "secondXBook", ArgFalse, (LPVOID) &appData.secondHasOwnBookUCI, FALSE },\r
1285   { "polyglotDir", ArgFilename, (LPVOID) &appData.polyglotDir, TRUE },\r
1286   { "usePolyglotBook", ArgBoolean, (LPVOID) &appData.usePolyglotBook, TRUE },\r
1287   { "polyglotBook", ArgFilename, (LPVOID) &appData.polyglotBook, TRUE },\r
1288   { "defaultHashSize", ArgInt, (LPVOID) &appData.defaultHashSize, TRUE }, \r
1289   { "defaultCacheSizeEGTB", ArgInt, (LPVOID) &appData.defaultCacheSizeEGTB, TRUE },\r
1290   { "defaultPathEGTB", ArgFilename, (LPVOID) &appData.defaultPathEGTB, TRUE },\r
1291 \r
1292   /* [HGM] board-size, adjudication and misc. options */\r
1293   { "boardWidth", ArgInt, (LPVOID) &appData.NrFiles, TRUE },\r
1294   { "boardHeight", ArgInt, (LPVOID) &appData.NrRanks, TRUE },\r
1295   { "holdingsSize", ArgInt, (LPVOID) &appData.holdingsSize, TRUE },\r
1296   { "matchPause", ArgInt, (LPVOID) &appData.matchPause, TRUE },\r
1297   { "pieceToCharTable", ArgString, (LPVOID) &appData.pieceToCharTable, FALSE },\r
1298   { "flipBlack", ArgBoolean, (LPVOID) &appData.upsideDown, TRUE },\r
1299   { "allWhite", ArgBoolean, (LPVOID) &appData.allWhite, TRUE },\r
1300   { "alphaRank", ArgBoolean, (LPVOID) &appData.alphaRank, FALSE },\r
1301   { "firstAlphaRank", ArgBoolean, (LPVOID) &first.alphaRank, FALSE },\r
1302   { "secondAlphaRank", ArgBoolean, (LPVOID) &second.alphaRank, FALSE },\r
1303   { "testClaims", ArgBoolean, (LPVOID) &appData.testClaims, TRUE },\r
1304   { "checkMates", ArgBoolean, (LPVOID) &appData.checkMates, TRUE },\r
1305   { "materialDraws", ArgBoolean, (LPVOID) &appData.materialDraws, TRUE },\r
1306   { "trivialDraws", ArgBoolean, (LPVOID) &appData.trivialDraws, TRUE },\r
1307   { "ruleMoves", ArgInt, (LPVOID) &appData.ruleMoves, TRUE },\r
1308   { "repeatsToDraw", ArgInt, (LPVOID) &appData.drawRepeats, TRUE },\r
1309   { "autoKibitz", ArgTrue, (LPVOID) &appData.autoKibitz, FALSE },\r
1310   { "engineDebugOutput", ArgInt, (LPVOID) &appData.engineComments, FALSE },\r
1311   { "userName", ArgString, (LPVOID) &appData.userName, FALSE },\r
1312   { "rewindIndex", ArgInt, (LPVOID) &appData.rewindIndex, FALSE },\r
1313   { "sameColorGames", ArgInt, (LPVOID) &appData.sameColorGames, FALSE },\r
1314   { "smpCores", ArgInt, (LPVOID) &appData.smpCores, TRUE },\r
1315   { "egtFormats", ArgString, (LPVOID) &appData.egtFormats, TRUE },\r
1316   { "niceEngines", ArgInt, (LPVOID) &appData.niceEngines, TRUE },\r
1317   { "firstLogo", ArgFilename, (LPVOID) &appData.firstLogo, FALSE },\r
1318   { "secondLogo", ArgFilename, (LPVOID) &appData.secondLogo, FALSE },\r
1319   { "autoLogo", ArgBoolean, (LPVOID) &appData.autoLogo, TRUE },\r
1320   { "firstOptions", ArgString, (LPVOID) &appData.firstOptions, FALSE },\r
1321   { "secondOptions", ArgString, (LPVOID) &appData.secondOptions, FALSE },\r
1322   { "firstNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride1, FALSE },\r
1323   { "secondNeedsNoncompliantFEN", ArgString, (LPVOID) &appData.fenOverride2, FALSE },\r
1324 \r
1325 #ifdef ZIPPY\r
1326   { "zippyTalk", ArgBoolean, (LPVOID) &appData.zippyTalk, FALSE },\r
1327   { "zt", ArgTrue, (LPVOID) &appData.zippyTalk, FALSE },\r
1328   { "xzt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1329   { "-zt", ArgFalse, (LPVOID) &appData.zippyTalk, FALSE },\r
1330   { "zippyPlay", ArgBoolean, (LPVOID) &appData.zippyPlay, FALSE },\r
1331   { "zp", ArgTrue, (LPVOID) &appData.zippyPlay, FALSE },\r
1332   { "xzp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1333   { "-zp", ArgFalse, (LPVOID) &appData.zippyPlay, FALSE },\r
1334   { "zippyLines", ArgFilename, (LPVOID) &appData.zippyLines, FALSE },\r
1335   { "zippyPinhead", ArgString, (LPVOID) &appData.zippyPinhead, FALSE },\r
1336   { "zippyPassword", ArgString, (LPVOID) &appData.zippyPassword, FALSE },\r
1337   { "zippyPassword2", ArgString, (LPVOID) &appData.zippyPassword2, FALSE },\r
1338   { "zippyWrongPassword", ArgString, (LPVOID) &appData.zippyWrongPassword,\r
1339     FALSE },\r
1340   { "zippyAcceptOnly", ArgString, (LPVOID) &appData.zippyAcceptOnly, FALSE },\r
1341   { "zippyUseI", ArgBoolean, (LPVOID) &appData.zippyUseI, FALSE },\r
1342   { "zui", ArgTrue, (LPVOID) &appData.zippyUseI, FALSE },\r
1343   { "xzui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1344   { "-zui", ArgFalse, (LPVOID) &appData.zippyUseI, FALSE },\r
1345   { "zippyBughouse", ArgInt, (LPVOID) &appData.zippyBughouse, FALSE },\r
1346   { "zippyNoplayCrafty", ArgBoolean, (LPVOID) &appData.zippyNoplayCrafty,\r
1347     FALSE },\r
1348   { "znc", ArgTrue, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1349   { "xznc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1350   { "-znc", ArgFalse, (LPVOID) &appData.zippyNoplayCrafty, FALSE },\r
1351   { "zippyGameEnd", ArgString, (LPVOID) &appData.zippyGameEnd, FALSE },\r
1352   { "zippyGameStart", ArgString, (LPVOID) &appData.zippyGameStart, FALSE },\r
1353   { "zippyAdjourn", ArgBoolean, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1354   { "zadj", ArgTrue, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1355   { "xzadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1356   { "-zadj", ArgFalse, (LPVOID) &appData.zippyAdjourn, FALSE },\r
1357   { "zippyAbort", ArgBoolean, (LPVOID) &appData.zippyAbort, FALSE },\r
1358   { "zab", ArgTrue, (LPVOID) &appData.zippyAbort, FALSE },\r
1359   { "xzab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1360   { "-zab", ArgFalse, (LPVOID) &appData.zippyAbort, FALSE },\r
1361   { "zippyVariants", ArgString, (LPVOID) &appData.zippyVariants, FALSE },\r
1362   { "zippyMaxGames", ArgInt, (LPVOID)&appData.zippyMaxGames, FALSE },\r
1363   { "zippyReplayTimeout", ArgInt, (LPVOID)&appData.zippyReplayTimeout, FALSE },\r
1364   { "zippyShortGame", ArgInt, (LPVOID)&appData.zippyShortGame, FALSE },\r
1365   /* Kludge to allow winboard.ini files from buggy 4.0.4 to be read: */\r
1366   { "zippyReplyTimeout", ArgInt, (LPVOID)&junk, FALSE },\r
1367 #endif\r
1368   /* [HGM] options for broadcasting and time odds */\r
1369   { "serverMoves", ArgString, (LPVOID) &appData.serverMovesName, FALSE },\r
1370   { "suppressLoadMoves", ArgBoolean, (LPVOID) &appData.suppressLoadMoves, FALSE },\r
1371   { "serverPause", ArgInt, (LPVOID) &appData.serverPause, FALSE },\r
1372   { "firstTimeOdds", ArgInt, (LPVOID) &appData.firstTimeOdds, FALSE },\r
1373   { "secondTimeOdds", ArgInt, (LPVOID) &appData.secondTimeOdds, FALSE },\r
1374   { "timeOddsMode", ArgInt, (LPVOID) &appData.timeOddsMode, TRUE },\r
1375   { "firstAccumulateTC", ArgInt, (LPVOID) &appData.firstAccumulateTC, FALSE },\r
1376   { "secondAccumulateTC", ArgInt, (LPVOID) &appData.secondAccumulateTC, FALSE },\r
1377   { "firstNPS", ArgInt, (LPVOID) &appData.firstNPS, FALSE },\r
1378   { "secondNPS", ArgInt, (LPVOID) &appData.secondNPS, FALSE },\r
1379   { "noGUI", ArgTrue, (LPVOID) &appData.noGUI, FALSE },\r
1380 \r
1381   // [HGM] placement: put all window layouts last in ini file, but man X,Y before all others\r
1382   { "minX", ArgZ, (LPVOID) &minX, FALSE }, // [HGM] placement: to make suer auxialary windows can be placed\r
1383   { "minY", ArgZ, (LPVOID) &minY, FALSE },\r
1384   { "winWidth",  ArgInt, (LPVOID) &winWidth,  TRUE }, // [HGM] placement: dummies to remember right & bottom\r
1385   { "winHeight", ArgInt, (LPVOID) &winHeight, TRUE }, //       for attaching auxiliary windows to them\r
1386   { "x", ArgInt, (LPVOID) &boardX, TRUE },\r
1387   { "y", ArgInt, (LPVOID) &boardY, TRUE },\r
1388   { "icsX", ArgX,   (LPVOID) &wpConsole.x, TRUE },\r
1389   { "icsY", ArgY,   (LPVOID) &wpConsole.y, TRUE },\r
1390   { "icsW", ArgInt, (LPVOID) &wpConsole.width, TRUE },\r
1391   { "icsH", ArgInt, (LPVOID) &wpConsole.height, TRUE },\r
1392   { "analysisX", ArgX,   (LPVOID) &analysisX, FALSE }, // [HGM] placement: analysis window no longer exists\r
1393   { "analysisY", ArgY,   (LPVOID) &analysisY, FALSE }, //       provided for compatibility with old ini files\r
1394   { "analysisW", ArgInt, (LPVOID) &analysisW, FALSE },\r
1395   { "analysisH", ArgInt, (LPVOID) &analysisH, FALSE },\r
1396   { "commentX", ArgX,   (LPVOID) &commentX, TRUE },\r
1397   { "commentY", ArgY,   (LPVOID) &commentY, TRUE },\r
1398   { "commentW", ArgInt, (LPVOID) &commentW, TRUE },\r
1399   { "commentH", ArgInt, (LPVOID) &commentH, TRUE },\r
1400   { "tagsX", ArgX,   (LPVOID) &editTagsX, TRUE },\r
1401   { "tagsY", ArgY,   (LPVOID) &editTagsY, TRUE },\r
1402   { "tagsW", ArgInt, (LPVOID) &editTagsW, TRUE },\r
1403   { "tagsH", ArgInt, (LPVOID) &editTagsH, TRUE },\r
1404   { "gameListX", ArgX,   (LPVOID) &wpGameList.x, TRUE },\r
1405   { "gameListY", ArgY,   (LPVOID) &wpGameList.y, TRUE },\r
1406   { "gameListW", ArgInt, (LPVOID) &wpGameList.width, TRUE },\r
1407   { "gameListH", ArgInt, (LPVOID) &wpGameList.height, TRUE },\r
1408   /* [AS] Layout stuff */\r
1409   { "moveHistoryUp", ArgBoolean, (LPVOID) &wpMoveHistory.visible, TRUE },\r
1410   { "moveHistoryX", ArgX,   (LPVOID) &wpMoveHistory.x, TRUE },\r
1411   { "moveHistoryY", ArgY,   (LPVOID) &wpMoveHistory.y, TRUE },\r
1412   { "moveHistoryW", ArgInt, (LPVOID) &wpMoveHistory.width, TRUE },\r
1413   { "moveHistoryH", ArgInt, (LPVOID) &wpMoveHistory.height, TRUE },\r
1414 \r
1415   { "evalGraphUp", ArgBoolean, (LPVOID) &wpEvalGraph.visible, TRUE },\r
1416   { "evalGraphX", ArgX,   (LPVOID) &wpEvalGraph.x, TRUE },\r
1417   { "evalGraphY", ArgY,   (LPVOID) &wpEvalGraph.y, TRUE },\r
1418   { "evalGraphW", ArgInt, (LPVOID) &wpEvalGraph.width, TRUE },\r
1419   { "evalGraphH", ArgInt, (LPVOID) &wpEvalGraph.height, TRUE },\r
1420 \r
1421   { "engineOutputUp", ArgBoolean, (LPVOID) &wpEngineOutput.visible, TRUE },\r
1422   { "engineOutputX", ArgX,   (LPVOID) &wpEngineOutput.x, TRUE },\r
1423   { "engineOutputY", ArgY,   (LPVOID) &wpEngineOutput.y, TRUE },\r
1424   { "engineOutputW", ArgInt, (LPVOID) &wpEngineOutput.width, TRUE },\r
1425   { "engineOutputH", ArgInt, (LPVOID) &wpEngineOutput.height, TRUE },\r
1426 \r
1427   { NULL, ArgNone, NULL, FALSE }\r
1428 };\r
1429 \r
1430 \r
1431 /* Kludge for indirection files on command line */\r
1432 char* lastIndirectionFilename;\r
1433 ArgDescriptor argDescriptorIndirection =\r
1434 { "", ArgSettingsFilename, (LPVOID) NULL, FALSE };\r
1435 \r
1436 \r
1437 VOID\r
1438 ExitArgError(char *msg, char *badArg)\r
1439 {\r
1440   char buf[MSG_SIZ];\r
1441 \r
1442   sprintf(buf, "%s %s", msg, badArg);\r
1443   DisplayFatalError(buf, 0, 2);\r
1444   exit(2);\r
1445 }\r
1446 \r
1447 /* Command line font name parser.  NULL name means do nothing.\r
1448    Syntax like "Courier New:10.0 bi" or "Arial:10" or "Arial:10b"\r
1449    For backward compatibility, syntax without the colon is also\r
1450    accepted, but font names with digits in them won't work in that case.\r
1451 */\r
1452 VOID\r
1453 ParseFontName(char *name, MyFontParams *mfp)\r
1454 {\r
1455   char *p, *q;\r
1456   if (name == NULL) return;\r
1457   p = name;\r
1458   q = strchr(p, ':');\r
1459   if (q) {\r
1460     if (q - p >= sizeof(mfp->faceName))\r
1461       ExitArgError("Font name too long:", name);\r
1462     memcpy(mfp->faceName, p, q - p);\r
1463     mfp->faceName[q - p] = NULLCHAR;\r
1464     p = q + 1;\r
1465   } else {\r
1466     q = mfp->faceName;\r
1467     while (*p && !isdigit(*p)) {\r
1468       *q++ = *p++;\r
1469       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
1470         ExitArgError("Font name too long:", name);\r
1471     }\r
1472     while (q > mfp->faceName && q[-1] == ' ') q--;\r
1473     *q = NULLCHAR;\r
1474   }\r
1475   if (!*p) ExitArgError("Font point size missing:", name);\r
1476   mfp->pointSize = (float) atof(p);\r
1477   mfp->bold = (strchr(p, 'b') != NULL);\r
1478   mfp->italic = (strchr(p, 'i') != NULL);\r
1479   mfp->underline = (strchr(p, 'u') != NULL);\r
1480   mfp->strikeout = (strchr(p, 's') != NULL);\r
1481 }\r
1482 \r
1483 /* Color name parser.\r
1484    X version accepts X color names, but this one\r
1485    handles only the #rrggbb form (hex) or rrr,ggg,bbb (decimal) */\r
1486 COLORREF\r
1487 ParseColorName(char *name)\r
1488 {\r
1489   int red, green, blue, count;\r
1490   char buf[MSG_SIZ];\r
1491 \r
1492   count = sscanf(name, "#%2x%2x%2x", &red, &green, &blue);\r
1493   if (count != 3) {\r
1494     count = sscanf(name, "%3d%*[^0-9]%3d%*[^0-9]%3d", \r
1495       &red, &green, &blue);\r
1496   }\r
1497   if (count != 3) {\r
1498     sprintf(buf, "Can't parse color name %s", name);\r
1499     DisplayError(buf, 0);\r
1500     return RGB(0, 0, 0);\r
1501   }\r
1502   return PALETTERGB(red, green, blue);\r
1503 }\r
1504 \r
1505 \r
1506 void ParseAttribs(COLORREF *color, int *effects, char* argValue)\r
1507 {\r
1508   char *e = argValue;\r
1509   int eff = 0;\r
1510 \r
1511   while (*e) {\r
1512     if (*e == 'b')      eff |= CFE_BOLD;\r
1513     else if (*e == 'i') eff |= CFE_ITALIC;\r
1514     else if (*e == 'u') eff |= CFE_UNDERLINE;\r
1515     else if (*e == 's') eff |= CFE_STRIKEOUT;\r
1516     else if (*e == '#' || isdigit(*e)) break;\r
1517     e++;\r
1518   }\r
1519   *effects = eff;\r
1520   *color   = ParseColorName(e);\r
1521 }\r
1522 \r
1523 \r
1524 BoardSize\r
1525 ParseBoardSize(char *name)\r
1526 {\r
1527   BoardSize bs = SizeTiny;\r
1528   while (sizeInfo[bs].name != NULL) {\r
1529     if (StrCaseCmp(name, sizeInfo[bs].name) == 0) return bs;\r
1530     bs++;\r
1531   }\r
1532   ExitArgError("Unrecognized board size value", name);\r
1533   return bs; /* not reached */\r
1534 }\r
1535 \r
1536 \r
1537 char\r
1538 StringGet(void *getClosure)\r
1539 {\r
1540   char **p = (char **) getClosure;\r
1541   return *((*p)++);\r
1542 }\r
1543 \r
1544 char\r
1545 FileGet(void *getClosure)\r
1546 {\r
1547   int c;\r
1548   FILE* f = (FILE*) getClosure;\r
1549 \r
1550   c = getc(f);\r
1551   if (c == '\r') c = getc(f); // work around DOS format files by bypassing the '\r' completely\r
1552   if (c == EOF)\r
1553     return NULLCHAR;\r
1554   else\r
1555     return (char) c;\r
1556 }\r
1557 \r
1558 /* Parse settings file named "name". If file found, return the\r
1559    full name in fullname and return TRUE; else return FALSE */\r
1560 BOOLEAN\r
1561 ParseSettingsFile(char *name, char fullname[MSG_SIZ])\r
1562 {\r
1563   char *dummy;\r
1564   FILE *f;\r
1565   int ok; char buf[MSG_SIZ];\r
1566 \r
1567   ok = SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
1568   if(!ok && strchr(name, '.') == NULL) { // [HGM] append default file-name extension '.ini' when needed\r
1569     sprintf(buf, "%s.ini", name);\r
1570     ok = SearchPath(installDir, buf, NULL, MSG_SIZ, fullname, &dummy);\r
1571   }\r
1572   if (ok) {\r
1573     f = fopen(fullname, "r");\r
1574     if (f != NULL) {\r
1575       ParseArgs(FileGet, f);\r
1576       fclose(f);\r
1577       return TRUE;\r
1578     }\r
1579   }\r
1580   return FALSE;\r
1581 }\r
1582 \r
1583 VOID\r
1584 ParseArgs(GetFunc get, void *cl)\r
1585 {\r
1586   char argName[ARG_MAX];\r
1587   char argValue[ARG_MAX];\r
1588   ArgDescriptor *ad;\r
1589   char start;\r
1590   char *q;\r
1591   int i, octval;\r
1592   char ch;\r
1593   int posarg = 0;\r
1594 \r
1595   ch = get(cl);\r
1596   for (;;) {\r
1597     while (ch == ' ' || ch == '\n' || ch == '\t') ch = get(cl);\r
1598     if (ch == NULLCHAR) break;\r
1599     if (ch == ';') {\r
1600       /* Comment to end of line */\r
1601       ch = get(cl);\r
1602       while (ch != '\n' && ch != NULLCHAR) ch = get(cl);\r
1603       continue;\r
1604     } else if (ch == '/' || ch == '-') {\r
1605       /* Switch */\r
1606       q = argName;\r
1607       while (ch != ' ' && ch != '=' && ch != ':' && ch != NULLCHAR &&\r
1608              ch != '\n' && ch != '\t') {\r
1609         *q++ = ch;\r
1610         ch = get(cl);\r
1611       }\r
1612       *q = NULLCHAR;\r
1613 \r
1614       for (ad = argDescriptors; ad->argName != NULL; ad++)\r
1615         if (strcmp(ad->argName, argName + 1) == 0) break;\r
1616 \r
1617       if (ad->argName == NULL)\r
1618         ExitArgError("Unrecognized argument", argName);\r
1619 \r
1620     } else if (ch == '@') {\r
1621       /* Indirection file */\r
1622       ad = &argDescriptorIndirection;\r
1623       ch = get(cl);\r
1624     } else {\r
1625       /* Positional argument */\r
1626       ad = &argDescriptors[posarg++];\r
1627       strcpy(argName, ad->argName);\r
1628     }\r
1629 \r
1630     if (ad->argType == ArgTrue) {\r
1631       *(Boolean *) ad->argLoc = TRUE;\r
1632       continue;\r
1633     }\r
1634     if (ad->argType == ArgFalse) {\r
1635       *(Boolean *) ad->argLoc = FALSE;\r
1636       continue;\r
1637     }\r
1638 \r
1639     while (ch == ' ' || ch == '=' || ch == ':' || ch == '\t') ch = get(cl);\r
1640     if (ch == NULLCHAR || ch == '\n') {\r
1641       ExitArgError("No value provided for argument", argName);\r
1642     }\r
1643     q = argValue;\r
1644     if (ch == '{') {\r
1645       // Quoting with { }.  No characters have to (or can) be escaped.\r
1646       // Thus the string cannot contain a '}' character.\r
1647       start = ch;\r
1648       ch = get(cl);\r
1649       while (start) {\r
1650         switch (ch) {\r
1651         case NULLCHAR:\r
1652           start = NULLCHAR;\r
1653           break;\r
1654           \r
1655         case '}':\r
1656           ch = get(cl);\r
1657           start = NULLCHAR;\r
1658           break;\r
1659 \r
1660         default:\r
1661           *q++ = ch;\r
1662           ch = get(cl);\r
1663           break;\r
1664         }\r
1665       }   \r
1666     } else if (ch == '\'' || ch == '"') {\r
1667       // Quoting with ' ' or " ", with \ as escape character.\r
1668       // Inconvenient for long strings that may contain Windows filenames.\r
1669       start = ch;\r
1670       ch = get(cl);\r
1671       while (start) {\r
1672         switch (ch) {\r
1673         case NULLCHAR:\r
1674           start = NULLCHAR;\r
1675           break;\r
1676 \r
1677         default:\r
1678         not_special:\r
1679           *q++ = ch;\r
1680           ch = get(cl);\r
1681           break;\r
1682 \r
1683         case '\'':\r
1684         case '\"':\r
1685           if (ch == start) {\r
1686             ch = get(cl);\r
1687             start = NULLCHAR;\r
1688             break;\r
1689           } else {\r
1690             goto not_special;\r
1691           }\r
1692 \r
1693         case '\\':\r
1694           if (ad->argType == ArgFilename\r
1695               || ad->argType == ArgSettingsFilename) {\r
1696               goto not_special;\r
1697           }\r
1698           ch = get(cl);\r
1699           switch (ch) {\r
1700           case NULLCHAR:\r
1701             ExitArgError("Incomplete \\ escape in value for", argName);\r
1702             break;\r
1703           case 'n':\r
1704             *q++ = '\n';\r
1705             ch = get(cl);\r
1706             break;\r
1707           case 'r':\r
1708             *q++ = '\r';\r
1709             ch = get(cl);\r
1710             break;\r
1711           case 't':\r
1712             *q++ = '\t';\r
1713             ch = get(cl);\r
1714             break;\r
1715           case 'b':\r
1716             *q++ = '\b';\r
1717             ch = get(cl);\r
1718             break;\r
1719           case 'f':\r
1720             *q++ = '\f';\r
1721             ch = get(cl);\r
1722             break;\r
1723           default:\r
1724             octval = 0;\r
1725             for (i = 0; i < 3; i++) {\r
1726               if (ch >= '0' && ch <= '7') {\r
1727                 octval = octval*8 + (ch - '0');\r
1728                 ch = get(cl);\r
1729               } else {\r
1730                 break;\r
1731               }\r
1732             }\r
1733             if (i > 0) {\r
1734               *q++ = (char) octval;\r
1735             } else {\r
1736               *q++ = ch;\r
1737               ch = get(cl);\r
1738             }\r
1739             break;\r
1740           }\r
1741           break;\r
1742         }\r
1743       }\r
1744     } else {\r
1745       while (ch != ' ' && ch != NULLCHAR && ch != '\t' && ch != '\n') {\r
1746         *q++ = ch;\r
1747         ch = get(cl);\r
1748       }\r
1749     }\r
1750     *q = NULLCHAR;\r
1751 \r
1752     switch (ad->argType) {\r
1753     case ArgInt:\r
1754       *(int *) ad->argLoc = atoi(argValue);\r
1755       break;\r
1756 \r
1757     case ArgX:\r
1758       *(int *) ad->argLoc = atoi(argValue) + boardX; // [HGM] placement: translate stored relative to absolute \r
1759       break;\r
1760 \r
1761     case ArgY:\r
1762       *(int *) ad->argLoc = atoi(argValue) + boardY; // (this is really kludgey, it should be done where used...)\r
1763       break;\r
1764 \r
1765     case ArgZ:\r
1766       *(int *) ad->argLoc = atoi(argValue);\r
1767       EnsureOnScreen(&boardX, &boardY, minX, minY); \r
1768       break;\r
1769 \r
1770     case ArgFloat:\r
1771       *(float *) ad->argLoc = (float) atof(argValue);\r
1772       break;\r
1773 \r
1774     case ArgString:\r
1775     case ArgFilename:\r
1776       *(char **) ad->argLoc = strdup(argValue);\r
1777       break;\r
1778 \r
1779     case ArgSettingsFilename:\r
1780       {\r
1781         char fullname[MSG_SIZ];\r
1782         if (ParseSettingsFile(argValue, fullname)) {\r
1783           if (ad->argLoc != NULL) {\r
1784             *(char **) ad->argLoc = strdup(fullname);\r
1785           }\r
1786         } else {\r
1787           if (ad->argLoc != NULL) {\r
1788           } else {\r
1789             ExitArgError("Failed to open indirection file", argValue);\r
1790           }\r
1791         }\r
1792       }\r
1793       break;\r
1794 \r
1795     case ArgBoolean:\r
1796       switch (argValue[0]) {\r
1797       case 't':\r
1798       case 'T':\r
1799         *(Boolean *) ad->argLoc = TRUE;\r
1800         break;\r
1801       case 'f':\r
1802       case 'F':\r
1803         *(Boolean *) ad->argLoc = FALSE;\r
1804         break;\r
1805       default:\r
1806         ExitArgError("Unrecognized boolean argument value", argValue);\r
1807         break;\r
1808       }\r
1809       break;\r
1810 \r
1811     case ArgColor:\r
1812       *(COLORREF *)ad->argLoc = ParseColorName(argValue);\r
1813       break;\r
1814 \r
1815     case ArgAttribs: {\r
1816       ColorClass cc = (ColorClass)ad->argLoc;\r
1817       ParseAttribs(&textAttribs[cc].color, &textAttribs[cc].effects, argValue);\r
1818       }\r
1819       break;\r
1820       \r
1821     case ArgBoardSize:\r
1822       *(BoardSize *)ad->argLoc = ParseBoardSize(argValue);\r
1823       break;\r
1824 \r
1825     case ArgFont:\r
1826       ParseFontName(argValue, &font[boardSize][(int)ad->argLoc]->mfp);\r
1827       break;\r
1828 \r
1829     case ArgCommSettings:\r
1830       ParseCommSettings(argValue, &dcb);\r
1831       break;\r
1832 \r
1833     case ArgNone:\r
1834       ExitArgError("Unrecognized argument", argValue);\r
1835       break;\r
1836     case ArgTrue:\r
1837     case ArgFalse: ;\r
1838     }\r
1839   }\r
1840 }\r
1841 \r
1842 VOID\r
1843 LFfromMFP(LOGFONT* lf, MyFontParams *mfp)\r
1844 {\r
1845   HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);\r
1846   lf->lfHeight = -(int)(mfp->pointSize * GetDeviceCaps(hdc, LOGPIXELSY) / 72.0 + 0.5);\r
1847   DeleteDC(hdc);\r
1848   lf->lfWidth = 0;\r
1849   lf->lfEscapement = 0;\r
1850   lf->lfOrientation = 0;\r
1851   lf->lfWeight = mfp->bold ? FW_BOLD : FW_NORMAL;\r
1852   lf->lfItalic = mfp->italic;\r
1853   lf->lfUnderline = mfp->underline;\r
1854   lf->lfStrikeOut = mfp->strikeout;\r
1855   lf->lfCharSet = DEFAULT_CHARSET;\r
1856   lf->lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1857   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1858   lf->lfQuality = DEFAULT_QUALITY;\r
1859   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
1860   strcpy(lf->lfFaceName, mfp->faceName);\r
1861 }\r
1862 \r
1863 VOID\r
1864 CreateFontInMF(MyFont *mf)\r
1865 {\r
1866   LFfromMFP(&mf->lf, &mf->mfp);\r
1867   if (mf->hf) DeleteObject(mf->hf);\r
1868   mf->hf = CreateFontIndirect(&mf->lf);\r
1869 }\r
1870 \r
1871 VOID\r
1872 SetDefaultTextAttribs()\r
1873 {\r
1874   ColorClass cc;\r
1875   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1876     ParseAttribs(&textAttribs[cc].color, \r
1877                  &textAttribs[cc].effects, \r
1878                  defaultTextAttribs[cc]);\r
1879   }\r
1880 }\r
1881 \r
1882 VOID\r
1883 SetDefaultSounds()\r
1884 {\r
1885   ColorClass cc;\r
1886   SoundClass sc;\r
1887   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1888     textAttribs[cc].sound.name = strdup("");\r
1889     textAttribs[cc].sound.data = NULL;\r
1890   }\r
1891   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1892     sounds[sc].name = strdup("");\r
1893     sounds[sc].data = NULL;\r
1894   }\r
1895   sounds[(int)SoundBell].name = strdup(SOUND_BELL);\r
1896 }\r
1897 \r
1898 VOID\r
1899 LoadAllSounds()\r
1900 {\r
1901   ColorClass cc;\r
1902   SoundClass sc;\r
1903   for (cc = (ColorClass)0; cc < NColorClasses; cc++) {\r
1904     MyLoadSound(&textAttribs[cc].sound);\r
1905   }\r
1906   for (sc = (SoundClass)0; sc < NSoundClasses; sc++) {\r
1907     MyLoadSound(&sounds[sc]);\r
1908   }\r
1909 }\r
1910 \r
1911 VOID\r
1912 InitAppData(LPSTR lpCmdLine)\r
1913 {\r
1914   int i, j;\r
1915   char buf[ARG_MAX], currDir[MSG_SIZ];\r
1916   char *dummy, *p;\r
1917 \r
1918   programName = szAppName;\r
1919 \r
1920   /* Initialize to defaults */\r
1921   lightSquareColor = ParseColorName(LIGHT_SQUARE_COLOR);\r
1922   darkSquareColor = ParseColorName(DARK_SQUARE_COLOR);\r
1923   whitePieceColor = ParseColorName(WHITE_PIECE_COLOR);\r
1924   blackPieceColor = ParseColorName(BLACK_PIECE_COLOR);\r
1925   highlightSquareColor = ParseColorName(HIGHLIGHT_SQUARE_COLOR);\r
1926   premoveHighlightColor = ParseColorName(PREMOVE_HIGHLIGHT_COLOR);\r
1927   consoleBackgroundColor = ParseColorName(COLOR_BKGD);\r
1928   SetDefaultTextAttribs();\r
1929   SetDefaultSounds();\r
1930   appData.movesPerSession = MOVES_PER_SESSION;\r
1931   appData.initString = INIT_STRING;\r
1932   appData.secondInitString = INIT_STRING;\r
1933   appData.firstComputerString = COMPUTER_STRING;\r
1934   appData.secondComputerString = COMPUTER_STRING;\r
1935   appData.firstChessProgram = FIRST_CHESS_PROGRAM;\r
1936   appData.secondChessProgram = SECOND_CHESS_PROGRAM;\r
1937   appData.firstPlaysBlack = FALSE;\r
1938   appData.noChessProgram = FALSE;\r
1939   chessProgram = FALSE;\r
1940   appData.firstHost = FIRST_HOST;\r
1941   appData.secondHost = SECOND_HOST;\r
1942   appData.firstDirectory = FIRST_DIRECTORY;\r
1943   appData.secondDirectory = SECOND_DIRECTORY;\r
1944   appData.bitmapDirectory = "";\r
1945   appData.remoteShell = REMOTE_SHELL;\r
1946   appData.remoteUser = "";\r
1947   appData.timeDelay = TIME_DELAY;\r
1948   appData.timeControl = TIME_CONTROL;\r
1949   appData.timeIncrement = TIME_INCREMENT;\r
1950   appData.icsActive = FALSE;\r
1951   appData.icsHost = "";\r
1952   appData.icsPort = ICS_PORT;\r
1953   appData.icsCommPort = ICS_COMM_PORT;\r
1954   appData.icsLogon = ICS_LOGON;\r
1955   appData.icsHelper = "";\r
1956   appData.useTelnet = FALSE;\r
1957   appData.telnetProgram = TELNET_PROGRAM;\r
1958   appData.gateway = "";\r
1959   appData.loadGameFile = "";\r
1960   appData.loadGameIndex = 0;\r
1961   appData.saveGameFile = "";\r
1962   appData.autoSaveGames = FALSE;\r
1963   appData.loadPositionFile = "";\r
1964   appData.loadPositionIndex = 1;\r
1965   appData.savePositionFile = "";\r
1966   appData.matchMode = FALSE;\r
1967   appData.matchGames = 0;\r
1968   appData.monoMode = FALSE;\r
1969   appData.debugMode = FALSE;\r
1970   appData.clockMode = TRUE;\r
1971   boardSize = (BoardSize) -1; /* determine by screen size */\r
1972   appData.Iconic = FALSE; /*unused*/\r
1973   appData.searchTime = "";\r
1974   appData.searchDepth = 0;\r
1975   appData.showCoords = FALSE;\r
1976   appData.ringBellAfterMoves = TRUE; /*obsolete in WinBoard*/\r
1977   appData.autoCallFlag = FALSE;\r
1978   appData.flipView = FALSE;\r
1979   appData.autoFlipView = TRUE;\r
1980   appData.cmailGameName = "";\r
1981   appData.alwaysPromoteToQueen = FALSE;\r
1982   appData.oldSaveStyle = FALSE;\r
1983   appData.quietPlay = FALSE;\r
1984   appData.showThinking = FALSE;\r
1985   appData.ponderNextMove = TRUE;\r
1986   appData.periodicUpdates = TRUE;\r
1987   appData.popupExitMessage = TRUE;\r
1988   appData.popupMoveErrors = FALSE;\r
1989   appData.autoObserve = FALSE;\r
1990   appData.autoComment = FALSE;\r
1991   appData.animate = TRUE;\r
1992   appData.animSpeed = 10;\r
1993   appData.animateDragging = TRUE;\r
1994   appData.highlightLastMove = TRUE;\r
1995   appData.getMoveList = TRUE;\r
1996   appData.testLegality = TRUE;\r
1997   appData.premove = TRUE;\r
1998   appData.premoveWhite = FALSE;\r
1999   appData.premoveWhiteText = "";\r
2000   appData.premoveBlack = FALSE;\r
2001   appData.premoveBlackText = "";\r
2002   appData.icsAlarm = TRUE;\r
2003   appData.icsAlarmTime = 5000;\r
2004   appData.autoRaiseBoard = TRUE;\r
2005   appData.localLineEditing = TRUE;\r
2006   appData.colorize = TRUE;\r
2007   appData.reuseFirst = TRUE;\r
2008   appData.reuseSecond = TRUE;\r
2009   appData.blindfold = FALSE;\r
2010   appData.icsEngineAnalyze = FALSE;\r
2011   memset(&dcb, 0, sizeof(DCB)); // required by VS 2002 +\r
2012   dcb.DCBlength = sizeof(DCB);\r
2013   dcb.BaudRate = 9600;\r
2014   dcb.fBinary = TRUE;\r
2015   dcb.fParity = FALSE;\r
2016   dcb.fOutxCtsFlow = FALSE;\r
2017   dcb.fOutxDsrFlow = FALSE;\r
2018   dcb.fDtrControl = DTR_CONTROL_ENABLE;\r
2019   dcb.fDsrSensitivity = FALSE;\r
2020   dcb.fTXContinueOnXoff = TRUE;\r
2021   dcb.fOutX = FALSE;\r
2022   dcb.fInX = FALSE;\r
2023   dcb.fNull = FALSE;\r
2024   dcb.fRtsControl = RTS_CONTROL_ENABLE;\r
2025   dcb.fAbortOnError = FALSE;\r
2026   dcb.ByteSize = 7;\r
2027   dcb.Parity = SPACEPARITY;\r
2028   dcb.StopBits = ONESTOPBIT;\r
2029   settingsFileName = SETTINGS_FILE;\r
2030   saveSettingsOnExit = TRUE;\r
2031   boardX = CW_USEDEFAULT;\r
2032   boardY = CW_USEDEFAULT;\r
2033   analysisX = CW_USEDEFAULT; \r
2034   analysisY = CW_USEDEFAULT; \r
2035   analysisW = CW_USEDEFAULT;\r
2036   analysisH = CW_USEDEFAULT;\r
2037   commentX = CW_USEDEFAULT; \r
2038   commentY = CW_USEDEFAULT; \r
2039   commentW = CW_USEDEFAULT;\r
2040   commentH = CW_USEDEFAULT;\r
2041   editTagsX = CW_USEDEFAULT; \r
2042   editTagsY = CW_USEDEFAULT; \r
2043   editTagsW = CW_USEDEFAULT;\r
2044   editTagsH = CW_USEDEFAULT;\r
2045   icsTextMenuString = ICS_TEXT_MENU_DEFAULT;\r
2046   icsNames = ICS_NAMES;\r
2047   firstChessProgramNames = FCP_NAMES;\r
2048   secondChessProgramNames = SCP_NAMES;\r
2049   appData.initialMode = "";\r
2050   appData.variant = "normal";\r
2051   appData.firstProtocolVersion = PROTOVER;\r
2052   appData.secondProtocolVersion = PROTOVER;\r
2053   appData.showButtonBar = TRUE;\r
2054 \r
2055    /* [AS] New properties (see comments in header file) */\r
2056   appData.firstScoreIsAbsolute = FALSE;\r
2057   appData.secondScoreIsAbsolute = FALSE;\r
2058   appData.saveExtendedInfoInPGN = FALSE;\r
2059   appData.hideThinkingFromHuman = FALSE;\r
2060   appData.liteBackTextureFile = "";\r
2061   appData.liteBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2062   appData.darkBackTextureFile = "";\r
2063   appData.darkBackTextureMode = BACK_TEXTURE_MODE_PLAIN;\r
2064   appData.renderPiecesWithFont = "";\r
2065   appData.fontToPieceTable = "";\r
2066   appData.fontBackColorWhite = 0;\r
2067   appData.fontForeColorWhite = 0;\r
2068   appData.fontBackColorBlack = 0;\r
2069   appData.fontForeColorBlack = 0;\r
2070   appData.fontPieceSize = 80;\r
2071   appData.overrideLineGap = 1;\r
2072   appData.adjudicateLossThreshold = 0;\r
2073   appData.delayBeforeQuit = 0;\r
2074   appData.delayAfterQuit = 0;\r
2075   appData.nameOfDebugFile = "winboard.debug";\r
2076   appData.pgnEventHeader = "Computer Chess Game";\r
2077   appData.defaultFrcPosition = -1;\r
2078   appData.gameListTags = GLT_DEFAULT_TAGS;\r
2079   appData.saveOutOfBookInfo = TRUE;\r
2080   appData.showEvalInMoveHistory = TRUE;\r
2081   appData.evalHistColorWhite = ParseColorName( "#FFFFB0" );\r
2082   appData.evalHistColorBlack = ParseColorName( "#AD5D3D" );\r
2083   appData.highlightMoveWithArrow = FALSE;\r
2084   appData.highlightArrowColor = ParseColorName( "#FFFF80" );\r
2085   appData.useStickyWindows = TRUE;\r
2086   appData.adjudicateDrawMoves = 0;\r
2087   appData.autoDisplayComment = TRUE;\r
2088   appData.autoDisplayTags = TRUE;\r
2089   appData.firstIsUCI = FALSE;\r
2090   appData.secondIsUCI = FALSE;\r
2091   appData.firstHasOwnBookUCI = TRUE;\r
2092   appData.secondHasOwnBookUCI = TRUE;\r
2093   appData.polyglotDir = "";\r
2094   appData.usePolyglotBook = FALSE;\r
2095   appData.polyglotBook = "";\r
2096   appData.defaultHashSize = 64;\r
2097   appData.defaultCacheSizeEGTB = 4;\r
2098   appData.defaultPathEGTB = "c:\\egtb";\r
2099   appData.firstOptions = "";\r
2100   appData.secondOptions = "";\r
2101 \r
2102   InitWindowPlacement( &wpGameList );\r
2103   InitWindowPlacement( &wpMoveHistory );\r
2104   InitWindowPlacement( &wpEvalGraph );\r
2105   InitWindowPlacement( &wpEngineOutput );\r
2106   InitWindowPlacement( &wpConsole );\r
2107 \r
2108   /* [HGM] User-selectable board size, adjudication control, miscellaneous */\r
2109   appData.NrFiles      = -1;\r
2110   appData.NrRanks      = -1;\r
2111   appData.holdingsSize = -1;\r
2112   appData.testClaims   = FALSE;\r
2113   appData.checkMates   = FALSE;\r
2114   appData.materialDraws= FALSE;\r
2115   appData.trivialDraws = FALSE;\r
2116   appData.ruleMoves    = 51;\r
2117   appData.drawRepeats  = 6;\r
2118   appData.matchPause   = 10000;\r
2119   appData.alphaRank    = FALSE;\r
2120   appData.allWhite     = FALSE;\r
2121   appData.upsideDown   = FALSE;\r
2122   appData.serverPause  = 15;\r
2123   appData.serverMovesName   = NULL;\r
2124   appData.suppressLoadMoves = FALSE;\r
2125   appData.firstTimeOdds  = 1;\r
2126   appData.secondTimeOdds = 1;\r
2127   appData.firstAccumulateTC  = 1; // combine previous and current sessions\r
2128   appData.secondAccumulateTC = 1;\r
2129   appData.firstNPS  = -1; // [HGM] nps: use wall-clock time\r
2130   appData.secondNPS = -1;\r
2131   appData.engineComments = 1;\r
2132   appData.smpCores = 1; // [HGM] SMP: max nr of cores\r
2133   appData.egtFormats = "";\r
2134 \r
2135 #ifdef ZIPPY\r
2136   appData.zippyTalk = ZIPPY_TALK;\r
2137   appData.zippyPlay = ZIPPY_PLAY;\r
2138   appData.zippyLines = ZIPPY_LINES;\r
2139   appData.zippyPinhead = ZIPPY_PINHEAD;\r
2140   appData.zippyPassword = ZIPPY_PASSWORD;\r
2141   appData.zippyPassword2 = ZIPPY_PASSWORD2;\r
2142   appData.zippyWrongPassword = ZIPPY_WRONG_PASSWORD;\r
2143   appData.zippyAcceptOnly = ZIPPY_ACCEPT_ONLY;\r
2144   appData.zippyUseI = ZIPPY_USE_I;\r
2145   appData.zippyBughouse = ZIPPY_BUGHOUSE;\r
2146   appData.zippyNoplayCrafty = ZIPPY_NOPLAY_CRAFTY;\r
2147   appData.zippyGameEnd = ZIPPY_GAME_END;\r
2148   appData.zippyGameStart = ZIPPY_GAME_START;\r
2149   appData.zippyAdjourn = ZIPPY_ADJOURN;\r
2150   appData.zippyAbort = ZIPPY_ABORT;\r
2151   appData.zippyVariants = ZIPPY_VARIANTS;\r
2152   appData.zippyMaxGames = ZIPPY_MAX_GAMES;\r
2153   appData.zippyReplayTimeout = ZIPPY_REPLAY_TIMEOUT;\r
2154 #endif\r
2155 \r
2156   /* Point font array elements to structures and\r
2157      parse default font names */\r
2158   for (i=0; i<NUM_FONTS; i++) {\r
2159     for (j=0; j<NUM_SIZES; j++) {\r
2160       font[j][i] = &fontRec[j][i];\r
2161       ParseFontName(font[j][i]->def, &font[j][i]->mfp);\r
2162     }\r
2163   }\r
2164   \r
2165   /* Parse default settings file if any */\r
2166   if (ParseSettingsFile(settingsFileName, buf)) {\r
2167     settingsFileName = strdup(buf);\r
2168   }\r
2169 \r
2170   /* Parse command line */\r
2171   ParseArgs(StringGet, &lpCmdLine);\r
2172 \r
2173   /* [HGM] make sure board size is acceptable */\r
2174   if(appData.NrFiles > BOARD_SIZE ||\r
2175      appData.NrRanks > BOARD_SIZE   )\r
2176       DisplayFatalError("Recompile with BOARD_SIZE > 12, to support this size", 0, 2);\r
2177 \r
2178   /* [HGM] After parsing the options from the .ini file, and overruling them\r
2179    * with options from the command line, we now make an even higher priority\r
2180    * overrule by WB options attached to the engine command line. This so that\r
2181    * tournament managers can use WB options (such as /timeOdds) that follow\r
2182    * the engines.\r
2183    */\r
2184   if(appData.firstChessProgram != NULL) {\r
2185       char *p = StrStr(appData.firstChessProgram, "WBopt");\r
2186       static char *f = "first";\r
2187       char buf[MSG_SIZ], *q = buf;\r
2188       if(p != NULL) { // engine command line contains WinBoard options\r
2189           sprintf(buf, p+6, f, f, f, f, f, f, f, f, f, f); // replace %s in them by "first"\r
2190           ParseArgs(StringGet, &q);\r
2191           p[-1] = 0; // cut them offengine command line\r
2192       }\r
2193   }\r
2194   // now do same for second chess program\r
2195   if(appData.secondChessProgram != NULL) {\r
2196       char *p = StrStr(appData.secondChessProgram, "WBopt");\r
2197       static char *s = "second";\r
2198       char buf[MSG_SIZ], *q = buf;\r
2199       if(p != NULL) { // engine command line contains WinBoard options\r
2200           sprintf(buf, p+6, s, s, s, s, s, s, s, s, s, s); // replace %s in them by "first"\r
2201           ParseArgs(StringGet, &q);\r
2202           p[-1] = 0; // cut them offengine command line\r
2203       }\r
2204   }\r
2205 \r
2206 \r
2207   /* Propagate options that affect others */\r
2208   if (appData.matchMode || appData.matchGames) chessProgram = TRUE;\r
2209   if (appData.icsActive || appData.noChessProgram) {\r
2210      chessProgram = FALSE;  /* not local chess program mode */\r
2211   }\r
2212 \r
2213   /* Open startup dialog if needed */\r
2214   if ((!appData.noChessProgram && !chessProgram && !appData.icsActive) ||\r
2215       (appData.icsActive && *appData.icsHost == NULLCHAR) ||\r
2216       (chessProgram && (*appData.firstChessProgram == NULLCHAR ||\r
2217                         *appData.secondChessProgram == NULLCHAR))) {\r
2218     FARPROC lpProc;\r
2219     \r
2220     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
2221     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
2222     FreeProcInstance(lpProc);\r
2223   }\r
2224 \r
2225   /* Make sure save files land in the right (?) directory */\r
2226   if (GetFullPathName(appData.saveGameFile, MSG_SIZ, buf, &dummy)) {\r
2227     appData.saveGameFile = strdup(buf);\r
2228   }\r
2229   if (GetFullPathName(appData.savePositionFile, MSG_SIZ, buf, &dummy)) {\r
2230     appData.savePositionFile = strdup(buf);\r
2231   }\r
2232 \r
2233   /* Finish initialization for fonts and sounds */\r
2234   for (i=0; i<NUM_FONTS; i++) {\r
2235     for (j=0; j<NUM_SIZES; j++) {\r
2236       CreateFontInMF(font[j][i]);\r
2237     }\r
2238   }\r
2239   /* xboard, and older WinBoards, controlled the move sound with the\r
2240      appData.ringBellAfterMoves option.  In the current WinBoard, we\r
2241      always turn the option on (so that the backend will call us),\r
2242      then let the user turn the sound off by setting it to silence if\r
2243      desired.  To accommodate old winboard.ini files saved by old\r
2244      versions of WinBoard, we also turn off the sound if the option\r
2245      was initially set to false. */\r
2246   if (!appData.ringBellAfterMoves) {\r
2247     sounds[(int)SoundMove].name = strdup("");\r
2248     appData.ringBellAfterMoves = TRUE;\r
2249   }\r
2250   GetCurrentDirectory(MSG_SIZ, currDir);\r
2251   SetCurrentDirectory(installDir);\r
2252   LoadAllSounds();\r
2253   SetCurrentDirectory(currDir);\r
2254 \r
2255   p = icsTextMenuString;\r
2256   if (p[0] == '@') {\r
2257     FILE* f = fopen(p + 1, "r");\r
2258     if (f == NULL) {\r
2259       DisplayFatalError(p + 1, errno, 2);\r
2260       return;\r
2261     }\r
2262     i = fread(buf, 1, sizeof(buf)-1, f);\r
2263     fclose(f);\r
2264     buf[i] = NULLCHAR;\r
2265     p = buf;\r
2266   }\r
2267   ParseIcsTextMenu(strdup(p));\r
2268 }\r
2269 \r
2270 \r
2271 VOID\r
2272 InitMenuChecks()\r
2273 {\r
2274   HMENU hmenu = GetMenu(hwndMain);\r
2275 \r
2276   (void) EnableMenuItem(hmenu, IDM_CommPort,\r
2277                         MF_BYCOMMAND|((appData.icsActive &&\r
2278                                        *appData.icsCommPort != NULLCHAR) ?\r
2279                                       MF_ENABLED : MF_GRAYED));\r
2280   (void) CheckMenuItem(hmenu, IDM_SaveSettingsOnExit,\r
2281                        MF_BYCOMMAND|(saveSettingsOnExit ?\r
2282                                      MF_CHECKED : MF_UNCHECKED));\r
2283 }\r
2284 \r
2285 \r
2286 VOID\r
2287 SaveSettings(char* name)\r
2288 {\r
2289   FILE *f;\r
2290   ArgDescriptor *ad;\r
2291   WINDOWPLACEMENT wp;\r
2292   char dir[MSG_SIZ];\r
2293 \r
2294   if (!hwndMain) return;\r
2295 \r
2296   GetCurrentDirectory(MSG_SIZ, dir);\r
2297   SetCurrentDirectory(installDir);\r
2298   f = fopen(name, "w");\r
2299   SetCurrentDirectory(dir);\r
2300   if (f == NULL) {\r
2301     DisplayError(name, errno);\r
2302     return;\r
2303   }\r
2304   fprintf(f, ";\n");\r
2305   fprintf(f, "; %s Save Settings file\n", PACKAGE_STRING);\r
2306   fprintf(f, ";\n");\r
2307   fprintf(f, "; You can edit the values of options that are already set in this file,\n");\r
2308   fprintf(f, "; but if you add other options, the next Save Settings will not save them.\n");\r
2309   fprintf(f, "; Use a shortcut, an @indirection file, or a .bat file instead.\n");\r
2310   fprintf(f, ";\n");\r
2311 \r
2312   wp.length = sizeof(WINDOWPLACEMENT);\r
2313   GetWindowPlacement(hwndMain, &wp);\r
2314   boardX = wp.rcNormalPosition.left;\r
2315   boardY = wp.rcNormalPosition.top;\r
2316 \r
2317   if (hwndConsole) {\r
2318     GetWindowPlacement(hwndConsole, &wp);\r
2319     wpConsole.x = wp.rcNormalPosition.left;\r
2320     wpConsole.y = wp.rcNormalPosition.top;\r
2321     wpConsole.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2322     wpConsole.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2323   }\r
2324 \r
2325   if (analysisDialog) {\r
2326     GetWindowPlacement(analysisDialog, &wp);\r
2327     analysisX = wp.rcNormalPosition.left;\r
2328     analysisY = wp.rcNormalPosition.top;\r
2329     analysisW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2330     analysisH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2331   }\r
2332 \r
2333   if (commentDialog) {\r
2334     GetWindowPlacement(commentDialog, &wp);\r
2335     commentX = wp.rcNormalPosition.left;\r
2336     commentY = wp.rcNormalPosition.top;\r
2337     commentW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2338     commentH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2339   }\r
2340 \r
2341   if (editTagsDialog) {\r
2342     GetWindowPlacement(editTagsDialog, &wp);\r
2343     editTagsX = wp.rcNormalPosition.left;\r
2344     editTagsY = wp.rcNormalPosition.top;\r
2345     editTagsW = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2346     editTagsH = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2347   }\r
2348 \r
2349   if (gameListDialog) {\r
2350     GetWindowPlacement(gameListDialog, &wp);\r
2351     wpGameList.x = wp.rcNormalPosition.left;\r
2352     wpGameList.y = wp.rcNormalPosition.top;\r
2353     wpGameList.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2354     wpGameList.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2355   }\r
2356 \r
2357   /* [AS] Move history */\r
2358   wpMoveHistory.visible = MoveHistoryIsUp();\r
2359   \r
2360   if( moveHistoryDialog ) {\r
2361     GetWindowPlacement(moveHistoryDialog, &wp);\r
2362     wpMoveHistory.x = wp.rcNormalPosition.left;\r
2363     wpMoveHistory.y = wp.rcNormalPosition.top;\r
2364     wpMoveHistory.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2365     wpMoveHistory.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2366   }\r
2367 \r
2368   /* [AS] Eval graph */\r
2369   wpEvalGraph.visible = EvalGraphIsUp();\r
2370 \r
2371   if( evalGraphDialog ) {\r
2372     GetWindowPlacement(evalGraphDialog, &wp);\r
2373     wpEvalGraph.x = wp.rcNormalPosition.left;\r
2374     wpEvalGraph.y = wp.rcNormalPosition.top;\r
2375     wpEvalGraph.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2376     wpEvalGraph.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2377   }\r
2378 \r
2379   /* [AS] Engine output */\r
2380   wpEngineOutput.visible = EngineOutputIsUp();\r
2381 \r
2382   if( engineOutputDialog ) {\r
2383     GetWindowPlacement(engineOutputDialog, &wp);\r
2384     wpEngineOutput.x = wp.rcNormalPosition.left;\r
2385     wpEngineOutput.y = wp.rcNormalPosition.top;\r
2386     wpEngineOutput.width = wp.rcNormalPosition.right - wp.rcNormalPosition.left;\r
2387     wpEngineOutput.height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;\r
2388   }\r
2389 \r
2390   for (ad = argDescriptors; ad->argName != NULL; ad++) {\r
2391     if (!ad->save) continue;\r
2392     switch (ad->argType) {\r
2393     case ArgString:\r
2394       {\r
2395         char *p = *(char **)ad->argLoc;\r
2396         if ((strchr(p, '\\') || strchr(p, '\n')) && !strchr(p, '}')) {\r
2397           /* Quote multiline values or \-containing values\r
2398              with { } if possible */\r
2399           fprintf(f, "/%s={%s}\n", ad->argName, p);\r
2400         } else {\r
2401           /* Else quote with " " */\r
2402           fprintf(f, "/%s=\"", ad->argName);\r
2403           while (*p) {\r
2404             if (*p == '\n') fprintf(f, "\n");\r
2405             else if (*p == '\r') fprintf(f, "\\r");\r
2406             else if (*p == '\t') fprintf(f, "\\t");\r
2407             else if (*p == '\b') fprintf(f, "\\b");\r
2408             else if (*p == '\f') fprintf(f, "\\f");\r
2409             else if (*p < ' ') fprintf(f, "\\%03o", *p);\r
2410             else if (*p == '\"') fprintf(f, "\\\"");\r
2411             else if (*p == '\\') fprintf(f, "\\\\");\r
2412             else putc(*p, f);\r
2413             p++;\r
2414           }\r
2415           fprintf(f, "\"\n");\r
2416         }\r
2417       }\r
2418       break;\r
2419     case ArgInt:\r
2420     case ArgZ:\r
2421       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc);\r
2422       break;\r
2423     case ArgX:\r
2424       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardX); // [HGM] placement: stor relative value\r
2425       break;\r
2426     case ArgY:\r
2427       fprintf(f, "/%s=%d\n", ad->argName, *(int *)ad->argLoc - boardY);\r
2428       break;\r
2429     case ArgFloat:\r
2430       fprintf(f, "/%s=%g\n", ad->argName, *(float *)ad->argLoc);\r
2431       break;\r
2432     case ArgBoolean:\r
2433       fprintf(f, "/%s=%s\n", ad->argName, \r
2434         (*(Boolean *)ad->argLoc) ? "true" : "false");\r
2435       break;\r
2436     case ArgTrue:\r
2437       if (*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2438       break;\r
2439     case ArgFalse:\r
2440       if (!*(Boolean *)ad->argLoc) fprintf(f, "/%s\n", ad->argName);\r
2441       break;\r
2442     case ArgColor:\r
2443       {\r
2444         COLORREF color = *(COLORREF *)ad->argLoc;\r
2445         fprintf(f, "/%s=#%02lx%02lx%02lx\n", ad->argName, \r
2446           color&0xff, (color>>8)&0xff, (color>>16)&0xff);\r
2447       }\r
2448       break;\r
2449     case ArgAttribs:\r
2450       {\r
2451         MyTextAttribs* ta = &textAttribs[(ColorClass)ad->argLoc];\r
2452         fprintf(f, "/%s=\"%s%s%s%s%s#%02lx%02lx%02lx\"\n", ad->argName,\r
2453           (ta->effects & CFE_BOLD) ? "b" : "",\r
2454           (ta->effects & CFE_ITALIC) ? "i" : "",\r
2455           (ta->effects & CFE_UNDERLINE) ? "u" : "",\r
2456           (ta->effects & CFE_STRIKEOUT) ? "s" : "",\r
2457           (ta->effects) ? " " : "",\r
2458           ta->color&0xff, (ta->color >> 8)&0xff, (ta->color >> 16)&0xff);\r
2459       }\r
2460       break;\r
2461     case ArgFilename:\r
2462       if (strchr(*(char **)ad->argLoc, '\"')) {\r
2463         fprintf(f, "/%s='%s'\n", ad->argName, *(char **)ad->argLoc);\r
2464       } else {\r
2465         fprintf(f, "/%s=\"%s\"\n", ad->argName, *(char **)ad->argLoc);\r
2466       }\r
2467       break;\r
2468     case ArgBoardSize:\r
2469       fprintf(f, "/%s=%s\n", ad->argName,\r
2470               sizeInfo[*(BoardSize *)ad->argLoc].name);\r
2471       break;\r
2472     case ArgFont:\r
2473       {\r
2474         int bs;\r
2475         for (bs=0; bs<NUM_SIZES; bs++) {\r
2476           MyFontParams *mfp = &font[bs][(int) ad->argLoc]->mfp;\r
2477           fprintf(f, "/size=%s ", sizeInfo[bs].name);\r
2478           fprintf(f, "/%s=\"%s:%g%s%s%s%s%s\"\n",\r
2479             ad->argName, mfp->faceName, mfp->pointSize,\r
2480             mfp->bold || mfp->italic || mfp->underline || mfp->strikeout ? " " : "",\r
2481             mfp->bold ? "b" : "",\r
2482             mfp->italic ? "i" : "",\r
2483             mfp->underline ? "u" : "",\r
2484             mfp->strikeout ? "s" : "");\r
2485         }\r
2486       }\r
2487       break;\r
2488     case ArgCommSettings:\r
2489       PrintCommSettings(f, ad->argName, (DCB *)ad->argLoc);\r
2490     case ArgNone:\r
2491     case ArgSettingsFilename: ;\r
2492     }\r
2493   }\r
2494   fclose(f);\r
2495 }\r
2496 \r
2497 \r
2498 \r
2499 /*---------------------------------------------------------------------------*\\r
2500  *\r
2501  * GDI board drawing routines\r
2502  *\r
2503 \*---------------------------------------------------------------------------*/\r
2504 \r
2505 /* [AS] Draw square using background texture */\r
2506 static void DrawTile( int dx, int dy, int dw, int dh, HDC dst, HDC src, int mode, int sx, int sy )\r
2507 {\r
2508     XFORM   x;\r
2509 \r
2510     if( mode == 0 ) {\r
2511         return; /* Should never happen! */\r
2512     }\r
2513 \r
2514     SetGraphicsMode( dst, GM_ADVANCED );\r
2515 \r
2516     switch( mode ) {\r
2517     case 1:\r
2518         /* Identity */\r
2519         break;\r
2520     case 2:\r
2521         /* X reflection */\r
2522         x.eM11 = -1.0;\r
2523         x.eM12 = 0;\r
2524         x.eM21 = 0;\r
2525         x.eM22 = 1.0;\r
2526         x.eDx = (FLOAT) dw + dx - 1;\r
2527         x.eDy = 0;\r
2528         dx = 0;\r
2529         SetWorldTransform( dst, &x );\r
2530         break;\r
2531     case 3:\r
2532         /* Y reflection */\r
2533         x.eM11 = 1.0;\r
2534         x.eM12 = 0;\r
2535         x.eM21 = 0;\r
2536         x.eM22 = -1.0;\r
2537         x.eDx = 0;\r
2538         x.eDy = (FLOAT) dh + dy - 1;\r
2539         dy = 0;\r
2540         SetWorldTransform( dst, &x );\r
2541         break;\r
2542     case 4:\r
2543         /* X/Y flip */\r
2544         x.eM11 = 0;\r
2545         x.eM12 = 1.0;\r
2546         x.eM21 = 1.0;\r
2547         x.eM22 = 0;\r
2548         x.eDx = (FLOAT) dx;\r
2549         x.eDy = (FLOAT) dy;\r
2550         dx = 0;\r
2551         dy = 0;\r
2552         SetWorldTransform( dst, &x );\r
2553         break;\r
2554     }\r
2555 \r
2556     BitBlt( dst, dx, dy, dw, dh, src, sx, sy, SRCCOPY );\r
2557 \r
2558     x.eM11 = 1.0;\r
2559     x.eM12 = 0;\r
2560     x.eM21 = 0;\r
2561     x.eM22 = 1.0;\r
2562     x.eDx = 0;\r
2563     x.eDy = 0;\r
2564     SetWorldTransform( dst, &x );\r
2565 \r
2566     ModifyWorldTransform( dst, 0, MWT_IDENTITY );\r
2567 }\r
2568 \r
2569 /* [AS] [HGM] Make room for more piece types, so all pieces can be different */\r
2570 enum {\r
2571     PM_WP = (int) WhitePawn, \r
2572     PM_WN = (int) WhiteKnight, \r
2573     PM_WB = (int) WhiteBishop, \r
2574     PM_WR = (int) WhiteRook, \r
2575     PM_WQ = (int) WhiteQueen, \r
2576     PM_WF = (int) WhiteFerz, \r
2577     PM_WW = (int) WhiteWazir, \r
2578     PM_WE = (int) WhiteAlfil, \r
2579     PM_WM = (int) WhiteMan, \r
2580     PM_WO = (int) WhiteCannon, \r
2581     PM_WU = (int) WhiteUnicorn, \r
2582     PM_WH = (int) WhiteNightrider, \r
2583     PM_WA = (int) WhiteAngel, \r
2584     PM_WC = (int) WhiteMarshall, \r
2585     PM_WAB = (int) WhiteCardinal, \r
2586     PM_WD = (int) WhiteDragon, \r
2587     PM_WL = (int) WhiteLance, \r
2588     PM_WS = (int) WhiteCobra, \r
2589     PM_WV = (int) WhiteFalcon, \r
2590     PM_WSG = (int) WhiteSilver, \r
2591     PM_WG = (int) WhiteGrasshopper, \r
2592     PM_WK = (int) WhiteKing,\r
2593     PM_BP = (int) BlackPawn, \r
2594     PM_BN = (int) BlackKnight, \r
2595     PM_BB = (int) BlackBishop, \r
2596     PM_BR = (int) BlackRook, \r
2597     PM_BQ = (int) BlackQueen, \r
2598     PM_BF = (int) BlackFerz, \r
2599     PM_BW = (int) BlackWazir, \r
2600     PM_BE = (int) BlackAlfil, \r
2601     PM_BM = (int) BlackMan,\r
2602     PM_BO = (int) BlackCannon, \r
2603     PM_BU = (int) BlackUnicorn, \r
2604     PM_BH = (int) BlackNightrider, \r
2605     PM_BA = (int) BlackAngel, \r
2606     PM_BC = (int) BlackMarshall, \r
2607     PM_BG = (int) BlackGrasshopper, \r
2608     PM_BAB = (int) BlackCardinal,\r
2609     PM_BD = (int) BlackDragon,\r
2610     PM_BL = (int) BlackLance,\r
2611     PM_BS = (int) BlackCobra,\r
2612     PM_BV = (int) BlackFalcon,\r
2613     PM_BSG = (int) BlackSilver,\r
2614     PM_BK = (int) BlackKing\r
2615 };\r
2616 \r
2617 static HFONT hPieceFont = NULL;\r
2618 static HBITMAP hPieceMask[(int) EmptySquare];\r
2619 static HBITMAP hPieceFace[(int) EmptySquare];\r
2620 static int fontBitmapSquareSize = 0;\r
2621 static char pieceToFontChar[(int) EmptySquare] =\r
2622                               { 'p', 'n', 'b', 'r', 'q', \r
2623                       'n', 'b', 'p', 'n', 'b', 'r', 'b', 'r', 'q', 'k',\r
2624                       'k', 'o', 'm', 'v', 't', 'w', \r
2625                       'v', 't', 'o', 'm', 'v', 't', 'v', 't', 'w', 'l',\r
2626                                                               'l' };\r
2627 \r
2628 extern BOOL SetCharTable( char *table, const char * map );\r
2629 /* [HGM] moved to backend.c */\r
2630 \r
2631 static void SetPieceBackground( HDC hdc, COLORREF color, int mode )\r
2632 {\r
2633     HBRUSH hbrush;\r
2634     BYTE r1 = GetRValue( color );\r
2635     BYTE g1 = GetGValue( color );\r
2636     BYTE b1 = GetBValue( color );\r
2637     BYTE r2 = r1 / 2;\r
2638     BYTE g2 = g1 / 2;\r
2639     BYTE b2 = b1 / 2;\r
2640     RECT rc;\r
2641 \r
2642     /* Create a uniform background first */\r
2643     hbrush = CreateSolidBrush( color );\r
2644     SetRect( &rc, 0, 0, squareSize, squareSize );\r
2645     FillRect( hdc, &rc, hbrush );\r
2646     DeleteObject( hbrush );\r
2647     \r
2648     if( mode == 1 ) {\r
2649         /* Vertical gradient, good for pawn, knight and rook, less for queen and king */\r
2650         int steps = squareSize / 2;\r
2651         int i;\r
2652 \r
2653         for( i=0; i<steps; i++ ) {\r
2654             BYTE r = r1 - (r1-r2) * i / steps;\r
2655             BYTE g = g1 - (g1-g2) * i / steps;\r
2656             BYTE b = b1 - (b1-b2) * i / steps;\r
2657 \r
2658             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2659             SetRect( &rc, i + squareSize - steps, 0, i + squareSize - steps + 1, squareSize );\r
2660             FillRect( hdc, &rc, hbrush );\r
2661             DeleteObject(hbrush);\r
2662         }\r
2663     }\r
2664     else if( mode == 2 ) {\r
2665         /* Diagonal gradient, good more or less for every piece */\r
2666         POINT triangle[3];\r
2667         HPEN hpen = SelectObject( hdc, GetStockObject(NULL_PEN) );\r
2668         HBRUSH hbrush_old;\r
2669         int steps = squareSize;\r
2670         int i;\r
2671 \r
2672         triangle[0].x = squareSize - steps;\r
2673         triangle[0].y = squareSize;\r
2674         triangle[1].x = squareSize;\r
2675         triangle[1].y = squareSize;\r
2676         triangle[2].x = squareSize;\r
2677         triangle[2].y = squareSize - steps;\r
2678 \r
2679         for( i=0; i<steps; i++ ) {\r
2680             BYTE r = r1 - (r1-r2) * i / steps;\r
2681             BYTE g = g1 - (g1-g2) * i / steps;\r
2682             BYTE b = b1 - (b1-b2) * i / steps;\r
2683 \r
2684             hbrush = CreateSolidBrush( RGB(r,g,b) );\r
2685             hbrush_old = SelectObject( hdc, hbrush );\r
2686             Polygon( hdc, triangle, 3 );\r
2687             SelectObject( hdc, hbrush_old );\r
2688             DeleteObject(hbrush);\r
2689             triangle[0].x++;\r
2690             triangle[2].y++;\r
2691         }\r
2692 \r
2693         SelectObject( hdc, hpen );\r
2694     }\r
2695 }\r
2696 \r
2697 /*\r
2698     [AS] The method I use to create the bitmaps it a bit tricky, but it\r
2699     seems to work ok. The main problem here is to find the "inside" of a chess\r
2700     piece: follow the steps as explained below.\r
2701 */\r
2702 static void CreatePieceMaskFromFont( HDC hdc_window, HDC hdc, int index )\r
2703 {\r
2704     HBITMAP hbm;\r
2705     HBITMAP hbm_old;\r
2706     COLORREF chroma = RGB(0xFF,0x00,0xFF);\r
2707     RECT rc;\r
2708     SIZE sz;\r
2709     POINT pt;\r
2710     int backColor = whitePieceColor; \r
2711     int foreColor = blackPieceColor;\r
2712     \r
2713     if( index < (int)BlackPawn && appData.fontBackColorWhite != appData.fontForeColorWhite ) {\r
2714         backColor = appData.fontBackColorWhite;\r
2715         foreColor = appData.fontForeColorWhite;\r
2716     }\r
2717     else if( index >= (int)BlackPawn && appData.fontBackColorBlack != appData.fontForeColorBlack ) {\r
2718         backColor = appData.fontBackColorBlack;\r
2719         foreColor = appData.fontForeColorBlack;\r
2720     }\r
2721 \r
2722     /* Mask */\r
2723     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2724 \r
2725     hbm_old = SelectObject( hdc, hbm );\r
2726 \r
2727     rc.left = 0;\r
2728     rc.top = 0;\r
2729     rc.right = squareSize;\r
2730     rc.bottom = squareSize;\r
2731 \r
2732     /* Step 1: background is now black */\r
2733     FillRect( hdc, &rc, GetStockObject(BLACK_BRUSH) );\r
2734 \r
2735     GetTextExtentPoint32( hdc, &pieceToFontChar[index], 1, &sz );\r
2736 \r
2737     pt.x = (squareSize - sz.cx) / 2;\r
2738     pt.y = (squareSize - sz.cy) / 2;\r
2739 \r
2740     SetBkMode( hdc, TRANSPARENT );\r
2741     SetTextColor( hdc, chroma );\r
2742     /* Step 2: the piece has been drawn in purple, there are now black and purple in this bitmap */\r
2743     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2744 \r
2745     SelectObject( hdc, GetStockObject(WHITE_BRUSH) );\r
2746     /* Step 3: the area outside the piece is filled with white */\r
2747 //    FloodFill( hdc, 0, 0, chroma );\r
2748     ExtFloodFill( hdc, 0, 0, 0, FLOODFILLSURFACE );\r
2749     ExtFloodFill( hdc, 0, squareSize-1, 0, FLOODFILLSURFACE ); // [HGM] fill from all 4 corners, for if piece too big\r
2750     ExtFloodFill( hdc, squareSize-1, 0, 0, FLOODFILLSURFACE );\r
2751     ExtFloodFill( hdc, squareSize-1, squareSize-1, 0, FLOODFILLSURFACE );\r
2752     SelectObject( hdc, GetStockObject(BLACK_BRUSH) );\r
2753     /* \r
2754         Step 4: this is the tricky part, the area inside the piece is filled with black,\r
2755         but if the start point is not inside the piece we're lost!\r
2756         There should be a better way to do this... if we could create a region or path\r
2757         from the fill operation we would be fine for example.\r
2758     */\r
2759 //    FloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF) );\r
2760     ExtFloodFill( hdc, squareSize / 2, squareSize / 2, RGB(0xFF,0xFF,0xFF), FLOODFILLBORDER );\r
2761 \r
2762     {   /* [HGM] shave off edges of mask, in an attempt to correct for the fact that FloodFill does not work correctly under Win XP */\r
2763         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2764         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2765 \r
2766         SelectObject( dc2, bm2 );\r
2767         BitBlt( dc2, 0, 0, squareSize, squareSize, hdc, 0, 0, SRCCOPY ); // make copy\r
2768         BitBlt( hdc, 0, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2769         BitBlt( hdc, 2, 1, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2770         BitBlt( hdc, 1, 0, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2771         BitBlt( hdc, 1, 2, squareSize-2, squareSize-2, dc2, 1, 1, SRCPAINT );\r
2772 \r
2773         DeleteDC( dc2 );\r
2774         DeleteObject( bm2 );\r
2775     }\r
2776 \r
2777     SetTextColor( hdc, 0 );\r
2778     /* \r
2779         Step 5: some fonts have "disconnected" areas that are skipped by the fill:\r
2780         draw the piece again in black for safety.\r
2781     */\r
2782     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2783 \r
2784     SelectObject( hdc, hbm_old );\r
2785 \r
2786     if( hPieceMask[index] != NULL ) {\r
2787         DeleteObject( hPieceMask[index] );\r
2788     }\r
2789 \r
2790     hPieceMask[index] = hbm;\r
2791 \r
2792     /* Face */\r
2793     hbm = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2794 \r
2795     SelectObject( hdc, hbm );\r
2796 \r
2797     {\r
2798         HDC dc1 = CreateCompatibleDC( hdc_window );\r
2799         HDC dc2 = CreateCompatibleDC( hdc_window );\r
2800         HBITMAP bm2 = CreateCompatibleBitmap( hdc_window, squareSize, squareSize );\r
2801 \r
2802         SelectObject( dc1, hPieceMask[index] );\r
2803         SelectObject( dc2, bm2 );\r
2804         FillRect( dc2, &rc, GetStockObject(WHITE_BRUSH) );\r
2805         BitBlt( dc2, 0, 0, squareSize, squareSize, dc1, 0, 0, SRCINVERT );\r
2806         \r
2807         /* \r
2808             Now dc2 contains the inverse of the piece mask, i.e. a mask that preserves\r
2809             the piece background and deletes (makes transparent) the rest.\r
2810             Thanks to that mask, we are free to paint the background with the greates\r
2811             freedom, as we'll be able to mask off the unwanted parts when finished.\r
2812             We use this, to make gradients and give the pieces a "roundish" look.\r
2813         */\r
2814         SetPieceBackground( hdc, backColor, 2 );\r
2815         BitBlt( hdc, 0, 0, squareSize, squareSize, dc2, 0, 0, SRCAND );\r
2816 \r
2817         DeleteDC( dc2 );\r
2818         DeleteDC( dc1 );\r
2819         DeleteObject( bm2 );\r
2820     }\r
2821 \r
2822     SetTextColor( hdc, foreColor );\r
2823     TextOut( hdc, pt.x, pt.y, &pieceToFontChar[appData.allWhite && index >= (int)BlackPawn ? index - (int)BlackPawn : index], 1 );\r
2824 \r
2825     SelectObject( hdc, hbm_old );\r
2826 \r
2827     if( hPieceFace[index] != NULL ) {\r
2828         DeleteObject( hPieceFace[index] );\r
2829     }\r
2830 \r
2831     hPieceFace[index] = hbm;\r
2832 }\r
2833 \r
2834 static int TranslatePieceToFontPiece( int piece )\r
2835 {\r
2836     switch( piece ) {\r
2837     case BlackPawn:\r
2838         return PM_BP;\r
2839     case BlackKnight:\r
2840         return PM_BN;\r
2841     case BlackBishop:\r
2842         return PM_BB;\r
2843     case BlackRook:\r
2844         return PM_BR;\r
2845     case BlackQueen:\r
2846         return PM_BQ;\r
2847     case BlackKing:\r
2848         return PM_BK;\r
2849     case WhitePawn:\r
2850         return PM_WP;\r
2851     case WhiteKnight:\r
2852         return PM_WN;\r
2853     case WhiteBishop:\r
2854         return PM_WB;\r
2855     case WhiteRook:\r
2856         return PM_WR;\r
2857     case WhiteQueen:\r
2858         return PM_WQ;\r
2859     case WhiteKing:\r
2860         return PM_WK;\r
2861 \r
2862     case BlackAngel:\r
2863         return PM_BA;\r
2864     case BlackMarshall:\r
2865         return PM_BC;\r
2866     case BlackFerz:\r
2867         return PM_BF;\r
2868     case BlackNightrider:\r
2869         return PM_BH;\r
2870     case BlackAlfil:\r
2871         return PM_BE;\r
2872     case BlackWazir:\r
2873         return PM_BW;\r
2874     case BlackUnicorn:\r
2875         return PM_BU;\r
2876     case BlackCannon:\r
2877         return PM_BO;\r
2878     case BlackGrasshopper:\r
2879         return PM_BG;\r
2880     case BlackMan:\r
2881         return PM_BM;\r
2882     case BlackSilver:\r
2883         return PM_BSG;\r
2884     case BlackLance:\r
2885         return PM_BL;\r
2886     case BlackFalcon:\r
2887         return PM_BV;\r
2888     case BlackCobra:\r
2889         return PM_BS;\r
2890     case BlackCardinal:\r
2891         return PM_BAB;\r
2892     case BlackDragon:\r
2893         return PM_BD;\r
2894 \r
2895     case WhiteAngel:\r
2896         return PM_WA;\r
2897     case WhiteMarshall:\r
2898         return PM_WC;\r
2899     case WhiteFerz:\r
2900         return PM_WF;\r
2901     case WhiteNightrider:\r
2902         return PM_WH;\r
2903     case WhiteAlfil:\r
2904         return PM_WE;\r
2905     case WhiteWazir:\r
2906         return PM_WW;\r
2907     case WhiteUnicorn:\r
2908         return PM_WU;\r
2909     case WhiteCannon:\r
2910         return PM_WO;\r
2911     case WhiteGrasshopper:\r
2912         return PM_WG;\r
2913     case WhiteMan:\r
2914         return PM_WM;\r
2915     case WhiteSilver:\r
2916         return PM_WSG;\r
2917     case WhiteLance:\r
2918         return PM_WL;\r
2919     case WhiteFalcon:\r
2920         return PM_WV;\r
2921     case WhiteCobra:\r
2922         return PM_WS;\r
2923     case WhiteCardinal:\r
2924         return PM_WAB;\r
2925     case WhiteDragon:\r
2926         return PM_WD;\r
2927     }\r
2928 \r
2929     return 0;\r
2930 }\r
2931 \r
2932 void CreatePiecesFromFont()\r
2933 {\r
2934     LOGFONT lf;\r
2935     HDC hdc_window = NULL;\r
2936     HDC hdc = NULL;\r
2937     HFONT hfont_old;\r
2938     int fontHeight;\r
2939     int i;\r
2940 \r
2941     if( fontBitmapSquareSize < 0 ) {\r
2942         /* Something went seriously wrong in the past: do not try to recreate fonts! */\r
2943         return;\r
2944     }\r
2945 \r
2946     if( appData.renderPiecesWithFont == NULL || appData.renderPiecesWithFont[0] == NULLCHAR || appData.renderPiecesWithFont[0] == '*' ) {\r
2947         fontBitmapSquareSize = -1;\r
2948         return;\r
2949     }\r
2950 \r
2951     if( fontBitmapSquareSize != squareSize ) {\r
2952         hdc_window = GetDC( hwndMain );\r
2953         hdc = CreateCompatibleDC( hdc_window );\r
2954 \r
2955         if( hPieceFont != NULL ) {\r
2956             DeleteObject( hPieceFont );\r
2957         }\r
2958         else {\r
2959             for( i=0; i<=(int)BlackKing; i++ ) {\r
2960                 hPieceMask[i] = NULL;\r
2961                 hPieceFace[i] = NULL;\r
2962             }\r
2963         }\r
2964 \r
2965         fontHeight = 75;\r
2966 \r
2967         if( appData.fontPieceSize >= 50 && appData.fontPieceSize <= 150 ) {\r
2968             fontHeight = appData.fontPieceSize;\r
2969         }\r
2970 \r
2971         fontHeight = (fontHeight * squareSize) / 100;\r
2972 \r
2973         lf.lfHeight = -MulDiv( fontHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72 );\r
2974         lf.lfWidth = 0;\r
2975         lf.lfEscapement = 0;\r
2976         lf.lfOrientation = 0;\r
2977         lf.lfWeight = FW_NORMAL;\r
2978         lf.lfItalic = 0;\r
2979         lf.lfUnderline = 0;\r
2980         lf.lfStrikeOut = 0;\r
2981         lf.lfCharSet = DEFAULT_CHARSET;\r
2982         lf.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
2983         lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
2984         lf.lfQuality = PROOF_QUALITY;\r
2985         lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;\r
2986         strncpy( lf.lfFaceName, appData.renderPiecesWithFont, sizeof(lf.lfFaceName) );\r
2987         lf.lfFaceName[ sizeof(lf.lfFaceName) - 1 ] = '\0';\r
2988 \r
2989         hPieceFont = CreateFontIndirect( &lf );\r
2990 \r
2991         if( hPieceFont == NULL ) {\r
2992             fontBitmapSquareSize = -2;\r
2993         }\r
2994         else {\r
2995             /* Setup font-to-piece character table */\r
2996             if( ! SetCharTable(pieceToFontChar, appData.fontToPieceTable) ) {\r
2997                 /* No (or wrong) global settings, try to detect the font */\r
2998                 if( strstr(lf.lfFaceName,"Alpha") != NULL ) {\r
2999                     /* Alpha */\r
3000                     SetCharTable(pieceToFontChar, "phbrqkojntwl");\r
3001                 }\r
3002                 else if( strstr(lf.lfFaceName,"DiagramTT") != NULL ) {\r
3003                     /* DiagramTT* family */\r
3004                     SetCharTable(pieceToFontChar, "PNLRQKpnlrqk");\r
3005                 }\r
3006                 else if( strstr(lf.lfFaceName,"WinboardF") != NULL ) {\r
3007                     /* Fairy symbols */\r
3008                      SetCharTable(pieceToFontChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");\r
3009                 }\r
3010                 else if( strstr(lf.lfFaceName,"GC2004D") != NULL ) {\r
3011                     /* Good Companion (Some characters get warped as literal :-( */\r
3012                     char s[] = "1cmWG0ñueOS¯®oYI23wgQU";\r
3013                     s[0]=0xB9; s[1]=0xA9; s[6]=0xB1; s[11]=0xBB; s[12]=0xAB; s[17]=0xB3;\r
3014                     SetCharTable(pieceToFontChar, s);\r
3015                 }\r
3016                 else {\r
3017                     /* Cases, Condal, Leipzig, Lucena, Marroquin, Merida, Usual */\r
3018                     SetCharTable(pieceToFontChar, "pnbrqkomvtwl");\r
3019                 }\r
3020             }\r
3021 \r
3022             /* Create bitmaps */\r
3023             hfont_old = SelectObject( hdc, hPieceFont );\r
3024 #if 0\r
3025             CreatePieceMaskFromFont( hdc_window, hdc, PM_WP );\r
3026             CreatePieceMaskFromFont( hdc_window, hdc, PM_WN );\r
3027             CreatePieceMaskFromFont( hdc_window, hdc, PM_WB );\r
3028             CreatePieceMaskFromFont( hdc_window, hdc, PM_WR );\r
3029             CreatePieceMaskFromFont( hdc_window, hdc, PM_WQ );\r
3030             CreatePieceMaskFromFont( hdc_window, hdc, PM_WK );\r
3031             CreatePieceMaskFromFont( hdc_window, hdc, PM_BP );\r
3032             CreatePieceMaskFromFont( hdc_window, hdc, PM_BN );\r
3033             CreatePieceMaskFromFont( hdc_window, hdc, PM_BB );\r
3034             CreatePieceMaskFromFont( hdc_window, hdc, PM_BR );\r
3035             CreatePieceMaskFromFont( hdc_window, hdc, PM_BQ );\r
3036             CreatePieceMaskFromFont( hdc_window, hdc, PM_BK );\r
3037 \r
3038             CreatePieceMaskFromFont( hdc_window, hdc, PM_WA );\r
3039             CreatePieceMaskFromFont( hdc_window, hdc, PM_WC );\r
3040             CreatePieceMaskFromFont( hdc_window, hdc, PM_WF );\r
3041             CreatePieceMaskFromFont( hdc_window, hdc, PM_WH );\r
3042             CreatePieceMaskFromFont( hdc_window, hdc, PM_WE );\r
3043             CreatePieceMaskFromFont( hdc_window, hdc, PM_WW );\r
3044             CreatePieceMaskFromFont( hdc_window, hdc, PM_WU );\r
3045             CreatePieceMaskFromFont( hdc_window, hdc, PM_WO );\r
3046             CreatePieceMaskFromFont( hdc_window, hdc, PM_WG );\r
3047             CreatePieceMaskFromFont( hdc_window, hdc, PM_WM );\r
3048             CreatePieceMaskFromFont( hdc_window, hdc, PM_WSG );\r
3049             CreatePieceMaskFromFont( hdc_window, hdc, PM_WV );\r
3050             CreatePieceMaskFromFont( hdc_window, hdc, PM_WAB );\r
3051             CreatePieceMaskFromFont( hdc_window, hdc, PM_WD );\r
3052             CreatePieceMaskFromFont( hdc_window, hdc, PM_WL );\r
3053             CreatePieceMaskFromFont( hdc_window, hdc, PM_WS );\r
3054             CreatePieceMaskFromFont( hdc_window, hdc, PM_BA );\r
3055             CreatePieceMaskFromFont( hdc_window, hdc, PM_BC );\r
3056             CreatePieceMaskFromFont( hdc_window, hdc, PM_BF );\r
3057             CreatePieceMaskFromFont( hdc_window, hdc, PM_BH );\r
3058             CreatePieceMaskFromFont( hdc_window, hdc, PM_BE );\r
3059             CreatePieceMaskFromFont( hdc_window, hdc, PM_BW );\r
3060             CreatePieceMaskFromFont( hdc_window, hdc, PM_BU );\r
3061             CreatePieceMaskFromFont( hdc_window, hdc, PM_BO );\r
3062             CreatePieceMaskFromFont( hdc_window, hdc, PM_BG );\r
3063             CreatePieceMaskFromFont( hdc_window, hdc, PM_BM );\r
3064             CreatePieceMaskFromFont( hdc_window, hdc, PM_BSG );\r
3065             CreatePieceMaskFromFont( hdc_window, hdc, PM_BV );\r
3066             CreatePieceMaskFromFont( hdc_window, hdc, PM_BAB );\r
3067             CreatePieceMaskFromFont( hdc_window, hdc, PM_BD );\r
3068             CreatePieceMaskFromFont( hdc_window, hdc, PM_BL );\r
3069             CreatePieceMaskFromFont( hdc_window, hdc, PM_BS );\r
3070 #else\r
3071             for(i=(int)WhitePawn; i<(int)EmptySquare; i++) /* [HGM] made a loop for this */\r
3072                 if(PieceToChar((ChessSquare)i) != '.')     /* skip unused pieces         */\r
3073                     CreatePieceMaskFromFont( hdc_window, hdc, i );\r
3074 #endif\r
3075             SelectObject( hdc, hfont_old );\r
3076 \r
3077             fontBitmapSquareSize = squareSize;\r
3078         }\r
3079     }\r
3080 \r
3081     if( hdc != NULL ) {\r
3082         DeleteDC( hdc );\r
3083     }\r
3084 \r
3085     if( hdc_window != NULL ) {\r
3086         ReleaseDC( hwndMain, hdc_window );\r
3087     }\r
3088 }\r
3089 \r
3090 HBITMAP\r
3091 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
3092 {\r
3093   char name[128];\r
3094 \r
3095   sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
3096   if (gameInfo.event &&\r
3097       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
3098       strcmp(name, "k80s") == 0) {\r
3099     strcpy(name, "tim");\r
3100   }\r
3101   return LoadBitmap(hinst, name);\r
3102 }\r
3103 \r
3104 \r
3105 /* Insert a color into the program's logical palette\r
3106    structure.  This code assumes the given color is\r
3107    the result of the RGB or PALETTERGB macro, and it\r
3108    knows how those macros work (which is documented).\r
3109 */\r
3110 VOID\r
3111 InsertInPalette(COLORREF color)\r
3112 {\r
3113   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
3114 \r
3115   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
3116     DisplayFatalError("Too many colors", 0, 1);\r
3117     pLogPal->palNumEntries--;\r
3118     return;\r
3119   }\r
3120 \r
3121   pe->peFlags = (char) 0;\r
3122   pe->peRed = (char) (0xFF & color);\r
3123   pe->peGreen = (char) (0xFF & (color >> 8));\r
3124   pe->peBlue = (char) (0xFF & (color >> 16));\r
3125   return;\r
3126 }\r
3127 \r
3128 \r
3129 VOID\r
3130 InitDrawingColors()\r
3131 {\r
3132   if (pLogPal == NULL) {\r
3133     /* Allocate enough memory for a logical palette with\r
3134      * PALETTESIZE entries and set the size and version fields\r
3135      * of the logical palette structure.\r
3136      */\r
3137     pLogPal = (NPLOGPALETTE)\r
3138       LocalAlloc(LMEM_FIXED, (sizeof(LOGPALETTE) +\r
3139                               (sizeof(PALETTEENTRY) * (PALETTESIZE))));\r
3140     pLogPal->palVersion    = 0x300;\r
3141   }\r
3142   pLogPal->palNumEntries = 0;\r
3143 \r
3144   InsertInPalette(lightSquareColor);\r
3145   InsertInPalette(darkSquareColor);\r
3146   InsertInPalette(whitePieceColor);\r
3147   InsertInPalette(blackPieceColor);\r
3148   InsertInPalette(highlightSquareColor);\r
3149   InsertInPalette(premoveHighlightColor);\r
3150 \r
3151   /*  create a logical color palette according the information\r
3152    *  in the LOGPALETTE structure.\r
3153    */\r
3154   hPal = CreatePalette((LPLOGPALETTE) pLogPal);\r
3155 \r
3156   lightSquareBrush = CreateSolidBrush(lightSquareColor);\r
3157   blackSquareBrush = CreateSolidBrush(blackPieceColor);\r
3158   darkSquareBrush = CreateSolidBrush(darkSquareColor);\r
3159   whitePieceBrush = CreateSolidBrush(whitePieceColor);\r
3160   blackPieceBrush = CreateSolidBrush(blackPieceColor);\r
3161   iconBkgndBrush = CreateSolidBrush(GetSysColor(COLOR_BACKGROUND));\r
3162   explodeBrush = CreateSolidBrush(highlightSquareColor); // [HGM] atomic\r
3163   /* [AS] Force rendering of the font-based pieces */\r
3164   if( fontBitmapSquareSize > 0 ) {\r
3165     fontBitmapSquareSize = 0;\r
3166   }\r
3167 }\r
3168 \r
3169 \r
3170 int\r
3171 BoardWidth(int boardSize, int n)\r
3172 { /* [HGM] argument n added to allow different width and height */\r
3173   int lineGap = sizeInfo[boardSize].lineGap;\r
3174 \r
3175   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3176       lineGap = appData.overrideLineGap;\r
3177   }\r
3178 \r
3179   return (n + 1) * lineGap +\r
3180           n * sizeInfo[boardSize].squareSize;\r
3181 }\r
3182 \r
3183 /* Respond to board resize by dragging edge */\r
3184 VOID\r
3185 ResizeBoard(int newSizeX, int newSizeY, int flags)\r
3186 {\r
3187   BoardSize newSize = NUM_SIZES - 1;\r
3188   static int recurse = 0;\r
3189   if (IsIconic(hwndMain)) return;\r
3190   if (recurse > 0) return;\r
3191   recurse++;\r
3192   while (newSize > 0) {\r
3193         InitDrawingSizes(newSize+1000, 0); // [HGM] kludge to update sizeInfo without visible effects\r
3194         if(newSizeX >= sizeInfo[newSize].cliWidth &&\r
3195            newSizeY >= sizeInfo[newSize].cliHeight) break;\r
3196     newSize--;\r
3197   } \r
3198   boardSize = newSize;\r
3199   InitDrawingSizes(boardSize, flags);\r
3200   recurse--;\r
3201 }\r
3202 \r
3203 \r
3204 \r
3205 VOID\r
3206 InitDrawingSizes(BoardSize boardSize, int flags)\r
3207 {\r
3208   int i, boardWidth, boardHeight; /* [HGM] height treated separately */\r
3209   ChessSquare piece;\r
3210   static int oldBoardSize = -1, oldTinyLayout = 0;\r
3211   HDC hdc;\r
3212   SIZE clockSize, messageSize;\r
3213   HFONT oldFont;\r
3214   char buf[MSG_SIZ];\r
3215   char *str;\r
3216   HMENU hmenu = GetMenu(hwndMain);\r
3217   RECT crect, wrect, oldRect;\r
3218   int offby;\r
3219   LOGBRUSH logbrush;\r
3220 \r
3221   int suppressVisibleEffects = 0; // [HGM] kludge to request updating sizeInfo only\r
3222   if((int)boardSize >= 1000 ) { boardSize -= 1000; suppressVisibleEffects = 1; }\r
3223 \r
3224   /* [HGM] call with -2 uses old size (for if nr of files, ranks changes) */\r
3225   if(boardSize == (BoardSize)(-2) ) boardSize = oldBoardSize;\r
3226 \r
3227   oldRect.left = boardX; //[HGM] placement: remember previous window params\r
3228   oldRect.top = boardY;\r
3229   oldRect.right = boardX + winWidth;\r
3230   oldRect.bottom = boardY + winHeight;\r
3231 \r
3232   tinyLayout = sizeInfo[boardSize].tinyLayout;\r
3233   smallLayout = sizeInfo[boardSize].smallLayout;\r
3234   squareSize = sizeInfo[boardSize].squareSize;\r
3235   lineGap = sizeInfo[boardSize].lineGap;\r
3236   minorSize = 0; /* [HGM] Kludge to see if demagnified pieces need to be shifted  */\r
3237 \r
3238   if( appData.overrideLineGap >= 0 && appData.overrideLineGap <= 5 ) {\r
3239       lineGap = appData.overrideLineGap;\r
3240   }\r
3241 \r
3242   if (tinyLayout != oldTinyLayout) {\r
3243     long style = GetWindowLong(hwndMain, GWL_STYLE);\r
3244     if (tinyLayout) {\r
3245       style &= ~WS_SYSMENU;\r
3246       InsertMenu(hmenu, IDM_Exit, MF_BYCOMMAND, IDM_Minimize,\r
3247                  "&Minimize\tCtrl+F4");\r
3248     } else {\r
3249       style |= WS_SYSMENU;\r
3250       RemoveMenu(hmenu, IDM_Minimize, MF_BYCOMMAND);\r
3251     }\r
3252     SetWindowLong(hwndMain, GWL_STYLE, style);\r
3253 \r
3254     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
3255       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
3256         (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
3257     }\r
3258     DrawMenuBar(hwndMain);\r
3259   }\r
3260 \r
3261   boardWidth  = BoardWidth(boardSize, BOARD_WIDTH);\r
3262   boardHeight = BoardWidth(boardSize, BOARD_HEIGHT);\r
3263 \r
3264   /* Get text area sizes */\r
3265   hdc = GetDC(hwndMain);\r
3266   if (appData.clockMode) {\r
3267     sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
3268   } else {\r
3269     sprintf(buf, "White");\r
3270   }\r
3271   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
3272   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
3273   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
3274   str = "We only care about the height here";\r
3275   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
3276   SelectObject(hdc, oldFont);\r
3277   ReleaseDC(hwndMain, hdc);\r
3278 \r
3279   /* Compute where everything goes */\r
3280   if((first.programLogo || second.programLogo) && !tinyLayout) {\r
3281         /* [HGM] logo: if either logo is on, reserve space for it */\r
3282         logoHeight =  2*clockSize.cy;\r
3283         leftLogoRect.left   = OUTER_MARGIN;\r
3284         leftLogoRect.right  = leftLogoRect.left + 4*clockSize.cy;\r
3285         leftLogoRect.top    = OUTER_MARGIN;\r
3286         leftLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3287 \r
3288         rightLogoRect.right  = OUTER_MARGIN + boardWidth;\r
3289         rightLogoRect.left   = rightLogoRect.right - 4*clockSize.cy;\r
3290         rightLogoRect.top    = OUTER_MARGIN;\r
3291         rightLogoRect.bottom = OUTER_MARGIN + logoHeight;\r
3292 \r
3293 \r
3294     whiteRect.left = leftLogoRect.right;\r
3295     whiteRect.right = OUTER_MARGIN + boardWidth/2 - INNER_MARGIN/2;\r
3296     whiteRect.top = OUTER_MARGIN;\r
3297     whiteRect.bottom = whiteRect.top + logoHeight;\r
3298 \r
3299     blackRect.right = rightLogoRect.left;\r
3300     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3301     blackRect.top = whiteRect.top;\r
3302     blackRect.bottom = whiteRect.bottom;\r
3303   } else {\r
3304     whiteRect.left = OUTER_MARGIN;\r
3305     whiteRect.right = whiteRect.left + boardWidth/2 - INNER_MARGIN/2;\r
3306     whiteRect.top = OUTER_MARGIN;\r
3307     whiteRect.bottom = whiteRect.top + clockSize.cy;\r
3308 \r
3309     blackRect.left = whiteRect.right + INNER_MARGIN;\r
3310     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
3311     blackRect.top = whiteRect.top;\r
3312     blackRect.bottom = whiteRect.bottom;\r
3313   }\r
3314 \r
3315   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
3316   if (appData.showButtonBar) {\r
3317     messageRect.right = OUTER_MARGIN + boardWidth         // [HGM] logo: expressed independent of clock placement\r
3318       - N_BUTTONS*BUTTON_WIDTH - MESSAGE_LINE_LEFTMARGIN;\r
3319   } else {\r
3320     messageRect.right = OUTER_MARGIN + boardWidth;\r
3321   }\r
3322   messageRect.top = whiteRect.bottom + INNER_MARGIN;\r
3323   messageRect.bottom = messageRect.top + messageSize.cy;\r
3324 \r
3325   boardRect.left = OUTER_MARGIN;\r
3326   boardRect.right = boardRect.left + boardWidth;\r
3327   boardRect.top = messageRect.bottom + INNER_MARGIN;\r
3328   boardRect.bottom = boardRect.top + boardHeight;\r
3329 \r
3330   sizeInfo[boardSize].cliWidth = boardRect.right + OUTER_MARGIN;\r
3331   sizeInfo[boardSize].cliHeight = boardRect.bottom + OUTER_MARGIN;\r
3332   oldBoardSize = boardSize;\r
3333   oldTinyLayout = tinyLayout;\r
3334   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
3335   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
3336     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
3337   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
3338   winWidth = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
3339   winHeight = winH; //       without disturbing window attachments\r
3340   GetWindowRect(hwndMain, &wrect);\r
3341   SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3342                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3343 \r
3344   // [HGM] placement: let attached windows follow size change.\r
3345   ReattachAfterSize( &oldRect, winWidth, winHeight, moveHistoryDialog, &wpMoveHistory );\r
3346   ReattachAfterSize( &oldRect, winWidth, winHeight, evalGraphDialog, &wpEvalGraph );\r
3347   ReattachAfterSize( &oldRect, winWidth, winHeight, engineOutputDialog, &wpEngineOutput );\r
3348   ReattachAfterSize( &oldRect, winWidth, winHeight, gameListDialog, &wpGameList );\r
3349   ReattachAfterSize( &oldRect, winWidth, winHeight, hwndConsole, &wpConsole );\r
3350 \r
3351   /* compensate if menu bar wrapped */\r
3352   GetClientRect(hwndMain, &crect);\r
3353   offby = boardRect.bottom + OUTER_MARGIN - crect.bottom;\r
3354   winHeight += offby;\r
3355   switch (flags) {\r
3356   case WMSZ_TOPLEFT:\r
3357     SetWindowPos(hwndMain, NULL, \r
3358                  wrect.right - winWidth, wrect.bottom - winHeight, \r
3359                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3360     break;\r
3361 \r
3362   case WMSZ_TOPRIGHT:\r
3363   case WMSZ_TOP:\r
3364     SetWindowPos(hwndMain, NULL, \r
3365                  wrect.left, wrect.bottom - winHeight, \r
3366                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3367     break;\r
3368 \r
3369   case WMSZ_BOTTOMLEFT:\r
3370   case WMSZ_LEFT:\r
3371     SetWindowPos(hwndMain, NULL, \r
3372                  wrect.right - winWidth, wrect.top, \r
3373                  winWidth, winHeight, SWP_NOCOPYBITS|SWP_NOZORDER);\r
3374     break;\r
3375 \r
3376   case WMSZ_BOTTOMRIGHT:\r
3377   case WMSZ_BOTTOM:\r
3378   case WMSZ_RIGHT:\r
3379   default:\r
3380     SetWindowPos(hwndMain, NULL, 0, 0, winWidth, winHeight,\r
3381                SWP_NOCOPYBITS|SWP_NOZORDER|SWP_NOMOVE);\r
3382     break;\r
3383   }\r
3384 \r
3385   hwndPause = NULL;\r
3386   for (i = 0; i < N_BUTTONS; i++) {\r
3387     if (buttonDesc[i].hwnd != NULL) {\r
3388       DestroyWindow(buttonDesc[i].hwnd);\r
3389       buttonDesc[i].hwnd = NULL;\r
3390     }\r
3391     if (appData.showButtonBar) {\r
3392       buttonDesc[i].hwnd =\r
3393         CreateWindow("BUTTON", buttonDesc[i].label,\r
3394                      WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,\r
3395                      boardRect.right - BUTTON_WIDTH*(N_BUTTONS-i),\r
3396                      messageRect.top, BUTTON_WIDTH, messageSize.cy, hwndMain,\r
3397                      (HMENU) buttonDesc[i].id,\r
3398                      (HINSTANCE) GetWindowLong(hwndMain, GWL_HINSTANCE), NULL);\r
3399       if (tinyLayout) {\r
3400         SendMessage(buttonDesc[i].hwnd, WM_SETFONT, \r
3401                     (WPARAM)font[boardSize][MESSAGE_FONT]->hf,\r
3402                     MAKELPARAM(FALSE, 0));\r
3403       }\r
3404       if (buttonDesc[i].id == IDM_Pause)\r
3405         hwndPause = buttonDesc[i].hwnd;\r
3406       buttonDesc[i].wndproc = (WNDPROC)\r
3407         SetWindowLong(buttonDesc[i].hwnd, GWL_WNDPROC, (LONG) ButtonProc);\r
3408     }\r
3409   }\r
3410   if (gridPen != NULL) DeleteObject(gridPen);\r
3411   if (highlightPen != NULL) DeleteObject(highlightPen);\r
3412   if (premovePen != NULL) DeleteObject(premovePen);\r
3413   if (lineGap != 0) {\r
3414     logbrush.lbStyle = BS_SOLID;\r
3415     logbrush.lbColor = RGB(0, 0, 0); /* grid pen color = black */\r
3416     gridPen =\r
3417       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3418                    lineGap, &logbrush, 0, NULL);\r
3419     logbrush.lbColor = highlightSquareColor;\r
3420     highlightPen =\r
3421       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3422                    lineGap, &logbrush, 0, NULL);\r
3423 \r
3424     logbrush.lbColor = premoveHighlightColor; \r
3425     premovePen =\r
3426       ExtCreatePen(PS_GEOMETRIC|PS_SOLID|PS_ENDCAP_FLAT|PS_JOIN_MITER,\r
3427                    lineGap, &logbrush, 0, NULL);\r
3428 \r
3429     /* [HGM] Loop had to be split in part for vert. and hor. lines */\r
3430     for (i = 0; i < BOARD_HEIGHT + 1; i++) {\r
3431       gridEndpoints[i*2].x = boardRect.left + lineGap / 2;\r
3432       gridEndpoints[i*2].y = gridEndpoints[i*2 + 1].y =\r
3433         boardRect.top + lineGap / 2 + (i * (squareSize + lineGap));\r
3434       gridEndpoints[i*2 + 1].x = boardRect.left + lineGap / 2 +\r
3435         BOARD_WIDTH * (squareSize + lineGap);\r
3436       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3437     }\r
3438     for (i = 0; i < BOARD_WIDTH + 1; i++) {\r
3439       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].y = boardRect.top + lineGap / 2;\r
3440       gridEndpoints[i*2 + BOARD_HEIGHT*2 + 2].x =\r
3441         gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].x = boardRect.left +\r
3442         lineGap / 2 + (i * (squareSize + lineGap));\r
3443       gridEndpoints[i*2 + 1 + BOARD_HEIGHT*2 + 2].y =\r
3444         boardRect.top + BOARD_HEIGHT * (squareSize + lineGap);\r
3445       gridVertexCounts[i*2] = gridVertexCounts[i*2 + 1] = 2;\r
3446     }\r
3447   }\r
3448 \r
3449   /* [HGM] Licensing requirement */\r
3450 #ifdef GOTHIC\r
3451   if(gameInfo.variant == VariantGothic) GothicPopUp( GOTHIC, VariantGothic); else\r
3452 #endif\r
3453 #ifdef FALCON\r
3454   if(gameInfo.variant == VariantFalcon) GothicPopUp( FALCON, VariantFalcon); else\r
3455 #endif\r
3456   GothicPopUp( "", VariantNormal);\r
3457 \r
3458 \r
3459 /*  if (boardSize == oldBoardSize) return; [HGM] variant might have changed */\r
3460 \r
3461   /* Load piece bitmaps for this board size */\r
3462   for (i=0; i<=2; i++) {\r
3463     for (piece = WhitePawn;\r
3464          (int) piece < (int) BlackPawn;\r
3465          piece = (ChessSquare) ((int) piece + 1)) {\r
3466       if (pieceBitmap[i][piece] != NULL)\r
3467         DeleteObject(pieceBitmap[i][piece]);\r
3468     }\r
3469   }\r
3470 \r
3471   fontBitmapSquareSize = 0; /* [HGM] render: make sure pieces will be recreated, as we might need others now */\r
3472   // Orthodox Chess pieces\r
3473   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "s");\r
3474   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "s");\r
3475   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "s");\r
3476   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "s");\r
3477   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "s");\r
3478   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "o");\r
3479   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "o");\r
3480   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "o");\r
3481   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "o");\r
3482   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "o");\r
3483   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "p", squareSize, "w");\r
3484   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
3485   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "b", squareSize, "w");\r
3486   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "r", squareSize, "w");\r
3487   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "k", squareSize, "w");\r
3488   if( !strcmp(appData.variant, "shogi") && (squareSize==72 || squareSize==49)) {\r
3489     // in Shogi, Hijack the unused Queen for Lance\r
3490     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3491     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3492     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3493   } else {\r
3494     pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "s");\r
3495     pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "o");\r
3496     pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "q", squareSize, "w");\r
3497   }\r
3498 \r
3499   if(squareSize <= 72 && squareSize >= 33) { \r
3500     /* A & C are available in most sizes now */\r
3501     if(squareSize != 49 && squareSize != 72 && squareSize != 33) { // Vortex-like\r
3502       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3503       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3504       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3505       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3506       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3507       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3508       pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3509       pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3510       pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3511       pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3512       pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3513       pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3514     } else { // Smirf-like\r
3515       pieceBitmap[0][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "s");\r
3516       pieceBitmap[1][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "o");\r
3517       pieceBitmap[2][WhiteAngel] = DoLoadBitmap(hInst, "aa", squareSize, "w");\r
3518     }\r
3519     if(gameInfo.variant == VariantGothic) { // Vortex-like\r
3520       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3521       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3522       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3523     } else { // WinBoard standard\r
3524       pieceBitmap[0][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "s");\r
3525       pieceBitmap[1][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "o");\r
3526       pieceBitmap[2][WhiteMarshall] = DoLoadBitmap(hInst, "c", squareSize, "w");\r
3527     }\r
3528   }\r
3529 \r
3530 \r
3531   if(squareSize==72 || squareSize==49 || squareSize==33) { /* experiment with some home-made bitmaps */\r
3532     pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "s");\r
3533     pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "o");\r
3534     pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "f", squareSize, "w");\r
3535     pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "s");\r
3536     pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "o");\r
3537     pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3538     pieceBitmap[0][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "s");\r
3539     pieceBitmap[1][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "o");\r
3540     pieceBitmap[2][WhiteAlfil] = DoLoadBitmap(hInst, "e", squareSize, "w");\r
3541     pieceBitmap[0][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "s");\r
3542     pieceBitmap[1][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "o");\r
3543     pieceBitmap[2][WhiteMan] = DoLoadBitmap(hInst, "m", squareSize, "w");\r
3544     pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "s");\r
3545     pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "o");\r
3546     pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "a", squareSize, "w");\r
3547     pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "s");\r
3548     pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "o");\r
3549     pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "dk", squareSize, "w");\r
3550     pieceBitmap[0][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "s");\r
3551     pieceBitmap[1][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "o");\r
3552     pieceBitmap[2][WhiteFalcon] = DoLoadBitmap(hInst, "v", squareSize, "w");\r
3553     pieceBitmap[0][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "s");\r
3554     pieceBitmap[1][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "o");\r
3555     pieceBitmap[2][WhiteCobra] = DoLoadBitmap(hInst, "s", squareSize, "w");\r
3556     pieceBitmap[0][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "s");\r
3557     pieceBitmap[1][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "o");\r
3558     pieceBitmap[2][WhiteLance] = DoLoadBitmap(hInst, "l", squareSize, "w");\r
3559     pieceBitmap[0][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "s");\r
3560     pieceBitmap[1][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "o");\r
3561     pieceBitmap[2][WhiteUnicorn] = DoLoadBitmap(hInst, "u", squareSize, "w");\r
3562 \r
3563     if(gameInfo.variant == VariantShogi) { /* promoted Gold represemtations */\r
3564       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
3565       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "o");\r
3566       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3567       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "s");\r
3568       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "wn", squareSize, "o");\r
3569       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3570       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "s");\r
3571       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ws", squareSize, "o");\r
3572       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3573       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "s");\r
3574       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "wl", squareSize, "o");\r
3575       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "w", squareSize, "w");\r
3576     } else {\r
3577       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "s");\r
3578       pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "o");\r
3579       pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "o", squareSize, "w");\r
3580       pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "s");\r
3581       pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "o");\r
3582       pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "h", squareSize, "w");\r
3583       pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "s");\r
3584       pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "o");\r
3585       pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "cv", squareSize, "w");\r
3586       pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "s");\r
3587       pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "o");\r
3588       pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "g", squareSize, "w");\r
3589     }\r
3590 \r
3591   } else { /* other size, no special bitmaps available. Use smaller symbols */\r
3592     if((int)boardSize < 2) minorSize = sizeInfo[0].squareSize;\r
3593     else  minorSize = sizeInfo[(int)boardSize - 2].squareSize;\r
3594     pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "s");\r
3595     pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "o");\r
3596     pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "n", minorSize, "w");\r
3597     pieceBitmap[0][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "s");\r
3598     pieceBitmap[1][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "o");\r
3599     pieceBitmap[2][WhiteCardinal]   = DoLoadBitmap(hInst, "b", minorSize, "w");\r
3600     pieceBitmap[0][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "s");\r
3601     pieceBitmap[1][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "o");\r
3602     pieceBitmap[2][WhiteDragon]   = DoLoadBitmap(hInst, "r", minorSize, "w");\r
3603     pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "s");\r
3604     pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "o");\r
3605     pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "q", minorSize, "w");\r
3606   }\r
3607 \r
3608 \r
3609   if(gameInfo.variant == VariantShogi && squareSize == 58)\r
3610   /* special Shogi support in this size */\r
3611   { for (i=0; i<=2; i++) { /* replace all bitmaps */\r
3612       for (piece = WhitePawn;\r
3613            (int) piece < (int) BlackPawn;\r
3614            piece = (ChessSquare) ((int) piece + 1)) {\r
3615         if (pieceBitmap[i][piece] != NULL)\r
3616           DeleteObject(pieceBitmap[i][piece]);\r
3617       }\r
3618     }\r
3619   pieceBitmap[0][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3620   pieceBitmap[0][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3621   pieceBitmap[0][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3622   pieceBitmap[0][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3623   pieceBitmap[0][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3624   pieceBitmap[0][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3625   pieceBitmap[0][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3626   pieceBitmap[0][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3627   pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3628   pieceBitmap[0][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3629   pieceBitmap[0][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3630   pieceBitmap[0][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3631   pieceBitmap[0][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3632   pieceBitmap[0][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3633   pieceBitmap[1][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "o");\r
3634   pieceBitmap[1][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "o");\r
3635   pieceBitmap[1][WhiteBishop] = DoLoadBitmap(hInst, "sb", squareSize, "o");\r
3636   pieceBitmap[1][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "o");\r
3637   pieceBitmap[1][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "o");\r
3638   pieceBitmap[1][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "o");\r
3639   pieceBitmap[1][WhiteFerz] = DoLoadBitmap(hInst, "sf", squareSize, "o");\r
3640   pieceBitmap[1][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "o");\r
3641   pieceBitmap[1][WhiteCannon] = DoLoadBitmap(hInst, "su", squareSize, "o");\r
3642   pieceBitmap[1][WhiteNightrider] = DoLoadBitmap(hInst, "sh", squareSize, "o");\r
3643   pieceBitmap[1][WhiteCardinal] = DoLoadBitmap(hInst, "sa", squareSize, "o");\r
3644   pieceBitmap[1][WhiteDragon] = DoLoadBitmap(hInst, "sc", squareSize, "o");\r
3645   pieceBitmap[1][WhiteGrasshopper] = DoLoadBitmap(hInst, "sg", squareSize, "o");\r
3646   pieceBitmap[1][WhiteSilver] = DoLoadBitmap(hInst, "ss", squareSize, "o");\r
3647   pieceBitmap[2][WhitePawn] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3648   pieceBitmap[2][WhiteKnight] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3649   pieceBitmap[2][WhiteBishop] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3650   pieceBitmap[2][WhiteRook] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3651   pieceBitmap[2][WhiteQueen] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3652   pieceBitmap[2][WhiteKing] = DoLoadBitmap(hInst, "sk", squareSize, "w");\r
3653   pieceBitmap[2][WhiteFerz] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3654   pieceBitmap[2][WhiteWazir] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3655   pieceBitmap[2][WhiteCannon] = DoLoadBitmap(hInst, "sp", squareSize, "w");\r
3656   pieceBitmap[2][WhiteNightrider] = DoLoadBitmap(hInst, "sn", squareSize, "w");\r
3657   pieceBitmap[2][WhiteCardinal] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3658   pieceBitmap[2][WhiteDragon] = DoLoadBitmap(hInst, "sr", squareSize, "w");\r
3659   pieceBitmap[2][WhiteGrasshopper] = DoLoadBitmap(hInst, "sl", squareSize, "w");\r
3660   pieceBitmap[2][WhiteSilver] = DoLoadBitmap(hInst, "sw", squareSize, "w");\r
3661   minorSize = 0;\r
3662   }\r
3663 }\r
3664 \r
3665 HBITMAP\r
3666 PieceBitmap(ChessSquare p, int kind)\r
3667 {\r
3668   if ((int) p >= (int) BlackPawn)\r
3669     p = (ChessSquare) ((int) p - (int) BlackPawn + (int) WhitePawn);\r
3670 \r
3671   return pieceBitmap[kind][(int) p];\r
3672 }\r
3673 \r
3674 /***************************************************************/\r
3675 \r
3676 #define MIN(a,b) (((a) < (b)) ? (a) : (b))\r
3677 #define MAX(a,b) (((a) > (b)) ? (a) : (b))\r
3678 /*\r
3679 #define MIN3(a,b,c) (((a) < (b) && (a) < (c)) ? (a) : (((b) < (a) && (b) < (c)) ? (b) : (c)))\r
3680 #define MAX3(a,b,c) (((a) > (b) && (a) > (c)) ? (a) : (((b) > (a) && (b) > (c)) ? (b) : (c)))\r
3681 */\r
3682 \r
3683 VOID\r
3684 SquareToPos(int row, int column, int * x, int * y)\r
3685 {\r
3686   if (flipView) {\r
3687     *x = boardRect.left + lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
3688     *y = boardRect.top + lineGap + row * (squareSize + lineGap);\r
3689   } else {\r
3690     *x = boardRect.left + lineGap + column * (squareSize + lineGap);\r
3691     *y = boardRect.top + lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
3692   }\r
3693 }\r
3694 \r
3695 VOID\r
3696 DrawCoordsOnDC(HDC hdc)\r
3697 {\r
3698   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
3699   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
3700   char str[2] = { NULLCHAR, NULLCHAR };\r
3701   int oldMode, oldAlign, x, y, start, i;\r
3702   HFONT oldFont;\r
3703   HBRUSH oldBrush;\r
3704 \r
3705   if (!appData.showCoords)\r
3706     return;\r
3707 \r
3708   start = flipView ? 1-(ONE!='1') : 23+(ONE!='1')-BOARD_HEIGHT;\r
3709 \r
3710   oldBrush = SelectObject(hdc, GetStockObject(BLACK_BRUSH));\r
3711   oldMode = SetBkMode(hdc, (appData.monoMode ? OPAQUE : TRANSPARENT));\r
3712   oldAlign = GetTextAlign(hdc);\r
3713   oldFont = SelectObject(hdc, font[boardSize][COORD_FONT]->hf);\r
3714 \r
3715   y = boardRect.top + lineGap;\r
3716   x = boardRect.left + lineGap + gameInfo.holdingsWidth*(squareSize + lineGap);\r
3717 \r
3718   SetTextAlign(hdc, TA_LEFT|TA_TOP);\r
3719   for (i = 0; i < BOARD_HEIGHT; i++) {\r
3720     str[0] = files[start + i];\r
3721     ExtTextOut(hdc, x + 2, y + 1, 0, NULL, str, 1, NULL);\r
3722     y += squareSize + lineGap;\r
3723   }\r
3724 \r
3725   start = flipView ? 12-(BOARD_RGHT-BOARD_LEFT) : 12;\r
3726 \r
3727   SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);\r
3728   for (i = 0; i < BOARD_RGHT - BOARD_LEFT; i++) {\r
3729     str[0] = ranks[start + i];\r
3730     ExtTextOut(hdc, x + squareSize - 2, y - 1, 0, NULL, str, 1, NULL);\r
3731     x += squareSize + lineGap;\r
3732   }    \r
3733 \r
3734   SelectObject(hdc, oldBrush);\r
3735   SetBkMode(hdc, oldMode);\r
3736   SetTextAlign(hdc, oldAlign);\r
3737   SelectObject(hdc, oldFont);\r
3738 }\r
3739 \r
3740 VOID\r
3741 DrawGridOnDC(HDC hdc)\r
3742 {\r
3743   HPEN oldPen;\r
3744  \r
3745   if (lineGap != 0) {\r
3746     oldPen = SelectObject(hdc, gridPen);\r
3747     PolyPolyline(hdc, gridEndpoints, gridVertexCounts, BOARD_WIDTH+BOARD_HEIGHT + 2);\r
3748     SelectObject(hdc, oldPen);\r
3749   }\r
3750 }\r
3751 \r
3752 #define HIGHLIGHT_PEN 0\r
3753 #define PREMOVE_PEN   1\r
3754 \r
3755 VOID\r
3756 DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)\r
3757 {\r
3758   int x1, y1;\r
3759   HPEN oldPen, hPen;\r
3760   if (lineGap == 0) return;\r
3761   if (flipView) {\r
3762     x1 = boardRect.left +\r
3763       lineGap/2 + ((BOARD_WIDTH-1)-x) * (squareSize + lineGap);\r
3764     y1 = boardRect.top +\r
3765       lineGap/2 + y * (squareSize + lineGap);\r
3766   } else {\r
3767     x1 = boardRect.left +\r
3768       lineGap/2 + x * (squareSize + lineGap);\r
3769     y1 = boardRect.top +\r
3770       lineGap/2 + ((BOARD_HEIGHT-1)-y) * (squareSize + lineGap);\r
3771   }\r
3772   hPen = pen ? premovePen : highlightPen;\r
3773   oldPen = SelectObject(hdc, on ? hPen : gridPen);\r
3774   MoveToEx(hdc, x1, y1, NULL);\r
3775   LineTo(hdc, x1 + squareSize + lineGap, y1);\r
3776   LineTo(hdc, x1 + squareSize + lineGap, y1 + squareSize + lineGap);\r
3777   LineTo(hdc, x1, y1 + squareSize + lineGap);\r
3778   LineTo(hdc, x1, y1);\r
3779   SelectObject(hdc, oldPen);\r
3780 }\r
3781 \r
3782 VOID\r
3783 DrawHighlightsOnDC(HDC hdc)\r
3784 {\r
3785   int i;\r
3786   for (i=0; i<2; i++) {\r
3787     if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
3788       DrawHighlightOnDC(hdc, TRUE,\r
3789                         highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
3790                         HIGHLIGHT_PEN);\r
3791   }\r
3792   for (i=0; i<2; i++) {\r
3793     if (premoveHighlightInfo.sq[i].x >= 0 && \r
3794         premoveHighlightInfo.sq[i].y >= 0) {\r
3795         DrawHighlightOnDC(hdc, TRUE,\r
3796                           premoveHighlightInfo.sq[i].x, \r
3797                           premoveHighlightInfo.sq[i].y,\r
3798                           PREMOVE_PEN);\r
3799     }\r
3800   }\r
3801 }\r
3802 \r
3803 /* Note: sqcolor is used only in monoMode */\r
3804 /* Note that this code is largely duplicated in woptions.c,\r
3805    function DrawSampleSquare, so that needs to be updated too */\r
3806 VOID\r
3807 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
3808 {\r
3809   HBITMAP oldBitmap;\r
3810   HBRUSH oldBrush;\r
3811   int tmpSize;\r
3812 \r
3813   if (appData.blindfold) return;\r
3814 \r
3815   /* [AS] Use font-based pieces if needed */\r
3816   if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
3817     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
3818     CreatePiecesFromFont();\r
3819 \r
3820     if( fontBitmapSquareSize == squareSize ) {\r
3821         int index = TranslatePieceToFontPiece(piece);\r
3822 \r
3823         SelectObject( tmphdc, hPieceMask[ index ] );\r
3824 \r
3825         BitBlt( hdc,\r
3826             x, y,\r
3827             squareSize, squareSize,\r
3828             tmphdc,\r
3829             0, 0,\r
3830             SRCAND );\r
3831 \r
3832         SelectObject( tmphdc, hPieceFace[ index ] );\r
3833 \r
3834         BitBlt( hdc,\r
3835             x, y,\r
3836             squareSize, squareSize,\r
3837             tmphdc,\r
3838             0, 0,\r
3839             SRCPAINT );\r
3840 \r
3841         return;\r
3842     }\r
3843   }\r
3844 \r
3845   if (appData.monoMode) {\r
3846     SelectObject(tmphdc, PieceBitmap(piece, \r
3847       color == sqcolor ? OUTLINE_PIECE : SOLID_PIECE));\r
3848     BitBlt(hdc, x, y, squareSize, squareSize, tmphdc, 0, 0,\r
3849            sqcolor ? SRCCOPY : NOTSRCCOPY);\r
3850   } else {\r
3851     tmpSize = squareSize;\r
3852     if(minorSize &&\r
3853         ((piece >= (int)WhiteNightrider && piece <= WhiteGrasshopper) ||\r
3854          (piece >= (int)BlackNightrider && piece <= BlackGrasshopper))  ) {\r
3855       /* [HGM] no bitmap available for promoted pieces in Crazyhouse        */\r
3856       /* Bitmaps of smaller size are substituted, but we have to align them */\r
3857       x += (squareSize - minorSize)>>1;\r
3858       y += squareSize - minorSize - 2;\r
3859       tmpSize = minorSize;\r
3860     }\r
3861     if (color || appData.allWhite ) {\r
3862       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
3863       if( color )\r
3864               oldBrush = SelectObject(hdc, whitePieceBrush);\r
3865       else    oldBrush = SelectObject(hdc, blackPieceBrush);\r
3866       if(appData.upsideDown && color==flipView)\r
3867         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3868       else\r
3869         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3870 #if 0\r
3871       /* Use black piece color for outline of white pieces */\r
3872       /* Not sure this looks really good (though xboard does it).\r
3873          Maybe better to have another selectable color, default black */\r
3874       SelectObject(hdc, blackPieceBrush); /* could have own brush */\r
3875       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3876       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3877 #else\r
3878       /* Use black for outline of white pieces */\r
3879       SelectObject(tmphdc, PieceBitmap(piece, OUTLINE_PIECE));\r
3880       if(appData.upsideDown && color==flipView)\r
3881         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, SRCAND);\r
3882       else\r
3883         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, SRCAND);\r
3884 #endif\r
3885     } else {\r
3886 #if 0\r
3887       /* Use white piece color for details of black pieces */\r
3888       /* Requires filled-in solid bitmaps (BLACK_PIECE class); the\r
3889          WHITE_PIECE ones aren't always the right shape. */\r
3890       /* Not sure this looks really good (though xboard does it).\r
3891          Maybe better to have another selectable color, default medium gray? */\r
3892       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, BLACK_PIECE));\r
3893       oldBrush = SelectObject(hdc, whitePieceBrush); /* could have own brush */\r
3894       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3895       SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3896       SelectObject(hdc, blackPieceBrush);\r
3897       BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3898 #else\r
3899       /* Use square color for details of black pieces */\r
3900       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, SOLID_PIECE));\r
3901       oldBrush = SelectObject(hdc, blackPieceBrush);\r
3902       if(appData.upsideDown && !flipView)\r
3903         StretchBlt(hdc, x+tmpSize, y+tmpSize, -tmpSize, -tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, 0x00B8074A);\r
3904       else\r
3905         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
3906 #endif\r
3907     }\r
3908     SelectObject(hdc, oldBrush);\r
3909     SelectObject(tmphdc, oldBitmap);\r
3910   }\r
3911 }\r
3912 \r
3913 /* [AS] Compute a drawing mode for a square, based on specified settings (see DrawTile) */\r
3914 int GetBackTextureMode( int algo )\r
3915 {\r
3916     int result = BACK_TEXTURE_MODE_DISABLED;\r
3917 \r
3918     switch( algo ) \r
3919     {\r
3920         case BACK_TEXTURE_MODE_PLAIN:\r
3921             result = 1; /* Always use identity map */\r
3922             break;\r
3923         case BACK_TEXTURE_MODE_FULL_RANDOM:\r
3924             result = 1 + (myrandom() % 3); /* Pick a transformation at random */\r
3925             break;\r
3926     }\r
3927 \r
3928     return result;\r
3929 }\r
3930 \r
3931 /* \r
3932     [AS] Compute and save texture drawing info, otherwise we may not be able\r
3933     to handle redraws cleanly (as random numbers would always be different).\r
3934 */\r
3935 VOID RebuildTextureSquareInfo()\r
3936 {\r
3937     BITMAP bi;\r
3938     int lite_w = 0;\r
3939     int lite_h = 0;\r
3940     int dark_w = 0;\r
3941     int dark_h = 0;\r
3942     int row;\r
3943     int col;\r
3944 \r
3945     ZeroMemory( &backTextureSquareInfo, sizeof(backTextureSquareInfo) );\r
3946 \r
3947     if( liteBackTexture != NULL ) {\r
3948         if( GetObject( liteBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3949             lite_w = bi.bmWidth;\r
3950             lite_h = bi.bmHeight;\r
3951         }\r
3952     }\r
3953 \r
3954     if( darkBackTexture != NULL ) {\r
3955         if( GetObject( darkBackTexture, sizeof(bi), &bi ) > 0 ) {\r
3956             dark_w = bi.bmWidth;\r
3957             dark_h = bi.bmHeight;\r
3958         }\r
3959     }\r
3960 \r
3961     for( row=0; row<BOARD_HEIGHT; row++ ) {\r
3962         for( col=0; col<BOARD_WIDTH; col++ ) {\r
3963             if( (col + row) & 1 ) {\r
3964                 /* Lite square */\r
3965                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
3966                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
3967                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
3968                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
3969                 }\r
3970             }\r
3971             else {\r
3972                 /* Dark square */\r
3973                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
3974                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
3975                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
3976                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
3977                 }\r
3978             }\r
3979         }\r
3980     }\r
3981 }\r
3982 \r
3983 /* [AS] Arrow highlighting support */\r
3984 \r
3985 static int A_WIDTH = 5; /* Width of arrow body */\r
3986 \r
3987 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */\r
3988 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */\r
3989 \r
3990 static double Sqr( double x )\r
3991 {\r
3992     return x*x;\r
3993 }\r
3994 \r
3995 static int Round( double x )\r
3996 {\r
3997     return (int) (x + 0.5);\r
3998 }\r
3999 \r
4000 /* Draw an arrow between two points using current settings */\r
4001 VOID DrawArrowBetweenPoints( HDC hdc, int s_x, int s_y, int d_x, int d_y )\r
4002 {\r
4003     POINT arrow[7];\r
4004     double dx, dy, j, k, x, y;\r
4005 \r
4006     if( d_x == s_x ) {\r
4007         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4008 \r
4009         arrow[0].x = s_x + A_WIDTH;\r
4010         arrow[0].y = s_y;\r
4011 \r
4012         arrow[1].x = s_x + A_WIDTH;\r
4013         arrow[1].y = d_y - h;\r
4014 \r
4015         arrow[2].x = s_x + A_WIDTH*A_WIDTH_FACTOR;\r
4016         arrow[2].y = d_y - h;\r
4017 \r
4018         arrow[3].x = d_x;\r
4019         arrow[3].y = d_y;\r
4020 \r
4021         arrow[4].x = s_x - A_WIDTH*A_WIDTH_FACTOR;\r
4022         arrow[4].y = d_y - h;\r
4023 \r
4024         arrow[5].x = s_x - A_WIDTH;\r
4025         arrow[5].y = d_y - h;\r
4026 \r
4027         arrow[6].x = s_x - A_WIDTH;\r
4028         arrow[6].y = s_y;\r
4029     }\r
4030     else if( d_y == s_y ) {\r
4031         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;\r
4032 \r
4033         arrow[0].x = s_x;\r
4034         arrow[0].y = s_y + A_WIDTH;\r
4035 \r
4036         arrow[1].x = d_x - w;\r
4037         arrow[1].y = s_y + A_WIDTH;\r
4038 \r
4039         arrow[2].x = d_x - w;\r
4040         arrow[2].y = s_y + A_WIDTH*A_WIDTH_FACTOR;\r
4041 \r
4042         arrow[3].x = d_x;\r
4043         arrow[3].y = d_y;\r
4044 \r
4045         arrow[4].x = d_x - w;\r
4046         arrow[4].y = s_y - A_WIDTH*A_WIDTH_FACTOR;\r
4047 \r
4048         arrow[5].x = d_x - w;\r
4049         arrow[5].y = s_y - A_WIDTH;\r
4050 \r
4051         arrow[6].x = s_x;\r
4052         arrow[6].y = s_y - A_WIDTH;\r
4053     }\r
4054     else {\r
4055         /* [AS] Needed a lot of paper for this! :-) */\r
4056         dy = (double) (d_y - s_y) / (double) (d_x - s_x);\r
4057         dx = (double) (s_x - d_x) / (double) (s_y - d_y);\r
4058   \r
4059         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );\r
4060 \r
4061         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );\r
4062 \r
4063         x = s_x;\r
4064         y = s_y;\r
4065 \r
4066         arrow[0].x = Round(x - j);\r
4067         arrow[0].y = Round(y + j*dx);\r
4068 \r
4069         arrow[1].x = Round(x + j);\r
4070         arrow[1].y = Round(y - j*dx);\r
4071 \r
4072         if( d_x > s_x ) {\r
4073             x = (double) d_x - k;\r
4074             y = (double) d_y - k*dy;\r
4075         }\r
4076         else {\r
4077             x = (double) d_x + k;\r
4078             y = (double) d_y + k*dy;\r
4079         }\r
4080 \r
4081         arrow[2].x = Round(x + j);\r
4082         arrow[2].y = Round(y - j*dx);\r
4083 \r
4084         arrow[3].x = Round(x + j*A_WIDTH_FACTOR);\r
4085         arrow[3].y = Round(y - j*A_WIDTH_FACTOR*dx);\r
4086 \r
4087         arrow[4].x = d_x;\r
4088         arrow[4].y = d_y;\r
4089 \r
4090         arrow[5].x = Round(x - j*A_WIDTH_FACTOR);\r
4091         arrow[5].y = Round(y + j*A_WIDTH_FACTOR*dx);\r
4092 \r
4093         arrow[6].x = Round(x - j);\r
4094         arrow[6].y = Round(y + j*dx);\r
4095     }\r
4096 \r
4097     Polygon( hdc, arrow, 7 );\r
4098 }\r
4099 \r
4100 /* [AS] Draw an arrow between two squares */\r
4101 VOID DrawArrowBetweenSquares( HDC hdc, int s_col, int s_row, int d_col, int d_row )\r
4102 {\r
4103     int s_x, s_y, d_x, d_y;\r
4104     HPEN hpen;\r
4105     HPEN holdpen;\r
4106     HBRUSH hbrush;\r
4107     HBRUSH holdbrush;\r
4108     LOGBRUSH stLB;\r
4109 \r
4110     if( s_col == d_col && s_row == d_row ) {\r
4111         return;\r
4112     }\r
4113 \r
4114     /* Get source and destination points */\r
4115     SquareToPos( s_row, s_col, &s_x, &s_y);\r
4116     SquareToPos( d_row, d_col, &d_x, &d_y);\r
4117 \r
4118     if( d_y > s_y ) {\r
4119         d_y += squareSize / 4;\r
4120     }\r
4121     else if( d_y < s_y ) {\r
4122         d_y += 3 * squareSize / 4;\r
4123     }\r
4124     else {\r
4125         d_y += squareSize / 2;\r
4126     }\r
4127 \r
4128     if( d_x > s_x ) {\r
4129         d_x += squareSize / 4;\r
4130     }\r
4131     else if( d_x < s_x ) {\r
4132         d_x += 3 * squareSize / 4;\r
4133     }\r
4134     else {\r
4135         d_x += squareSize / 2;\r
4136     }\r
4137 \r
4138     s_x += squareSize / 2;\r
4139     s_y += squareSize / 2;\r
4140 \r
4141     /* Adjust width */\r
4142     A_WIDTH = squareSize / 14;\r
4143 \r
4144     /* Draw */\r
4145     stLB.lbStyle = BS_SOLID;\r
4146     stLB.lbColor = appData.highlightArrowColor;\r
4147     stLB.lbHatch = 0;\r
4148 \r
4149     hpen = CreatePen( PS_SOLID, 2, RGB(0x00,0x00,0x00) );\r
4150     holdpen = SelectObject( hdc, hpen );\r
4151     hbrush = CreateBrushIndirect( &stLB );\r
4152     holdbrush = SelectObject( hdc, hbrush );\r
4153 \r
4154     DrawArrowBetweenPoints( hdc, s_x, s_y, d_x, d_y );\r
4155 \r
4156     SelectObject( hdc, holdpen );\r
4157     SelectObject( hdc, holdbrush );\r
4158     DeleteObject( hpen );\r
4159     DeleteObject( hbrush );\r
4160 }\r
4161 \r
4162 BOOL HasHighlightInfo()\r
4163 {\r
4164     BOOL result = FALSE;\r
4165 \r
4166     if( highlightInfo.sq[0].x >= 0 && highlightInfo.sq[0].y >= 0 &&\r
4167         highlightInfo.sq[1].x >= 0 && highlightInfo.sq[1].y >= 0 )\r
4168     {\r
4169         result = TRUE;\r
4170     }\r
4171 \r
4172     return result;\r
4173 }\r
4174 \r
4175 BOOL IsDrawArrowEnabled()\r
4176 {\r
4177     BOOL result = FALSE;\r
4178 \r
4179     if( appData.highlightMoveWithArrow && squareSize >= 32 ) {\r
4180         result = TRUE;\r
4181     }\r
4182 \r
4183     return result;\r
4184 }\r
4185 \r
4186 VOID DrawArrowHighlight( HDC hdc )\r
4187 {\r
4188     if( IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4189         DrawArrowBetweenSquares( hdc,\r
4190             highlightInfo.sq[0].x, highlightInfo.sq[0].y,\r
4191             highlightInfo.sq[1].x, highlightInfo.sq[1].y );\r
4192     }\r
4193 }\r
4194 \r
4195 HRGN GetArrowHighlightClipRegion( HDC hdc )\r
4196 {\r
4197     HRGN result = NULL;\r
4198 \r
4199     if( HasHighlightInfo() ) {\r
4200         int x1, y1, x2, y2;\r
4201         int sx, sy, dx, dy;\r
4202 \r
4203         SquareToPos(highlightInfo.sq[0].y, highlightInfo.sq[0].x, &x1, &y1 );\r
4204         SquareToPos(highlightInfo.sq[1].y, highlightInfo.sq[1].x, &x2, &y2 );\r
4205 \r
4206         sx = MIN( x1, x2 );\r
4207         sy = MIN( y1, y2 );\r
4208         dx = MAX( x1, x2 ) + squareSize;\r
4209         dy = MAX( y1, y2 ) + squareSize;\r
4210 \r
4211         result = CreateRectRgn( sx, sy, dx, dy );\r
4212     }\r
4213 \r
4214     return result;\r
4215 }\r
4216 \r
4217 /*\r
4218     Warning: this function modifies the behavior of several other functions. \r
4219     \r
4220     Basically, Winboard is optimized to avoid drawing the whole board if not strictly\r
4221     needed. Unfortunately, the decision whether or not to perform a full or partial\r
4222     repaint is scattered all over the place, which is not good for features such as\r
4223     "arrow highlighting" that require a full repaint of the board.\r
4224 \r
4225     So, I've tried to patch the code where I thought it made sense (e.g. after or during\r
4226     user interaction, when speed is not so important) but especially to avoid errors\r
4227     in the displayed graphics.\r
4228 \r
4229     In such patched places, I always try refer to this function so there is a single\r
4230     place to maintain knowledge.\r
4231     \r
4232     To restore the original behavior, just return FALSE unconditionally.\r
4233 */\r
4234 BOOL IsFullRepaintPreferrable()\r
4235 {\r
4236     BOOL result = FALSE;\r
4237 \r
4238     if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() ) {\r
4239         /* Arrow may appear on the board */\r
4240         result = TRUE;\r
4241     }\r
4242 \r
4243     return result;\r
4244 }\r
4245 \r
4246 /* \r
4247     This function is called by DrawPosition to know whether a full repaint must\r
4248     be forced or not.\r
4249 \r
4250     Only DrawPosition may directly call this function, which makes use of \r
4251     some state information. Other function should call DrawPosition specifying \r
4252     the repaint flag, and can use IsFullRepaintPreferrable if needed.\r
4253 */\r
4254 BOOL DrawPositionNeedsFullRepaint()\r
4255 {\r
4256     BOOL result = FALSE;\r
4257 \r
4258     /* \r
4259         Probably a slightly better policy would be to trigger a full repaint\r
4260         when animInfo.piece changes state (i.e. empty -> non-empty and viceversa),\r
4261         but animation is fast enough that it's difficult to notice.\r
4262     */\r
4263     if( animInfo.piece == EmptySquare ) {\r
4264         if( (appData.highlightLastMove || appData.highlightDragging) && IsDrawArrowEnabled() && HasHighlightInfo() ) {\r
4265             result = TRUE;\r
4266         }\r
4267     }\r
4268 \r
4269     return result;\r
4270 }\r
4271 \r
4272 VOID\r
4273 DrawBoardOnDC(HDC hdc, Board board, HDC tmphdc)\r
4274 {\r
4275   int row, column, x, y, square_color, piece_color;\r
4276   ChessSquare piece;\r
4277   HBRUSH oldBrush;\r
4278   HDC texture_hdc = NULL;\r
4279 \r
4280   /* [AS] Initialize background textures if needed */\r
4281   if( liteBackTexture != NULL || darkBackTexture != NULL ) {\r
4282       static int backTextureBoardSize; /* [HGM] boardsize: also new texture if board format changed */\r
4283       if( backTextureSquareSize != squareSize \r
4284        || backTextureBoardSize != BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT) {\r
4285           backTextureBoardSize = BOARD_WIDTH+BOARD_SIZE*BOARD_HEIGHT;\r
4286           backTextureSquareSize = squareSize;\r
4287           RebuildTextureSquareInfo();\r
4288       }\r
4289 \r
4290       texture_hdc = CreateCompatibleDC( hdc );\r
4291   }\r
4292 \r
4293   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4294     for (column = 0; column < BOARD_WIDTH; column++) {\r
4295   \r
4296       SquareToPos(row, column, &x, &y);\r
4297 \r
4298       piece = board[row][column];\r
4299 \r
4300       square_color = ((column + row) % 2) == 1;\r
4301       if( gameInfo.variant == VariantXiangqi ) {\r
4302           square_color = !InPalace(row, column);\r
4303           if(BOARD_HEIGHT&1) { if(row==BOARD_HEIGHT/2) square_color ^= 1; }\r
4304           else if(row < BOARD_HEIGHT/2) square_color ^= 1;\r
4305       }\r
4306       piece_color = (int) piece < (int) BlackPawn;\r
4307 \r
4308 \r
4309       /* [HGM] holdings file: light square or black */\r
4310       if(column == BOARD_LEFT-2) {\r
4311             if( row > BOARD_HEIGHT - gameInfo.holdingsSize - 1 )\r
4312                 square_color = 1;\r
4313             else {\r
4314                 DisplayHoldingsCount(hdc, x, y, 0, 0); /* black out */\r
4315                 continue;\r
4316             }\r
4317       } else\r
4318       if(column == BOARD_RGHT + 1 ) {\r
4319             if( row < gameInfo.holdingsSize )\r
4320                 square_color = 1;\r
4321             else {\r
4322                 DisplayHoldingsCount(hdc, x, y, 0, 0); \r
4323                 continue;\r
4324             }\r
4325       }\r
4326       if(column == BOARD_LEFT-1 ) /* left align */\r
4327             DisplayHoldingsCount(hdc, x, y, flipView, (int) board[row][column]);\r
4328       else if( column == BOARD_RGHT) /* right align */\r
4329             DisplayHoldingsCount(hdc, x, y, !flipView, (int) board[row][column]);\r
4330       else\r
4331       if (appData.monoMode) {\r
4332         if (piece == EmptySquare) {\r
4333           BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0,\r
4334                  square_color ? WHITENESS : BLACKNESS);\r
4335         } else {\r
4336           DrawPieceOnDC(hdc, piece, piece_color, square_color, x, y, tmphdc);\r
4337         }\r
4338       } \r
4339       else if( backTextureSquareInfo[row][column].mode > 0 ) {\r
4340           /* [AS] Draw the square using a texture bitmap */\r
4341           HBITMAP hbm = SelectObject( texture_hdc, square_color ? liteBackTexture : darkBackTexture );\r
4342           int r = row, c = column; // [HGM] do not flip board in flipView\r
4343           if(flipView) { r = BOARD_HEIGHT-1 - r; c = BOARD_WIDTH-1 - c; }\r
4344 \r
4345           DrawTile( x, y, \r
4346               squareSize, squareSize, \r
4347               hdc, \r
4348               texture_hdc,\r
4349               backTextureSquareInfo[r][c].mode,\r
4350               backTextureSquareInfo[r][c].x,\r
4351               backTextureSquareInfo[r][c].y );\r
4352 \r
4353           SelectObject( texture_hdc, hbm );\r
4354 \r
4355           if (piece != EmptySquare) {\r
4356               DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4357           }\r
4358       }\r
4359       else {\r
4360         HBRUSH brush = square_color ? lightSquareBrush : darkSquareBrush;\r
4361 \r
4362         oldBrush = SelectObject(hdc, brush );\r
4363         BitBlt(hdc, x, y, squareSize, squareSize, 0, 0, 0, PATCOPY);\r
4364         SelectObject(hdc, oldBrush);\r
4365         if (piece != EmptySquare)\r
4366           DrawPieceOnDC(hdc, piece, piece_color, -1, x, y, tmphdc);\r
4367       }\r
4368     }\r
4369   }\r
4370 \r
4371   if( texture_hdc != NULL ) {\r
4372     DeleteDC( texture_hdc );\r
4373   }\r
4374 }\r
4375 \r
4376 int saveDiagFlag = 0; FILE *diagFile; // [HGM] diag\r
4377 void fputDW(FILE *f, int x)\r
4378 {\r
4379         fputc(x     & 255, f);\r
4380         fputc(x>>8  & 255, f);\r
4381         fputc(x>>16 & 255, f);\r
4382         fputc(x>>24 & 255, f);\r
4383 }\r
4384 \r
4385 #define MAX_CLIPS 200   /* more than enough */\r
4386 \r
4387 VOID\r
4388 DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)\r
4389 {\r
4390 //  HBITMAP bufferBitmap;\r
4391   BITMAP bi;\r
4392 //  RECT Rect;\r
4393   HDC tmphdc;\r
4394   HBITMAP hbm;\r
4395   int w = 100, h = 50;\r
4396 \r
4397   if(logo == NULL) return;\r
4398 //  GetClientRect(hwndMain, &Rect);\r
4399 //  bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4400 //                                      Rect.bottom-Rect.top+1);\r
4401   tmphdc = CreateCompatibleDC(hdc);\r
4402   hbm = SelectObject(tmphdc, logo);\r
4403   if( GetObject( logo, sizeof(bi), &bi ) > 0 ) {\r
4404             w = bi.bmWidth;\r
4405             h = bi.bmHeight;\r
4406   }\r
4407   StretchBlt(hdc, logoRect.left, logoRect.top, logoRect.right - logoRect.left, \r
4408                   logoRect.bottom - logoRect.top, tmphdc, 0, 0, w, h, SRCCOPY);\r
4409   SelectObject(tmphdc, hbm);\r
4410   DeleteDC(tmphdc);\r
4411 }\r
4412 \r
4413 VOID\r
4414 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
4415 {\r
4416   static Board lastReq, lastDrawn;\r
4417   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
4418   static int lastDrawnFlipView = 0;\r
4419   static int lastReqValid = 0, lastDrawnValid = 0;\r
4420   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
4421   HDC tmphdc;\r
4422   HDC hdcmem;\r
4423   HBITMAP bufferBitmap;\r
4424   HBITMAP oldBitmap;\r
4425   RECT Rect;\r
4426   HRGN clips[MAX_CLIPS];\r
4427   ChessSquare dragged_piece = EmptySquare;\r
4428 \r
4429   /* I'm undecided on this - this function figures out whether a full\r
4430    * repaint is necessary on its own, so there's no real reason to have the\r
4431    * caller tell it that.  I think this can safely be set to FALSE - but\r
4432    * if we trust the callers not to request full repaints unnessesarily, then\r
4433    * we could skip some clipping work.  In other words, only request a full\r
4434    * redraw when the majority of pieces have changed positions (ie. flip, \r
4435    * gamestart and similar)  --Hawk\r
4436    */\r
4437   Boolean fullrepaint = repaint;\r
4438 \r
4439   if( DrawPositionNeedsFullRepaint() ) {\r
4440       fullrepaint = TRUE;\r
4441   }\r
4442 \r
4443 #if 0\r
4444   if( fullrepaint ) {\r
4445       static int repaint_count = 0;\r
4446       char buf[128];\r
4447 \r
4448       repaint_count++;\r
4449       sprintf( buf, "FULL repaint: %d\n", repaint_count );\r
4450       OutputDebugString( buf );\r
4451   }\r
4452 #endif\r
4453 \r
4454   if (board == NULL) {\r
4455     if (!lastReqValid) {\r
4456       return;\r
4457     }\r
4458     board = lastReq;\r
4459   } else {\r
4460     CopyBoard(lastReq, board);\r
4461     lastReqValid = 1;\r
4462   }\r
4463 \r
4464   if (doingSizing) {\r
4465     return;\r
4466   }\r
4467 \r
4468   if (IsIconic(hwndMain)) {\r
4469     return;\r
4470   }\r
4471 \r
4472   if (hdc == NULL) {\r
4473     hdc = GetDC(hwndMain);\r
4474     if (!appData.monoMode) {\r
4475       SelectPalette(hdc, hPal, FALSE);\r
4476       RealizePalette(hdc);\r
4477     }\r
4478     releaseDC = TRUE;\r
4479   } else {\r
4480     releaseDC = FALSE;\r
4481   }\r
4482 \r
4483 #if 0\r
4484   fprintf(debugFP, "*******************************\n"\r
4485                    "repaint = %s\n"\r
4486                    "dragInfo.from (%d,%d)\n"\r
4487                    "dragInfo.start (%d,%d)\n"\r
4488                    "dragInfo.pos (%d,%d)\n"\r
4489                    "dragInfo.lastpos (%d,%d)\n", \r
4490                     repaint ? "TRUE" : "FALSE",\r
4491                     dragInfo.from.x, dragInfo.from.y, \r
4492                     dragInfo.start.x, dragInfo.start.y,\r
4493                     dragInfo.pos.x, dragInfo.pos.y,\r
4494                     dragInfo.lastpos.x, dragInfo.lastpos.y);\r
4495   fprintf(debugFP, "prev:  ");\r
4496   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4497     for (column = 0; column < BOARD_WIDTH; column++) {\r
4498       fprintf(debugFP, "%d ", lastDrawn[row][column]);\r
4499     }\r
4500   }\r
4501   fprintf(debugFP, "\n");\r
4502   fprintf(debugFP, "board: ");\r
4503   for (row = 0; row < BOARD_HEIGHT; row++) {\r
4504     for (column = 0; column < BOARD_WIDTH; column++) {\r
4505       fprintf(debugFP, "%d ", board[row][column]);\r
4506     }\r
4507   }\r
4508   fprintf(debugFP, "\n");\r
4509   fflush(debugFP);\r
4510 #endif\r
4511 \r
4512   /* Create some work-DCs */\r
4513   hdcmem = CreateCompatibleDC(hdc);\r
4514   tmphdc = CreateCompatibleDC(hdc);\r
4515 \r
4516   /* If dragging is in progress, we temporarely remove the piece */\r
4517   /* [HGM] or temporarily decrease count if stacked              */\r
4518   /*       !! Moved to before board compare !!                   */\r
4519   if (dragInfo.from.x >= 0 && dragInfo.pos.x >= 0) {\r
4520     dragged_piece = board[dragInfo.from.y][dragInfo.from.x];\r
4521     if(dragInfo.from.x == BOARD_LEFT-2 ) {\r
4522             if(--board[dragInfo.from.y][dragInfo.from.x+1] == 0 )\r
4523         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4524     } else \r
4525     if(dragInfo.from.x == BOARD_RGHT+1) {\r
4526             if(--board[dragInfo.from.y][dragInfo.from.x-1] == 0 )\r
4527         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4528     } else \r
4529         board[dragInfo.from.y][dragInfo.from.x] = EmptySquare;\r
4530   }\r
4531 \r
4532   /* Figure out which squares need updating by comparing the \r
4533    * newest board with the last drawn board and checking if\r
4534    * flipping has changed.\r
4535    */\r
4536   if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
4537     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
4538       for (column = 0; column < BOARD_WIDTH; column++) {\r
4539         if (lastDrawn[row][column] != board[row][column]) {\r
4540           SquareToPos(row, column, &x, &y);\r
4541           clips[num_clips++] =\r
4542             CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
4543         }\r
4544       }\r
4545     }\r
4546     for (i=0; i<2; i++) {\r
4547       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
4548           lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
4549         if (lastDrawnHighlight.sq[i].x >= 0 &&\r
4550             lastDrawnHighlight.sq[i].y >= 0) {\r
4551           SquareToPos(lastDrawnHighlight.sq[i].y,\r
4552                       lastDrawnHighlight.sq[i].x, &x, &y);\r
4553           clips[num_clips++] =\r
4554             CreateRectRgn(x - lineGap, y - lineGap, \r
4555                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4556         }\r
4557         if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) {\r
4558           SquareToPos(highlightInfo.sq[i].y, highlightInfo.sq[i].x, &x, &y);\r
4559           clips[num_clips++] =\r
4560             CreateRectRgn(x - lineGap, y - lineGap, \r
4561                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4562         }\r
4563       }\r
4564     }\r
4565     for (i=0; i<2; i++) {\r
4566       if (lastDrawnPremove.sq[i].x != premoveHighlightInfo.sq[i].x ||\r
4567           lastDrawnPremove.sq[i].y != premoveHighlightInfo.sq[i].y) {\r
4568         if (lastDrawnPremove.sq[i].x >= 0 &&\r
4569             lastDrawnPremove.sq[i].y >= 0) {\r
4570           SquareToPos(lastDrawnPremove.sq[i].y,\r
4571                       lastDrawnPremove.sq[i].x, &x, &y);\r
4572           clips[num_clips++] =\r
4573             CreateRectRgn(x - lineGap, y - lineGap, \r
4574                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4575         }\r
4576         if (premoveHighlightInfo.sq[i].x >= 0 && \r
4577             premoveHighlightInfo.sq[i].y >= 0) {\r
4578           SquareToPos(premoveHighlightInfo.sq[i].y, \r
4579                       premoveHighlightInfo.sq[i].x, &x, &y);\r
4580           clips[num_clips++] =\r
4581             CreateRectRgn(x - lineGap, y - lineGap, \r
4582                           x + squareSize + lineGap, y + squareSize + lineGap);\r
4583         }\r
4584       }\r
4585     }\r
4586   } else {\r
4587     fullrepaint = TRUE;\r
4588   }\r
4589 \r
4590   /* Create a buffer bitmap - this is the actual bitmap\r
4591    * being written to.  When all the work is done, we can\r
4592    * copy it to the real DC (the screen).  This avoids\r
4593    * the problems with flickering.\r
4594    */\r
4595   GetClientRect(hwndMain, &Rect);\r
4596   bufferBitmap = CreateCompatibleBitmap(hdc, Rect.right-Rect.left+1,\r
4597                                         Rect.bottom-Rect.top+1);\r
4598   oldBitmap = SelectObject(hdcmem, bufferBitmap);\r
4599   if (!appData.monoMode) {\r
4600     SelectPalette(hdcmem, hPal, FALSE);\r
4601   }\r
4602 \r
4603   /* Create clips for dragging */\r
4604   if (!fullrepaint) {\r
4605     if (dragInfo.from.x >= 0) {\r
4606       SquareToPos(dragInfo.from.y, dragInfo.from.x, &x, &y);\r
4607       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4608     }\r
4609     if (dragInfo.start.x >= 0) {\r
4610       SquareToPos(dragInfo.start.y, dragInfo.start.x, &x, &y);\r
4611       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4612     }\r
4613     if (dragInfo.pos.x >= 0) {\r
4614       x = dragInfo.pos.x - squareSize / 2;\r
4615       y = dragInfo.pos.y - squareSize / 2;\r
4616       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4617     }\r
4618     if (dragInfo.lastpos.x >= 0) {\r
4619       x = dragInfo.lastpos.x - squareSize / 2;\r
4620       y = dragInfo.lastpos.y - squareSize / 2;\r
4621       clips[num_clips++] = CreateRectRgn(x, y, x+squareSize, y+squareSize);\r
4622     }\r
4623   }\r
4624 \r
4625   /* Are we animating a move?  \r
4626    * If so, \r
4627    *   - remove the piece from the board (temporarely)\r
4628    *   - calculate the clipping region\r
4629    */\r
4630   if (!fullrepaint) {\r
4631     if (animInfo.piece != EmptySquare) {\r
4632       board[animInfo.from.y][animInfo.from.x] = EmptySquare;\r
4633       x = boardRect.left + animInfo.lastpos.x;\r
4634       y = boardRect.top + animInfo.lastpos.y;\r
4635       x2 = boardRect.left + animInfo.pos.x;\r
4636       y2 = boardRect.top + animInfo.pos.y;\r
4637       clips[num_clips++] = CreateRectRgn(MIN(x,x2), MIN(y,y2), MAX(x,x2)+squareSize, MAX(y,y2)+squareSize);\r
4638       /* Slight kludge.  The real problem is that after AnimateMove is\r
4639          done, the position on the screen does not match lastDrawn.\r
4640          This currently causes trouble only on e.p. captures in\r
4641          atomic, where the piece moves to an empty square and then\r
4642          explodes.  The old and new positions both had an empty square\r
4643          at the destination, but animation has drawn a piece there and\r
4644          we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
4645       lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
4646     }\r
4647   }\r
4648 \r
4649   /* No clips?  Make sure we have fullrepaint set to TRUE */\r
4650   if (num_clips == 0)\r
4651     fullrepaint = TRUE;\r
4652 \r
4653   /* Set clipping on the memory DC */\r
4654   if (!fullrepaint) {\r
4655     SelectClipRgn(hdcmem, clips[0]);\r
4656     for (x = 1; x < num_clips; x++) {\r
4657       if (ExtSelectClipRgn(hdcmem, clips[x], RGN_OR) == ERROR)\r
4658         abort();  // this should never ever happen!\r
4659     }\r
4660   }\r
4661 \r
4662   /* Do all the drawing to the memory DC */\r
4663   if(explodeInfo.radius) { // [HGM] atomic\r
4664         HBRUSH oldBrush;\r
4665         int x, y, r=(explodeInfo.radius * squareSize)/100;\r
4666         board[explodeInfo.fromY][explodeInfo.fromX] = EmptySquare; // suppress display of capturer\r
4667         SquareToPos(explodeInfo.toY, explodeInfo.toX, &x, &y);\r
4668         x += squareSize/2;\r
4669         y += squareSize/2;\r
4670         if(!fullrepaint) {\r
4671           clips[num_clips] = CreateRectRgn(x-r, y-r, x+r, y+r);\r
4672           ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
4673         }\r
4674         DrawGridOnDC(hdcmem);\r
4675         DrawHighlightsOnDC(hdcmem);\r
4676         DrawBoardOnDC(hdcmem, board, tmphdc);\r
4677         oldBrush = SelectObject(hdcmem, explodeBrush);\r
4678         Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
4679         SelectObject(hdcmem, oldBrush);\r
4680   } else {\r
4681     DrawGridOnDC(hdcmem);\r
4682     DrawHighlightsOnDC(hdcmem);\r
4683     DrawBoardOnDC(hdcmem, board, tmphdc);\r
4684   }\r
4685   if(logoHeight) {\r
4686         HBITMAP whiteLogo = (HBITMAP) first.programLogo, blackLogo = (HBITMAP) second.programLogo;\r
4687         if(appData.autoLogo) {\r
4688           \r
4689           switch(gameMode) { // pick logos based on game mode\r
4690             case IcsObserving:\r
4691                 whiteLogo = second.programLogo; // ICS logo\r
4692                 blackLogo = second.programLogo;\r
4693             default:\r
4694                 break;\r
4695             case IcsPlayingWhite:\r
4696                 if(!appData.zippyPlay) whiteLogo = userLogo;\r
4697                 blackLogo = second.programLogo; // ICS logo\r
4698                 break;\r
4699             case IcsPlayingBlack:\r
4700                 whiteLogo = second.programLogo; // ICS logo\r
4701                 blackLogo = appData.zippyPlay ? first.programLogo : userLogo;\r
4702                 break;\r
4703             case TwoMachinesPlay:\r
4704                 if(first.twoMachinesColor[0] == 'b') {\r
4705                     whiteLogo = second.programLogo;\r
4706                     blackLogo = first.programLogo;\r
4707                 }\r
4708                 break;\r
4709             case MachinePlaysWhite:\r
4710                 blackLogo = userLogo;\r
4711                 break;\r
4712             case MachinePlaysBlack:\r
4713                 whiteLogo = userLogo;\r
4714                 blackLogo = first.programLogo;\r
4715           }\r
4716         }\r
4717         DrawLogoOnDC(hdc, leftLogoRect, flipClock ? blackLogo : whiteLogo);\r
4718         DrawLogoOnDC(hdc, rightLogoRect, flipClock ? whiteLogo : blackLogo);\r
4719   }\r
4720 \r
4721   if( appData.highlightMoveWithArrow ) {\r
4722     DrawArrowHighlight(hdcmem);\r
4723   }\r
4724 \r
4725   DrawCoordsOnDC(hdcmem);\r
4726 \r
4727   CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
4728                  /* to make sure lastDrawn contains what is actually drawn */\r
4729 \r
4730   /* Put the dragged piece back into place and draw it (out of place!) */\r
4731     if (dragged_piece != EmptySquare) {\r
4732     /* [HGM] or restack */\r
4733     if(dragInfo.from.x == BOARD_LEFT-2 )\r
4734                  board[dragInfo.from.y][dragInfo.from.x+1]++;\r
4735     else\r
4736     if(dragInfo.from.x == BOARD_RGHT+1 )\r
4737                  board[dragInfo.from.y][dragInfo.from.x-1]++;\r
4738     board[dragInfo.from.y][dragInfo.from.x] = dragged_piece;\r
4739     x = dragInfo.pos.x - squareSize / 2;\r
4740     y = dragInfo.pos.y - squareSize / 2;\r
4741     DrawPieceOnDC(hdcmem, dragged_piece,\r
4742                   ((int) dragged_piece < (int) BlackPawn), \r
4743                   (dragInfo.from.y + dragInfo.from.x) % 2, x, y, tmphdc);\r
4744   }   \r
4745   \r
4746   /* Put the animated piece back into place and draw it */\r
4747   if (animInfo.piece != EmptySquare) {\r
4748     board[animInfo.from.y][animInfo.from.x]  = animInfo.piece;\r
4749     x = boardRect.left + animInfo.pos.x;\r
4750     y = boardRect.top + animInfo.pos.y;\r
4751     DrawPieceOnDC(hdcmem, animInfo.piece,\r
4752                   ((int) animInfo.piece < (int) BlackPawn),\r
4753                   (animInfo.from.y + animInfo.from.x) % 2, x, y, tmphdc);\r
4754   }\r
4755 \r
4756   /* Release the bufferBitmap by selecting in the old bitmap \r
4757    * and delete the memory DC\r
4758    */\r
4759   SelectObject(hdcmem, oldBitmap);\r
4760   DeleteDC(hdcmem);\r
4761 \r
4762   /* Set clipping on the target DC */\r
4763   if (!fullrepaint) {\r
4764     SelectClipRgn(hdc, clips[0]);\r
4765     for (x = 1; x < num_clips; x++) {\r
4766       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
4767         abort();   // this should never ever happen!\r
4768     } \r
4769   }\r
4770 \r
4771   /* Copy the new bitmap onto the screen in one go.\r
4772    * This way we avoid any flickering\r
4773    */\r
4774   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
4775   BitBlt(hdc, boardRect.left, boardRect.top,\r
4776          boardRect.right - boardRect.left,\r
4777          boardRect.bottom - boardRect.top,\r
4778          tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
4779   if(saveDiagFlag) { \r
4780     BITMAP b; int i, j=0, m, w, wb, fac=0; char pData[1000000]; \r
4781     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
4782 \r
4783     GetObject(bufferBitmap, sizeof(b), &b);\r
4784     if(b.bmWidthBytes*b.bmHeight <= 990000) {\r
4785         bih.biSize = sizeof(BITMAPINFOHEADER);\r
4786         bih.biWidth = b.bmWidth;\r
4787         bih.biHeight = b.bmHeight;\r
4788         bih.biPlanes = 1;\r
4789         bih.biBitCount = b.bmBitsPixel;\r
4790         bih.biCompression = 0;\r
4791         bih.biSizeImage = b.bmWidthBytes*b.bmHeight;\r
4792         bih.biXPelsPerMeter = 0;\r
4793         bih.biYPelsPerMeter = 0;\r
4794         bih.biClrUsed = 0;\r
4795         bih.biClrImportant = 0;\r
4796 //      fprintf(diagFile, "t=%d\nw=%d\nh=%d\nB=%d\nP=%d\nX=%d\n", \r
4797 //              b.bmType,  b.bmWidth,  b.bmHeight, b.bmWidthBytes,  b.bmPlanes,  b.bmBitsPixel);\r
4798         GetDIBits(tmphdc,bufferBitmap,0,b.bmHeight,pData,(BITMAPINFO*)&bih,DIB_RGB_COLORS);\r
4799 //      fprintf(diagFile, "%8x\n", (int) pData);\r
4800 \r
4801 #if 1\r
4802         wb = b.bmWidthBytes;\r
4803         // count colors\r
4804         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)>>2; i++) {\r
4805                 int k = ((int*) pData)[i];\r
4806                 for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4807                 if(j >= 16) break;\r
4808                 color[j] = k;\r
4809                 if(j >= nrColors) nrColors = j+1;\r
4810         }\r
4811         if(j<16) { // 16 colors is enough. Compress to 4 bits per pixel\r
4812                 INT p = 0;\r
4813                 for(i=0; i<b.bmHeight - boardRect.top + OUTER_MARGIN; i++) {\r
4814                     for(w=0; w<(wb>>2); w+=2) {\r
4815                         int k = ((int*) pData)[(wb*i>>2) + w];\r
4816                         for(j=0; j<nrColors; j++) if(color[j] == k) break;\r
4817                         k = ((int*) pData)[(wb*i>>2) + w + 1];\r
4818                         for(m=0; m<nrColors; m++) if(color[m] == k) break;\r
4819                         pData[p++] = m | j<<4;\r
4820                     }\r
4821                     while(p&3) pData[p++] = 0;\r
4822                 }\r
4823                 fac = 3;\r
4824                 wb = ((wb+31)>>5)<<2;\r
4825         }\r
4826         // write BITMAPFILEHEADER\r
4827         fprintf(diagFile, "BM");\r
4828         fputDW(diagFile, wb*(b.bmHeight - boardRect.top + OUTER_MARGIN)+0x36 + (fac?64:0));\r
4829         fputDW(diagFile, 0);\r
4830         fputDW(diagFile, 0x36 + (fac?64:0));\r
4831         // write BITMAPINFOHEADER\r
4832         fputDW(diagFile, 40);\r
4833         fputDW(diagFile, b.bmWidth);\r
4834         fputDW(diagFile, b.bmHeight - boardRect.top + OUTER_MARGIN);\r
4835         if(fac) fputDW(diagFile, 0x040001);   // planes and bits/pixel\r
4836         else    fputDW(diagFile, 0x200001);   // planes and bits/pixel\r
4837         fputDW(diagFile, 0);\r
4838         fputDW(diagFile, 0);\r
4839         fputDW(diagFile, 0);\r
4840         fputDW(diagFile, 0);\r
4841         fputDW(diagFile, 0);\r
4842         fputDW(diagFile, 0);\r
4843         // write color table\r
4844         if(fac)\r
4845         for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
4846         // write bitmap data\r
4847         for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
4848                 fputc(pData[i], diagFile);\r
4849 #endif\r
4850      }\r
4851   }\r
4852 \r
4853   SelectObject(tmphdc, oldBitmap);\r
4854 \r
4855   /* Massive cleanup */\r
4856   for (x = 0; x < num_clips; x++)\r
4857     DeleteObject(clips[x]);\r
4858 \r
4859   DeleteDC(tmphdc);\r
4860   DeleteObject(bufferBitmap);\r
4861 \r
4862   if (releaseDC) \r
4863     ReleaseDC(hwndMain, hdc);\r
4864   \r
4865   if (lastDrawnFlipView != flipView) {\r
4866     if (flipView)\r
4867       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
4868     else\r
4869       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_UNCHECKED);\r
4870   }\r
4871 \r
4872 /*  CopyBoard(lastDrawn, board);*/\r
4873   lastDrawnHighlight = highlightInfo;\r
4874   lastDrawnPremove   = premoveHighlightInfo;\r
4875   lastDrawnFlipView = flipView;\r
4876   lastDrawnValid = 1;\r
4877 }\r
4878 \r
4879 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
4880 int\r
4881 SaveDiagram(f)\r
4882      FILE *f;\r
4883 {\r
4884     saveDiagFlag = 1; diagFile = f;\r
4885     HDCDrawPosition(NULL, TRUE, NULL);\r
4886 \r
4887     saveDiagFlag = 0;\r
4888 \r
4889 //    if(f != NULL) fprintf(f, "Sorry, but this feature is still in preparation\n");\r
4890     \r
4891     fclose(f);\r
4892     return TRUE;\r
4893 }\r
4894 \r
4895 \r
4896 /*---------------------------------------------------------------------------*\\r
4897 | CLIENT PAINT PROCEDURE\r
4898 |   This is the main event-handler for the WM_PAINT message.\r
4899 |\r
4900 \*---------------------------------------------------------------------------*/\r
4901 VOID\r
4902 PaintProc(HWND hwnd)\r
4903 {\r
4904   HDC         hdc;\r
4905   PAINTSTRUCT ps;\r
4906   HFONT       oldFont;\r
4907 \r
4908   if((hdc = BeginPaint(hwnd, &ps))) {\r
4909     if (IsIconic(hwnd)) {\r
4910       DrawIcon(hdc, 2, 2, iconCurrent);\r
4911     } else {\r
4912       if (!appData.monoMode) {\r
4913         SelectPalette(hdc, hPal, FALSE);\r
4914         RealizePalette(hdc);\r
4915       }\r
4916       HDCDrawPosition(hdc, 1, NULL);\r
4917       oldFont =\r
4918         SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
4919       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
4920                  ETO_CLIPPED|ETO_OPAQUE,\r
4921                  &messageRect, messageText, strlen(messageText), NULL);\r
4922       SelectObject(hdc, oldFont);\r
4923       DisplayBothClocks();\r
4924     }\r
4925     EndPaint(hwnd,&ps);\r
4926   }\r
4927 \r
4928   return;\r
4929 }\r
4930 \r
4931 \r
4932 /*\r
4933  * If the user selects on a border boundary, return -1; if off the board,\r
4934  *   return -2.  Otherwise map the event coordinate to the square.\r
4935  * The offset boardRect.left or boardRect.top must already have been\r
4936  *   subtracted from x.\r
4937  */\r
4938 int\r
4939 EventToSquare(int x)\r
4940 {\r
4941   if (x <= 0)\r
4942     return -2;\r
4943   if (x < lineGap)\r
4944     return -1;\r
4945   x -= lineGap;\r
4946   if ((x % (squareSize + lineGap)) >= squareSize)\r
4947     return -1;\r
4948   x /= (squareSize + lineGap);\r
4949   if (x >= BOARD_SIZE)\r
4950     return -2;\r
4951   return x;\r
4952 }\r
4953 \r
4954 typedef struct {\r
4955   char piece;\r
4956   int command;\r
4957   char* name;\r
4958 } DropEnable;\r
4959 \r
4960 DropEnable dropEnables[] = {\r
4961   { 'P', DP_Pawn, "Pawn" },\r
4962   { 'N', DP_Knight, "Knight" },\r
4963   { 'B', DP_Bishop, "Bishop" },\r
4964   { 'R', DP_Rook, "Rook" },\r
4965   { 'Q', DP_Queen, "Queen" },\r
4966 };\r
4967 \r
4968 VOID\r
4969 SetupDropMenu(HMENU hmenu)\r
4970 {\r
4971   int i, count, enable;\r
4972   char *p;\r
4973   extern char white_holding[], black_holding[];\r
4974   char item[MSG_SIZ];\r
4975 \r
4976   for (i=0; i<sizeof(dropEnables)/sizeof(DropEnable); i++) {\r
4977     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,\r
4978                dropEnables[i].piece);\r
4979     count = 0;\r
4980     while (p && *p++ == dropEnables[i].piece) count++;\r
4981     sprintf(item, "%s  %d", dropEnables[i].name, count);\r
4982     enable = count > 0 || !appData.testLegality\r
4983       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
4984                       && !appData.icsActive);\r
4985     ModifyMenu(hmenu, dropEnables[i].command,\r
4986                MF_BYCOMMAND | (enable ? MF_ENABLED : MF_GRAYED) | MF_STRING,\r
4987                dropEnables[i].command, item);\r
4988   }\r
4989 }\r
4990 \r
4991 /* Event handler for mouse messages */\r
4992 VOID\r
4993 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
4994 {\r
4995   int x, y;\r
4996   POINT pt;\r
4997   static int recursive = 0;\r
4998   HMENU hmenu;\r
4999 //  BOOLEAN needsRedraw = FALSE;\r
5000   BOOLEAN saveAnimate;\r
5001   BOOLEAN forceFullRepaint = IsFullRepaintPreferrable(); /* [AS] */\r
5002   static BOOLEAN sameAgain = FALSE, promotionChoice = FALSE;\r
5003   ChessMove moveType;\r
5004 \r
5005   if (recursive) {\r
5006     if (message == WM_MBUTTONUP) {\r
5007       /* Hideous kludge to fool TrackPopupMenu into paying attention\r
5008          to the middle button: we simulate pressing the left button too!\r
5009          */\r
5010       PostMessage(hwnd, WM_LBUTTONDOWN, wParam, lParam);\r
5011       PostMessage(hwnd, WM_LBUTTONUP, wParam, lParam);\r
5012     }\r
5013     return;\r
5014   }\r
5015   recursive++;\r
5016   \r
5017   pt.x = LOWORD(lParam);\r
5018   pt.y = HIWORD(lParam);\r
5019   x = EventToSquare(pt.x - boardRect.left);\r
5020   y = EventToSquare(pt.y - boardRect.top);\r
5021   if (!flipView && y >= 0) {\r
5022     y = BOARD_HEIGHT - 1 - y;\r
5023   }\r
5024   if (flipView && x >= 0) {\r
5025     x = BOARD_WIDTH - 1 - x;\r
5026   }\r
5027 \r
5028   switch (message) {\r
5029   case WM_LBUTTONDOWN:\r
5030     if(promotionChoice) { // we are waiting for a click to indicate promotion piece\r
5031         promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel\r
5032         if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);\r
5033         if(gameInfo.holdingsWidth && \r
5034                 (WhiteOnMove(currentMove) \r
5035                         ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0\r
5036                         : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {\r
5037             // click in right holdings, for determining promotion piece\r
5038             ChessSquare p = boards[currentMove][y][x];\r
5039             if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);\r
5040             if(p != EmptySquare) {\r
5041                 FinishMove(WhitePromotionQueen, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));\r
5042                 fromX = fromY = -1;\r
5043                 break;\r
5044             }\r
5045         }\r
5046         DrawPosition(FALSE, boards[currentMove]);\r
5047         break;\r
5048     }\r
5049     ErrorPopDown();\r
5050     sameAgain = FALSE;\r
5051     if (y == -2) {\r
5052       /* Downclick vertically off board; check if on clock */\r
5053       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5054         if (gameMode == EditPosition) {\r
5055           SetWhiteToPlayEvent();\r
5056         } else if (gameMode == IcsPlayingBlack ||\r
5057                    gameMode == MachinePlaysWhite) {\r
5058           CallFlagEvent();\r
5059         } else if (gameMode == EditGame) {\r
5060           AdjustClock(flipClock, -1);\r
5061         }\r
5062       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5063         if (gameMode == EditPosition) {\r
5064           SetBlackToPlayEvent();\r
5065         } else if (gameMode == IcsPlayingWhite ||\r
5066                    gameMode == MachinePlaysBlack) {\r
5067           CallFlagEvent();\r
5068         } else if (gameMode == EditGame) {\r
5069           AdjustClock(!flipClock, -1);\r
5070         }\r
5071       }\r
5072       if (!appData.highlightLastMove) {\r
5073         ClearHighlights();\r
5074         DrawPosition((int) (forceFullRepaint || FALSE), NULL);\r
5075       }\r
5076       fromX = fromY = -1;\r
5077       dragInfo.start.x = dragInfo.start.y = -1;\r
5078       dragInfo.from = dragInfo.start;\r
5079       break;\r
5080     } else if (x < 0 || y < 0\r
5081       /* [HGM] block clicks between board and holdings */\r
5082               || x == BOARD_LEFT-1 || x == BOARD_RGHT\r
5083               || (x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize)\r
5084               || (x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize)\r
5085         /* EditPosition, empty square, or different color piece;\r
5086            click-click move is possible */\r
5087                                ) {\r
5088       break;\r
5089     } else if (fromX == x && fromY == y) {\r
5090       /* Downclick on same square again */\r
5091       ClearHighlights();\r
5092       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5093       sameAgain = TRUE;  \r
5094     } else if (fromX != -1 &&\r
5095                x != BOARD_LEFT-2 && x != BOARD_RGHT+1 \r
5096                                                                         ) {\r
5097       /* Downclick on different square. */\r
5098       /* [HGM] if on holdings file, should count as new first click ! */\r
5099       /* [HGM] <sameColor> now always do UserMoveTest(), and check colors there */\r
5100         toX = x;\r
5101         toY = y;\r
5102         /* [HGM] <popupFix> UserMoveEvent requires two calls now,\r
5103            to make sure move is legal before showing promotion popup */\r
5104         moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5105         if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5106                 fromX = fromY = -1; \r
5107                 ClearHighlights();\r
5108                 DrawPosition(FALSE, boards[currentMove]);\r
5109                 break; \r
5110         } else \r
5111         if(moveType != ImpossibleMove) {\r
5112           if(moveType == IllegalMove) {\r
5113                 ;\r
5114           } else\r
5115           /* [HGM] We use PromotionToKnight in Shogi to indicate frorced promotion */\r
5116           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5117             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5118               appData.alwaysPromoteToQueen)) {\r
5119                   FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5120                   if (!appData.highlightLastMove) {\r
5121                       ClearHighlights();\r
5122                       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5123                   }\r
5124           } else\r
5125           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5126                   SetHighlights(fromX, fromY, toX, toY);\r
5127                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5128                   /* [HGM] <popupFix> Popup calls FinishMove now.\r
5129                      If promotion to Q is legal, all are legal! */\r
5130                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5131                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5132                     // kludge to temporarily execute move on display, without promotng yet\r
5133                     promotionChoice = TRUE;\r
5134                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5135                     boards[currentMove][toY][toX] = p;\r
5136                     DrawPosition(FALSE, boards[currentMove]);\r
5137                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5138                     boards[currentMove][toY][toX] = q;\r
5139                   } else\r
5140                   PromotionPopup(hwnd);\r
5141           } else {       /* not a promotion */\r
5142              if (appData.animate || appData.highlightLastMove) {\r
5143                  SetHighlights(fromX, fromY, toX, toY);\r
5144              } else {\r
5145                  ClearHighlights();\r
5146              }\r
5147              FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5148              if (appData.animate && !appData.highlightLastMove) {\r
5149                   ClearHighlights();\r
5150                   DrawPosition(forceFullRepaint || FALSE, NULL);\r
5151              }\r
5152           }\r
5153           fromX = fromY = -1;\r
5154           break;\r
5155         }\r
5156         if (gotPremove) {\r
5157             SetPremoveHighlights(fromX, fromY, toX, toY);\r
5158             DrawPosition(forceFullRepaint || FALSE, NULL);\r
5159         } else ClearHighlights();\r
5160         fromX = fromY = -1;\r
5161         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5162     }\r
5163     /* First downclick, or restart on a square with same color piece */\r
5164     if (!frozen && OKToStartUserMove(x, y)) {\r
5165       fromX = x;\r
5166       fromY = y;\r
5167       dragInfo.lastpos = pt;\r
5168       dragInfo.from.x = fromX;\r
5169       dragInfo.from.y = fromY;\r
5170       dragInfo.start = dragInfo.from;\r
5171       SetCapture(hwndMain);\r
5172     } else {\r
5173       fromX = fromY = -1;\r
5174       dragInfo.start.x = dragInfo.start.y = -1;\r
5175       dragInfo.from = dragInfo.start;\r
5176       DrawPosition(forceFullRepaint || FALSE, NULL); /* [AS] */\r
5177     }\r
5178     break;\r
5179 \r
5180   case WM_LBUTTONUP:\r
5181     ReleaseCapture();\r
5182     if (fromX == -1) break;\r
5183     if (x == fromX && y == fromY) {\r
5184       dragInfo.from.x = dragInfo.from.y = -1;\r
5185       /* Upclick on same square */\r
5186       if (sameAgain) {\r
5187         /* Clicked same square twice: abort click-click move */\r
5188         fromX = fromY = -1;\r
5189         gotPremove = 0;\r
5190         ClearPremoveHighlights();\r
5191       } else {\r
5192         /* First square clicked: start click-click move */\r
5193         SetHighlights(fromX, fromY, -1, -1);\r
5194       }\r
5195       DrawPosition(forceFullRepaint || FALSE, NULL);\r
5196     } else if (dragInfo.from.x < 0 || dragInfo.from.y < 0) {\r
5197       /* Errant click; ignore */\r
5198       break;\r
5199     } else {\r
5200       /* Finish drag move. */\r
5201     if (appData.debugMode) {\r
5202         fprintf(debugFP, "release\n");\r
5203     }\r
5204       dragInfo.from.x = dragInfo.from.y = -1;\r
5205       toX = x;\r
5206       toY = y;\r
5207       saveAnimate = appData.animate; /* sorry, Hawk :) */\r
5208       appData.animate = appData.animate && !appData.animateDragging;\r
5209       moveType = UserMoveTest(fromX, fromY, toX, toY, NULLCHAR);\r
5210       if(moveType == AmbiguousMove) { /* [HGM] Edit-Position move executed */\r
5211                 fromX = fromY = -1; \r
5212                 ClearHighlights();\r
5213                 DrawPosition(FALSE, boards[currentMove]);\r
5214                 appData.animate = saveAnimate;\r
5215                 break; \r
5216       } else \r
5217       if(moveType != ImpossibleMove) {\r
5218           /* [HGM] use move type to determine if move is promotion.\r
5219              Knight is Shogi kludge for mandatory promotion, Queen means choice */\r
5220           if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||\r
5221             ((moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&\r
5222               appData.alwaysPromoteToQueen)) \r
5223                FinishMove(moveType, fromX, fromY, toX, toY, 'q');\r
5224           else \r
5225           if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {\r
5226                DrawPosition(forceFullRepaint || FALSE, NULL);\r
5227                   if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
5228                   { ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];\r
5229                     // kludge to temporarily execute move on display, wthout promotng yet\r
5230                     promotionChoice = TRUE;\r
5231                     boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank\r
5232                     boards[currentMove][toY][toX] = p;\r
5233                     DrawPosition(FALSE, boards[currentMove]);\r
5234                     boards[currentMove][fromY][fromX] = p; // take back, but display stays\r
5235                     boards[currentMove][toY][toX] = q;\r
5236                     appData.animate = saveAnimate;\r
5237                     break;\r
5238                   } else\r
5239                PromotionPopup(hwnd); /* [HGM] Popup now calls FinishMove */\r
5240           } else {\r
5241             if(saveAnimate /* ^$!%@#$!$ */  && gameInfo.variant == VariantAtomic \r
5242                           && (boards[currentMove][toY][toX] != EmptySquare || \r
5243                                         moveType == WhiteCapturesEnPassant || \r
5244                                         moveType == BlackCapturesEnPassant   ) )\r
5245                 AnimateAtomicCapture(fromX, fromY, toX, toY, 20);\r
5246             FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);\r
5247           }\r
5248       }\r
5249       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);\r
5250       appData.animate = saveAnimate;\r
5251       fromX = fromY = -1;\r
5252       if (appData.highlightDragging && !appData.highlightLastMove) {\r
5253         ClearHighlights();\r
5254       }\r
5255       if (appData.animate || appData.animateDragging ||\r
5256           appData.highlightDragging || gotPremove) {\r
5257         DrawPosition(forceFullRepaint || FALSE, NULL);\r
5258       }\r
5259     }\r
5260     dragInfo.start.x = dragInfo.start.y = -1; \r
5261     dragInfo.pos = dragInfo.lastpos = dragInfo.start;\r
5262     break;\r
5263 \r
5264   case WM_MOUSEMOVE:\r
5265     if ((appData.animateDragging || appData.highlightDragging)\r
5266         && (wParam & MK_LBUTTON)\r
5267         && dragInfo.from.x >= 0) \r
5268     {\r
5269       BOOL full_repaint = FALSE;\r
5270 \r
5271       sameAgain = FALSE; /* [HGM] if we drag something around, do keep square selected */\r
5272       if (appData.animateDragging) {\r
5273         dragInfo.pos = pt;\r
5274       }\r
5275       if (appData.highlightDragging) {\r
5276         SetHighlights(fromX, fromY, x, y);\r
5277         if( IsDrawArrowEnabled() && (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) ) {\r
5278             full_repaint = TRUE;\r
5279         }\r
5280       }\r
5281       \r
5282       DrawPosition( full_repaint, NULL);\r
5283       \r
5284       dragInfo.lastpos = dragInfo.pos;\r
5285     }\r
5286     break;\r
5287 \r
5288   case WM_MOUSEWHEEL: // [DM]\r
5289     {  static int lastDir = 0; // [HGM] build in some hysteresis to avoid spurious events\r
5290        /* Mouse Wheel is being rolled forward\r
5291         * Play moves forward\r
5292         */\r
5293        if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
5294                 { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
5295        /* Mouse Wheel is being rolled backward\r
5296         * Play moves backward\r
5297         */\r
5298        if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
5299                 { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
5300     }\r
5301     break;\r
5302 \r
5303   case WM_MBUTTONDOWN:\r
5304   case WM_RBUTTONDOWN:\r
5305     ErrorPopDown();\r
5306     ReleaseCapture();\r
5307     fromX = fromY = -1;\r
5308     dragInfo.pos.x = dragInfo.pos.y = -1;\r
5309     dragInfo.start.x = dragInfo.start.y = -1;\r
5310     dragInfo.from = dragInfo.start;\r
5311     dragInfo.lastpos = dragInfo.pos;\r
5312     if (appData.highlightDragging) {\r
5313       ClearHighlights();\r
5314     }\r
5315     if(y == -2) {\r
5316       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
5317       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
5318           if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
5319       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
5320           if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
5321       }\r
5322     }\r
5323     DrawPosition(TRUE, NULL);\r
5324 \r
5325     switch (gameMode) {\r
5326     case EditPosition:\r
5327     case IcsExamining:\r
5328       if (x < 0 || y < 0) break;\r
5329       fromX = x;\r
5330       fromY = y;\r
5331       if (message == WM_MBUTTONDOWN) {\r
5332         buttonCount = 3;  /* even if system didn't think so */\r
5333         if (wParam & MK_SHIFT) \r
5334           MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5335         else\r
5336           MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5337       } else { /* message == WM_RBUTTONDOWN */\r
5338 #if 0\r
5339         if (buttonCount == 3) {\r
5340           if (wParam & MK_SHIFT) \r
5341             MenuPopup(hwnd, pt, LoadMenu(hInst, "WhitePieceMenu"), -1);\r
5342           else\r
5343             MenuPopup(hwnd, pt, LoadMenu(hInst, "BlackPieceMenu"), -1);\r
5344         } else {\r
5345           MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5346         }\r
5347 #else\r
5348         /* Just have one menu, on the right button.  Windows users don't\r
5349            think to try the middle one, and sometimes other software steals\r
5350            it, or it doesn't really exist. */\r
5351         if(gameInfo.variant != VariantShogi)\r
5352             MenuPopup(hwnd, pt, LoadMenu(hInst, "PieceMenu"), -1);\r
5353         else\r
5354             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
5355 #endif\r
5356       }\r
5357       break;\r
5358     case IcsPlayingWhite:\r
5359     case IcsPlayingBlack:\r
5360     case EditGame:\r
5361     case MachinePlaysWhite:\r
5362     case MachinePlaysBlack:\r
5363       if (appData.testLegality &&\r
5364           gameInfo.variant != VariantBughouse &&\r
5365           gameInfo.variant != VariantCrazyhouse) break;\r
5366       if (x < 0 || y < 0) break;\r
5367       fromX = x;\r
5368       fromY = y;\r
5369       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
5370       SetupDropMenu(hmenu);\r
5371       MenuPopup(hwnd, pt, hmenu, -1);\r
5372       break;\r
5373     default:\r
5374       break;\r
5375     }\r
5376     break;\r
5377   }\r
5378 \r
5379   recursive--;\r
5380 }\r
5381 \r
5382 /* Preprocess messages for buttons in main window */\r
5383 LRESULT CALLBACK\r
5384 ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5385 {\r
5386   int id = GetWindowLong(hwnd, GWL_ID);\r
5387   int i, dir;\r
5388 \r
5389   for (i=0; i<N_BUTTONS; i++) {\r
5390     if (buttonDesc[i].id == id) break;\r
5391   }\r
5392   if (i == N_BUTTONS) return 0;\r
5393   switch (message) {\r
5394   case WM_KEYDOWN:\r
5395     switch (wParam) {\r
5396     case VK_LEFT:\r
5397     case VK_RIGHT:\r
5398       dir = (wParam == VK_LEFT) ? -1 : 1;\r
5399       SetFocus(buttonDesc[(i + dir + N_BUTTONS) % N_BUTTONS].hwnd);\r
5400       return TRUE;\r
5401     }\r
5402     break;\r
5403   case WM_CHAR:\r
5404     switch (wParam) {\r
5405     case '\r':\r
5406       SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(buttonDesc[i].id, 0), 0);\r
5407       return TRUE;\r
5408     default:\r
5409       if (appData.icsActive && (isalpha((char)wParam) || wParam == '0')) {\r
5410         // [HGM] movenum: only letters or leading zero should go to ICS input\r
5411         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5412         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5413         SetFocus(h);\r
5414         SendMessage(h, WM_CHAR, wParam, lParam);\r
5415         return TRUE;\r
5416       } else if (isalpha((char)wParam) || isdigit((char)wParam)){\r
5417         PopUpMoveDialog((char)wParam);\r
5418       }\r
5419       break;\r
5420     }\r
5421     break;\r
5422   }\r
5423   return CallWindowProc(buttonDesc[i].wndproc, hwnd, message, wParam, lParam);\r
5424 }\r
5425 \r
5426 /* Process messages for Promotion dialog box */\r
5427 LRESULT CALLBACK\r
5428 Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
5429 {\r
5430   char promoChar;\r
5431 \r
5432   switch (message) {\r
5433   case WM_INITDIALOG: /* message: initialize dialog box */\r
5434     /* Center the dialog over the application window */\r
5435     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
5436     ShowWindow(GetDlgItem(hDlg, PB_King), \r
5437       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
5438        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
5439                SW_SHOW : SW_HIDE);\r
5440     /* [HGM] Only allow C & A promotions if these pieces are defined */\r
5441     ShowWindow(GetDlgItem(hDlg, PB_Archbishop),\r
5442        ((PieceToChar(WhiteAngel) >= 'A' &&\r
5443          PieceToChar(WhiteAngel) != '~') ||\r
5444         (PieceToChar(BlackAngel) >= 'A' &&\r
5445          PieceToChar(BlackAngel) != '~')   ) ?\r
5446                SW_SHOW : SW_HIDE);\r
5447     ShowWindow(GetDlgItem(hDlg, PB_Chancellor), \r
5448        ((PieceToChar(WhiteMarshall) >= 'A' &&\r
5449          PieceToChar(WhiteMarshall) != '~') ||\r
5450         (PieceToChar(BlackMarshall) >= 'A' &&\r
5451          PieceToChar(BlackMarshall) != '~')   ) ?\r
5452                SW_SHOW : SW_HIDE);\r
5453     /* [HGM] Hide B & R button in Shogi, use Q as promote, N as defer */\r
5454     ShowWindow(GetDlgItem(hDlg, PB_Rook),\r
5455        gameInfo.variant != VariantShogi ?\r
5456                SW_SHOW : SW_HIDE);\r
5457     ShowWindow(GetDlgItem(hDlg, PB_Bishop), \r
5458        gameInfo.variant != VariantShogi ?\r
5459                SW_SHOW : SW_HIDE);\r
5460     ShowWindow(GetDlgItem(hDlg, IDC_Yes), \r
5461        gameInfo.variant == VariantShogi ?\r
5462                SW_SHOW : SW_HIDE);\r
5463     ShowWindow(GetDlgItem(hDlg, IDC_No), \r
5464        gameInfo.variant == VariantShogi ?\r
5465                SW_SHOW : SW_HIDE);\r
5466     ShowWindow(GetDlgItem(hDlg, IDC_Centaur), \r
5467        gameInfo.variant == VariantSuper ?\r
5468                SW_SHOW : SW_HIDE);\r
5469     return TRUE;\r
5470 \r
5471   case WM_COMMAND: /* message: received a command */\r
5472     switch (LOWORD(wParam)) {\r
5473     case IDCANCEL:\r
5474       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5475       ClearHighlights();\r
5476       DrawPosition(FALSE, NULL);\r
5477       return TRUE;\r
5478     case PB_King:\r
5479       promoChar = gameInfo.variant == VariantSuper ? PieceToChar(BlackSilver) : PieceToChar(BlackKing);\r
5480       break;\r
5481     case PB_Queen:\r
5482       promoChar = gameInfo.variant == VariantShogi ? '+' : PieceToChar(BlackQueen);\r
5483       break;\r
5484     case PB_Rook:\r
5485       promoChar = PieceToChar(BlackRook);\r
5486       break;\r
5487     case PB_Bishop:\r
5488       promoChar = PieceToChar(BlackBishop);\r
5489       break;\r
5490     case PB_Chancellor:\r
5491       promoChar = PieceToChar(BlackMarshall);\r
5492       break;\r
5493     case PB_Archbishop:\r
5494       promoChar = PieceToChar(BlackAngel);\r
5495       break;\r
5496     case PB_Knight:\r
5497       promoChar = gameInfo.variant == VariantShogi ? '=' : PieceToChar(BlackKnight);\r
5498       break;\r
5499     default:\r
5500       return FALSE;\r
5501     }\r
5502     EndDialog(hDlg, TRUE); /* Exit the dialog */\r
5503     /* [HGM] <popupFix> Call FinishMove rather than UserMoveEvent, as we\r
5504        only show the popup when we are already sure the move is valid or\r
5505        legal. We pass a faulty move type, but the kludge is that FinishMove\r
5506        will figure out it is a promotion from the promoChar. */\r
5507     FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);\r
5508     if (!appData.highlightLastMove) {\r
5509       ClearHighlights();\r
5510       DrawPosition(FALSE, NULL);\r
5511     }\r
5512     return TRUE;\r
5513   }\r
5514   return FALSE;\r
5515 }\r
5516 \r
5517 /* Pop up promotion dialog */\r
5518 VOID\r
5519 PromotionPopup(HWND hwnd)\r
5520 {\r
5521   FARPROC lpProc;\r
5522 \r
5523   lpProc = MakeProcInstance((FARPROC)Promotion, hInst);\r
5524   DialogBox(hInst, MAKEINTRESOURCE(DLG_PromotionKing),\r
5525     hwnd, (DLGPROC)lpProc);\r
5526   FreeProcInstance(lpProc);\r
5527 }\r
5528 \r
5529 /* Toggle ShowThinking */\r
5530 VOID\r
5531 ToggleShowThinking()\r
5532 {\r
5533   appData.showThinking = !appData.showThinking;\r
5534   ShowThinkingEvent();\r
5535 }\r
5536 \r
5537 VOID\r
5538 LoadGameDialog(HWND hwnd, char* title)\r
5539 {\r
5540   UINT number = 0;\r
5541   FILE *f;\r
5542   char fileTitle[MSG_SIZ];\r
5543   f = OpenFileDialog(hwnd, "rb", "",\r
5544                      appData.oldSaveStyle ? "gam" : "pgn",\r
5545                      GAME_FILT,\r
5546                      title, &number, fileTitle, NULL);\r
5547   if (f != NULL) {\r
5548     cmailMsgLoaded = FALSE;\r
5549     if (number == 0) {\r
5550       int error = GameListBuild(f);\r
5551       if (error) {\r
5552         DisplayError("Cannot build game list", error);\r
5553       } else if (!ListEmpty(&gameList) &&\r
5554                  ((ListGame *) gameList.tailPred)->number > 1) {\r
5555         GameListPopUp(f, fileTitle);\r
5556         return;\r
5557       }\r
5558       GameListDestroy();\r
5559       number = 1;\r
5560     }\r
5561     LoadGame(f, number, fileTitle, FALSE);\r
5562   }\r
5563 }\r
5564 \r
5565 VOID\r
5566 ChangedConsoleFont()\r
5567 {\r
5568   CHARFORMAT cfmt;\r
5569   CHARRANGE tmpsel, sel;\r
5570   MyFont *f = font[boardSize][CONSOLE_FONT];\r
5571   HWND hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
5572   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5573   PARAFORMAT paraf;\r
5574 \r
5575   cfmt.cbSize = sizeof(CHARFORMAT);\r
5576   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
5577   strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
5578   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
5579    * size.  This was undocumented in the version of MSVC++ that I had\r
5580    * when I wrote the code, but is apparently documented now.\r
5581    */\r
5582   cfmt.yHeight = (int)(f->mfp.pointSize * 20.0 + 0.5);\r
5583   cfmt.bCharSet = f->lf.lfCharSet;\r
5584   cfmt.bPitchAndFamily = f->lf.lfPitchAndFamily;\r
5585   SendMessage(hText, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5586   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &cfmt); \r
5587   /* Why are the following seemingly needed too? */\r
5588   SendMessage(hText, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5589   SendMessage(hInput, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfmt); \r
5590   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&sel);\r
5591   tmpsel.cpMin = 0;\r
5592   tmpsel.cpMax = -1; /*999999?*/\r
5593   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&tmpsel);\r
5594   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &cfmt); \r
5595   /* Trying putting this here too.  It still seems to tickle a RichEdit\r
5596    *  bug: sometimes RichEdit indents the first line of a paragraph too.\r
5597    */\r
5598   paraf.cbSize = sizeof(paraf);\r
5599   paraf.dwMask = PFM_OFFSET | PFM_STARTINDENT;\r
5600   paraf.dxStartIndent = 0;\r
5601   paraf.dxOffset = WRAP_INDENT;\r
5602   SendMessage(hText, EM_SETPARAFORMAT, 0, (LPARAM) &paraf);\r
5603   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
5604 }\r
5605 \r
5606 /*---------------------------------------------------------------------------*\\r
5607  *\r
5608  * Window Proc for main window\r
5609  *\r
5610 \*---------------------------------------------------------------------------*/\r
5611 \r
5612 /* Process messages for main window, etc. */\r
5613 LRESULT CALLBACK\r
5614 WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
5615 {\r
5616   FARPROC lpProc;\r
5617   int wmId, wmEvent;\r
5618   char *defName;\r
5619   FILE *f;\r
5620   UINT number;\r
5621   char fileTitle[MSG_SIZ];\r
5622   char buf[MSG_SIZ];\r
5623   static SnapData sd;\r
5624 \r
5625   switch (message) {\r
5626 \r
5627   case WM_PAINT: /* message: repaint portion of window */\r
5628     PaintProc(hwnd);\r
5629     break;\r
5630 \r
5631   case WM_ERASEBKGND:\r
5632     if (IsIconic(hwnd)) {\r
5633       /* Cheat; change the message */\r
5634       return (DefWindowProc(hwnd, WM_ICONERASEBKGND, wParam, lParam));\r
5635     } else {\r
5636       return (DefWindowProc(hwnd, message, wParam, lParam));\r
5637     }\r
5638     break;\r
5639 \r
5640   case WM_LBUTTONDOWN:\r
5641   case WM_MBUTTONDOWN:\r
5642   case WM_RBUTTONDOWN:\r
5643   case WM_LBUTTONUP:\r
5644   case WM_MBUTTONUP:\r
5645   case WM_RBUTTONUP:\r
5646   case WM_MOUSEMOVE:\r
5647   case WM_MOUSEWHEEL:\r
5648     MouseEvent(hwnd, message, wParam, lParam);\r
5649     break;\r
5650 \r
5651   JAWS_KB_NAVIGATION\r
5652 \r
5653   case WM_CHAR:\r
5654     \r
5655     JAWS_ALT_INTERCEPT\r
5656 \r
5657     if (appData.icsActive && (char)wParam > ' ' && !((char)wParam >= '1' && (char)wParam <= '9')) { \r
5658         // [HGM] movenum: for non-zero digits we always do type-in dialog\r
5659         HWND h = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
5660         if (IsIconic(hwndConsole)) ShowWindow(hwndConsole, SW_RESTORE);\r
5661         SetFocus(h);\r
5662         SendMessage(h, message, wParam, lParam);\r
5663     } else if(lParam != KF_REPEAT) {\r
5664         if (isalpha((char)wParam) || isdigit((char)wParam)) {\r
5665                 PopUpMoveDialog((char)wParam);\r
5666         } else if((char)wParam == 003) CopyGameToClipboard();\r
5667          else if((char)wParam == 026) PasteGameOrFENFromClipboard();\r
5668     }\r
5669 \r
5670     break;\r
5671 \r
5672   case WM_PALETTECHANGED:\r
5673     if (hwnd != (HWND)wParam && !appData.monoMode) {\r
5674       int nnew;\r
5675       HDC hdc = GetDC(hwndMain);\r
5676       SelectPalette(hdc, hPal, TRUE);\r
5677       nnew = RealizePalette(hdc);\r
5678       if (nnew > 0) {\r
5679         paletteChanged = TRUE;\r
5680 #if 0\r
5681         UpdateColors(hdc);\r
5682 #else\r
5683         InvalidateRect(hwnd, &boardRect, FALSE);/*faster!*/\r
5684 #endif\r
5685       }\r
5686       ReleaseDC(hwnd, hdc);\r
5687     }\r
5688     break;\r
5689 \r
5690   case WM_QUERYNEWPALETTE:\r
5691     if (!appData.monoMode /*&& paletteChanged*/) {\r
5692       int nnew;\r
5693       HDC hdc = GetDC(hwndMain);\r
5694       paletteChanged = FALSE;\r
5695       SelectPalette(hdc, hPal, FALSE);\r
5696       nnew = RealizePalette(hdc);\r
5697       if (nnew > 0) {\r
5698         InvalidateRect(hwnd, &boardRect, FALSE);\r
5699       }\r
5700       ReleaseDC(hwnd, hdc);\r
5701       return TRUE;\r
5702     }\r
5703     return FALSE;\r
5704 \r
5705   case WM_COMMAND: /* message: command from application menu */\r
5706     wmId    = LOWORD(wParam);\r
5707     wmEvent = HIWORD(wParam);\r
5708 \r
5709     switch (wmId) {\r
5710     case IDM_NewGame:\r
5711       ResetGameEvent();\r
5712       AnalysisPopDown();\r
5713       SAY("new game enter a move to play against the computer with white");\r
5714       break;\r
5715 \r
5716     case IDM_NewGameFRC:\r
5717       if( NewGameFRC() == 0 ) {\r
5718         ResetGameEvent();\r
5719         AnalysisPopDown();\r
5720       }\r
5721       break;\r
5722 \r
5723     case IDM_NewVariant:\r
5724       NewVariantPopup(hwnd);\r
5725       break;\r
5726 \r
5727     case IDM_LoadGame:\r
5728       LoadGameDialog(hwnd, "Load Game from File");\r
5729       break;\r
5730 \r
5731     case IDM_LoadNextGame:\r
5732       ReloadGame(1);\r
5733       break;\r
5734 \r
5735     case IDM_LoadPrevGame:\r
5736       ReloadGame(-1);\r
5737       break;\r
5738 \r
5739     case IDM_ReloadGame:\r
5740       ReloadGame(0);\r
5741       break;\r
5742 \r
5743     case IDM_LoadPosition:\r
5744       if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {\r
5745         Reset(FALSE, TRUE);\r
5746       }\r
5747       number = 1;\r
5748       f = OpenFileDialog(hwnd, "rb", "",\r
5749                          appData.oldSaveStyle ? "pos" : "fen",\r
5750                          POSITION_FILT,\r
5751                          "Load Position from File", &number, fileTitle, NULL);\r
5752       if (f != NULL) {\r
5753         LoadPosition(f, number, fileTitle);\r
5754       }\r
5755       break;\r
5756 \r
5757     case IDM_LoadNextPosition:\r
5758       ReloadPosition(1);\r
5759       break;\r
5760 \r
5761     case IDM_LoadPrevPosition:\r
5762       ReloadPosition(-1);\r
5763       break;\r
5764 \r
5765     case IDM_ReloadPosition:\r
5766       ReloadPosition(0);\r
5767       break;\r
5768 \r
5769     case IDM_SaveGame:\r
5770       defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
5771       f = OpenFileDialog(hwnd, "a", defName,\r
5772                          appData.oldSaveStyle ? "gam" : "pgn",\r
5773                          GAME_FILT,\r
5774                          "Save Game to File", NULL, fileTitle, NULL);\r
5775       if (f != NULL) {\r
5776         SaveGame(f, 0, "");\r
5777       }\r
5778       break;\r
5779 \r
5780     case IDM_SavePosition:\r
5781       defName = DefaultFileName(appData.oldSaveStyle ? "pos" : "fen");\r
5782       f = OpenFileDialog(hwnd, "a", defName,\r
5783                          appData.oldSaveStyle ? "pos" : "fen",\r
5784                          POSITION_FILT,\r
5785                          "Save Position to File", NULL, fileTitle, NULL);\r
5786       if (f != NULL) {\r
5787         SavePosition(f, 0, "");\r
5788       }\r
5789       break;\r
5790 \r
5791     case IDM_SaveDiagram:\r
5792       defName = "diagram";\r
5793       f = OpenFileDialog(hwnd, "wb", defName,\r
5794                          "bmp",\r
5795                          DIAGRAM_FILT,\r
5796                          "Save Diagram to File", NULL, fileTitle, NULL);\r
5797       if (f != NULL) {\r
5798         SaveDiagram(f);\r
5799       }\r
5800       break;\r
5801 \r
5802     case IDM_CopyGame:\r
5803       CopyGameToClipboard();\r
5804       break;\r
5805 \r
5806     case IDM_PasteGame:\r
5807       PasteGameFromClipboard();\r
5808       break;\r
5809 \r
5810     case IDM_CopyGameListToClipboard:\r
5811       CopyGameListToClipboard();\r
5812       break;\r
5813 \r
5814     /* [AS] Autodetect FEN or PGN data */\r
5815     case IDM_PasteAny:\r
5816       PasteGameOrFENFromClipboard();\r
5817       break;\r
5818 \r
5819     /* [AS] Move history */\r
5820     case IDM_ShowMoveHistory:\r
5821         if( MoveHistoryIsUp() ) {\r
5822             MoveHistoryPopDown();\r
5823         }\r
5824         else {\r
5825             MoveHistoryPopUp();\r
5826         }\r
5827         break;\r
5828 \r
5829     /* [AS] Eval graph */\r
5830     case IDM_ShowEvalGraph:\r
5831         if( EvalGraphIsUp() ) {\r
5832             EvalGraphPopDown();\r
5833         }\r
5834         else {\r
5835             EvalGraphPopUp();\r
5836             SetFocus(hwndMain);\r
5837         }\r
5838         break;\r
5839 \r
5840     /* [AS] Engine output */\r
5841     case IDM_ShowEngineOutput:\r
5842         if( EngineOutputIsUp() ) {\r
5843             EngineOutputPopDown();\r
5844         }\r
5845         else {\r
5846             EngineOutputPopUp();\r
5847         }\r
5848         break;\r
5849 \r
5850     /* [AS] User adjudication */\r
5851     case IDM_UserAdjudication_White:\r
5852         UserAdjudicationEvent( +1 );\r
5853         break;\r
5854 \r
5855     case IDM_UserAdjudication_Black:\r
5856         UserAdjudicationEvent( -1 );\r
5857         break;\r
5858 \r
5859     case IDM_UserAdjudication_Draw:\r
5860         UserAdjudicationEvent( 0 );\r
5861         break;\r
5862 \r
5863     /* [AS] Game list options dialog */\r
5864     case IDM_GameListOptions:\r
5865       GameListOptions();\r
5866       break;\r
5867 \r
5868     case IDM_CopyPosition:\r
5869       CopyFENToClipboard();\r
5870       break;\r
5871 \r
5872     case IDM_PastePosition:\r
5873       PasteFENFromClipboard();\r
5874       break;\r
5875 \r
5876     case IDM_MailMove:\r
5877       MailMoveEvent();\r
5878       break;\r
5879 \r
5880     case IDM_ReloadCMailMsg:\r
5881       Reset(TRUE, TRUE);\r
5882       ReloadCmailMsgEvent(FALSE);\r
5883       break;\r
5884 \r
5885     case IDM_Minimize:\r
5886       ShowWindow(hwnd, SW_MINIMIZE);\r
5887       break;\r
5888 \r
5889     case IDM_Exit:\r
5890       ExitEvent(0);\r
5891       break;\r
5892 \r
5893     case IDM_MachineWhite:\r
5894       MachineWhiteEvent();\r
5895       /*\r
5896        * refresh the tags dialog only if it's visible\r
5897        */\r
5898       if (gameMode == MachinePlaysWhite && IsWindowVisible(editTagsDialog)) {\r
5899           char *tags;\r
5900           tags = PGNTags(&gameInfo);\r
5901           TagsPopUp(tags, CmailMsg());\r
5902           free(tags);\r
5903       }\r
5904       SAY("computer starts playing white");\r
5905       break;\r
5906 \r
5907     case IDM_MachineBlack:\r
5908       MachineBlackEvent();\r
5909       /*\r
5910        * refresh the tags dialog only if it's visible\r
5911        */\r
5912       if (gameMode == MachinePlaysBlack && IsWindowVisible(editTagsDialog)) {\r
5913           char *tags;\r
5914           tags = PGNTags(&gameInfo);\r
5915           TagsPopUp(tags, CmailMsg());\r
5916           free(tags);\r
5917       }\r
5918       SAY("computer starts playing black");\r
5919       break;\r
5920 \r
5921     case IDM_TwoMachines:\r
5922       TwoMachinesEvent();\r
5923       /*\r
5924        * refresh the tags dialog only if it's visible\r
5925        */\r
5926       if (gameMode == TwoMachinesPlay && IsWindowVisible(editTagsDialog)) {\r
5927           char *tags;\r
5928           tags = PGNTags(&gameInfo);\r
5929           TagsPopUp(tags, CmailMsg());\r
5930           free(tags);\r
5931       }\r
5932       SAY("programs start playing each other");\r
5933       break;\r
5934 \r
5935     case IDM_AnalysisMode:\r
5936       if (!first.analysisSupport) {\r
5937         sprintf(buf, "%s does not support analysis", first.tidy);\r
5938         DisplayError(buf, 0);\r
5939       } else {\r
5940         SAY("analyzing current position");\r
5941         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
5942         if (appData.icsActive) {\r
5943                if (gameMode != IcsObserving) {\r
5944                        sprintf(buf, "You are not observing a game");\r
5945                        DisplayError(buf, 0);\r
5946                        /* secure check */\r
5947                        if (appData.icsEngineAnalyze) {\r
5948                                if (appData.debugMode) \r
5949                                        fprintf(debugFP, "Found unexpected active ICS engine analyze \n");\r
5950                                ExitAnalyzeMode();\r
5951                                ModeHighlight();\r
5952                                break;\r
5953                        }\r
5954                        break;\r
5955                } else {\r
5956                        /* if enable, user want disable icsEngineAnalyze */\r
5957                        if (appData.icsEngineAnalyze) {\r
5958                                ExitAnalyzeMode();\r
5959                                ModeHighlight();\r
5960                                break;\r
5961                        }\r
5962                        appData.icsEngineAnalyze = TRUE;\r
5963                        if (appData.debugMode) fprintf(debugFP, "ICS engine analyze starting...\n");\r
5964                }\r
5965         } \r
5966         if (!appData.showThinking) ToggleShowThinking();\r
5967         AnalyzeModeEvent();\r
5968       }\r
5969       break;\r
5970 \r
5971     case IDM_AnalyzeFile:\r
5972       if (!first.analysisSupport) {\r
5973         char buf[MSG_SIZ];\r
5974         sprintf(buf, "%s does not support analysis", first.tidy);\r
5975         DisplayError(buf, 0);\r
5976       } else {\r
5977         if (!appData.showThinking) ToggleShowThinking();\r
5978         AnalyzeFileEvent();\r
5979         LoadGameDialog(hwnd, "Analyze Game from File");\r
5980         AnalysisPeriodicEvent(1);\r
5981       }\r
5982       break;\r
5983 \r
5984     case IDM_IcsClient:\r
5985       IcsClientEvent();\r
5986       break;\r
5987 \r
5988     case IDM_EditGame:\r
5989       EditGameEvent();\r
5990       SAY("edit game");\r
5991       break;\r
5992 \r
5993     case IDM_EditPosition:\r
5994       EditPositionEvent();\r
5995       SAY("to set up a position type a FEN");\r
5996       break;\r
5997 \r
5998     case IDM_Training:\r
5999       TrainingEvent();\r
6000       break;\r
6001 \r
6002     case IDM_ShowGameList:\r
6003       ShowGameListProc();\r
6004       break;\r
6005 \r
6006     case IDM_EditTags:\r
6007       EditTagsProc();\r
6008       break;\r
6009 \r
6010     case IDM_EditComment:\r
6011       if (commentDialogUp && editComment) {\r
6012         CommentPopDown();\r
6013       } else {\r
6014         EditCommentEvent();\r
6015       }\r
6016       break;\r
6017 \r
6018     case IDM_Pause:\r
6019       PauseEvent();\r
6020       break;\r
6021 \r
6022     case IDM_Accept:\r
6023       AcceptEvent();\r
6024       break;\r
6025 \r
6026     case IDM_Decline:\r
6027       DeclineEvent();\r
6028       break;\r
6029 \r
6030     case IDM_Rematch:\r
6031       RematchEvent();\r
6032       break;\r
6033 \r
6034     case IDM_CallFlag:\r
6035       CallFlagEvent();\r
6036       break;\r
6037 \r
6038     case IDM_Draw:\r
6039       DrawEvent();\r
6040       break;\r
6041 \r
6042     case IDM_Adjourn:\r
6043       AdjournEvent();\r
6044       break;\r
6045 \r
6046     case IDM_Abort:\r
6047       AbortEvent();\r
6048       break;\r
6049 \r
6050     case IDM_Resign:\r
6051       ResignEvent();\r
6052       break;\r
6053 \r
6054     case IDM_StopObserving:\r
6055       StopObservingEvent();\r
6056       break;\r
6057 \r
6058     case IDM_StopExamining:\r
6059       StopExaminingEvent();\r
6060       break;\r
6061 \r
6062     case IDM_TypeInMove:\r
6063       PopUpMoveDialog('\000');\r
6064       break;\r
6065 \r
6066     case IDM_TypeInName:\r
6067       PopUpNameDialog('\000');\r
6068       break;\r
6069 \r
6070     case IDM_Backward:\r
6071       BackwardEvent();\r
6072       SetFocus(hwndMain);\r
6073       break;\r
6074 \r
6075     JAWS_MENU_ITEMS\r
6076 \r
6077     case IDM_Forward:\r
6078       ForwardEvent();\r
6079       SetFocus(hwndMain);\r
6080       break;\r
6081 \r
6082     case IDM_ToStart:\r
6083       ToStartEvent();\r
6084       SetFocus(hwndMain);\r
6085       break;\r
6086 \r
6087     case IDM_ToEnd:\r
6088       ToEndEvent();\r
6089       SetFocus(hwndMain);\r
6090       break;\r
6091 \r
6092     case IDM_Revert:\r
6093       RevertEvent();\r
6094       break;\r
6095 \r
6096     case IDM_TruncateGame:\r
6097       TruncateGameEvent();\r
6098       break;\r
6099 \r
6100     case IDM_MoveNow:\r
6101       MoveNowEvent();\r
6102       break;\r
6103 \r
6104     case IDM_RetractMove:\r
6105       RetractMoveEvent();\r
6106       break;\r
6107 \r
6108     case IDM_FlipView:\r
6109       flipView = !flipView;\r
6110       DrawPosition(FALSE, NULL);\r
6111       break;\r
6112 \r
6113     case IDM_FlipClock:\r
6114       flipClock = !flipClock;\r
6115       DisplayBothClocks();\r
6116       DrawPosition(FALSE, NULL);\r
6117       break;\r
6118 \r
6119     case IDM_GeneralOptions:\r
6120       GeneralOptionsPopup(hwnd);\r
6121       DrawPosition(TRUE, NULL);\r
6122       break;\r
6123 \r
6124     case IDM_BoardOptions:\r
6125       BoardOptionsPopup(hwnd);\r
6126       break;\r
6127 \r
6128     case IDM_EnginePlayOptions:\r
6129       EnginePlayOptionsPopup(hwnd);\r
6130       break;\r
6131 \r
6132     case IDM_Engine1Options:\r
6133       EngineOptionsPopup(hwnd, &first);\r
6134       break;\r
6135 \r
6136     case IDM_Engine2Options:\r
6137       EngineOptionsPopup(hwnd, &second);\r
6138       break;\r
6139 \r
6140     case IDM_OptionsUCI:\r
6141       UciOptionsPopup(hwnd);\r
6142       break;\r
6143 \r
6144     case IDM_IcsOptions:\r
6145       IcsOptionsPopup(hwnd);\r
6146       break;\r
6147 \r
6148     case IDM_Fonts:\r
6149       FontsOptionsPopup(hwnd);\r
6150       break;\r
6151 \r
6152     case IDM_Sounds:\r
6153       SoundOptionsPopup(hwnd);\r
6154       break;\r
6155 \r
6156     case IDM_CommPort:\r
6157       CommPortOptionsPopup(hwnd);\r
6158       break;\r
6159 \r
6160     case IDM_LoadOptions:\r
6161       LoadOptionsPopup(hwnd);\r
6162       break;\r
6163 \r
6164     case IDM_SaveOptions:\r
6165       SaveOptionsPopup(hwnd);\r
6166       break;\r
6167 \r
6168     case IDM_TimeControl:\r
6169       TimeControlOptionsPopup(hwnd);\r
6170       break;\r
6171 \r
6172     case IDM_SaveSettings:\r
6173       SaveSettings(settingsFileName);\r
6174       break;\r
6175 \r
6176     case IDM_SaveSettingsOnExit:\r
6177       saveSettingsOnExit = !saveSettingsOnExit;\r
6178       (void) CheckMenuItem(GetMenu(hwndMain), IDM_SaveSettingsOnExit,\r
6179                            MF_BYCOMMAND|(saveSettingsOnExit ?\r
6180                                          MF_CHECKED : MF_UNCHECKED));\r
6181       break;\r
6182 \r
6183     case IDM_Hint:\r
6184       HintEvent();\r
6185       break;\r
6186 \r
6187     case IDM_Book:\r
6188       BookEvent();\r
6189       break;\r
6190 \r
6191     case IDM_AboutGame:\r
6192       AboutGameEvent();\r
6193       break;\r
6194 \r
6195     case IDM_Debug:\r
6196       appData.debugMode = !appData.debugMode;\r
6197       if (appData.debugMode) {\r
6198         char dir[MSG_SIZ];\r
6199         GetCurrentDirectory(MSG_SIZ, dir);\r
6200         SetCurrentDirectory(installDir);\r
6201         debugFP = fopen(appData.nameOfDebugFile, "w");\r
6202         SetCurrentDirectory(dir);\r
6203         setbuf(debugFP, NULL);\r
6204       } else {\r
6205         fclose(debugFP);\r
6206         debugFP = NULL;\r
6207       }\r
6208       break;\r
6209 \r
6210     case IDM_HELPCONTENTS:\r
6211       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
6212           !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
6213           MessageBox (GetFocus(),\r
6214                     "Unable to activate help",\r
6215                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6216       }\r
6217       break;\r
6218 \r
6219     case IDM_HELPSEARCH:\r
6220         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
6221             !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
6222         MessageBox (GetFocus(),\r
6223                     "Unable to activate help",\r
6224                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6225       }\r
6226       break;\r
6227 \r
6228     case IDM_HELPHELP:\r
6229       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
6230         MessageBox (GetFocus(),\r
6231                     "Unable to activate help",\r
6232                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
6233       }\r
6234       break;\r
6235 \r
6236     case IDM_ABOUT:\r
6237       lpProc = MakeProcInstance((FARPROC)About, hInst);\r
6238       DialogBox(hInst, \r
6239         (gameInfo.event && strcmp(gameInfo.event, "Easter Egg Hunt") == 0) ?\r
6240         "AboutBox2" : "AboutBox", hwnd, (DLGPROC)lpProc);\r
6241       FreeProcInstance(lpProc);\r
6242       break;\r
6243 \r
6244     case IDM_DirectCommand1:\r
6245       AskQuestionEvent("Direct Command",\r
6246                        "Send to chess program:", "", "1");\r
6247       break;\r
6248     case IDM_DirectCommand2:\r
6249       AskQuestionEvent("Direct Command",\r
6250                        "Send to second chess program:", "", "2");\r
6251       break;\r
6252 \r
6253     case EP_WhitePawn:\r
6254       EditPositionMenuEvent(WhitePawn, fromX, fromY);\r
6255       fromX = fromY = -1;\r
6256       break;\r
6257 \r
6258     case EP_WhiteKnight:\r
6259       EditPositionMenuEvent(WhiteKnight, fromX, fromY);\r
6260       fromX = fromY = -1;\r
6261       break;\r
6262 \r
6263     case EP_WhiteBishop:\r
6264       EditPositionMenuEvent(WhiteBishop, fromX, fromY);\r
6265       fromX = fromY = -1;\r
6266       break;\r
6267 \r
6268     case EP_WhiteRook:\r
6269       EditPositionMenuEvent(WhiteRook, fromX, fromY);\r
6270       fromX = fromY = -1;\r
6271       break;\r
6272 \r
6273     case EP_WhiteQueen:\r
6274       EditPositionMenuEvent(WhiteQueen, fromX, fromY);\r
6275       fromX = fromY = -1;\r
6276       break;\r
6277 \r
6278     case EP_WhiteFerz:\r
6279       EditPositionMenuEvent(WhiteFerz, fromX, fromY);\r
6280       fromX = fromY = -1;\r
6281       break;\r
6282 \r
6283     case EP_WhiteWazir:\r
6284       EditPositionMenuEvent(WhiteWazir, fromX, fromY);\r
6285       fromX = fromY = -1;\r
6286       break;\r
6287 \r
6288     case EP_WhiteAlfil:\r
6289       EditPositionMenuEvent(WhiteAlfil, fromX, fromY);\r
6290       fromX = fromY = -1;\r
6291       break;\r
6292 \r
6293     case EP_WhiteCannon:\r
6294       EditPositionMenuEvent(WhiteCannon, fromX, fromY);\r
6295       fromX = fromY = -1;\r
6296       break;\r
6297 \r
6298     case EP_WhiteCardinal:\r
6299       EditPositionMenuEvent(WhiteAngel, fromX, fromY);\r
6300       fromX = fromY = -1;\r
6301       break;\r
6302 \r
6303     case EP_WhiteMarshall:\r
6304       EditPositionMenuEvent(WhiteMarshall, fromX, fromY);\r
6305       fromX = fromY = -1;\r
6306       break;\r
6307 \r
6308     case EP_WhiteKing:\r
6309       EditPositionMenuEvent(WhiteKing, fromX, fromY);\r
6310       fromX = fromY = -1;\r
6311       break;\r
6312 \r
6313     case EP_BlackPawn:\r
6314       EditPositionMenuEvent(BlackPawn, fromX, fromY);\r
6315       fromX = fromY = -1;\r
6316       break;\r
6317 \r
6318     case EP_BlackKnight:\r
6319       EditPositionMenuEvent(BlackKnight, fromX, fromY);\r
6320       fromX = fromY = -1;\r
6321       break;\r
6322 \r
6323     case EP_BlackBishop:\r
6324       EditPositionMenuEvent(BlackBishop, fromX, fromY);\r
6325       fromX = fromY = -1;\r
6326       break;\r
6327 \r
6328     case EP_BlackRook:\r
6329       EditPositionMenuEvent(BlackRook, fromX, fromY);\r
6330       fromX = fromY = -1;\r
6331       break;\r
6332 \r
6333     case EP_BlackQueen:\r
6334       EditPositionMenuEvent(BlackQueen, fromX, fromY);\r
6335       fromX = fromY = -1;\r
6336       break;\r
6337 \r
6338     case EP_BlackFerz:\r
6339       EditPositionMenuEvent(BlackFerz, fromX, fromY);\r
6340       fromX = fromY = -1;\r
6341       break;\r
6342 \r
6343     case EP_BlackWazir:\r
6344       EditPositionMenuEvent(BlackWazir, fromX, fromY);\r
6345       fromX = fromY = -1;\r
6346       break;\r
6347 \r
6348     case EP_BlackAlfil:\r
6349       EditPositionMenuEvent(BlackAlfil, fromX, fromY);\r
6350       fromX = fromY = -1;\r
6351       break;\r
6352 \r
6353     case EP_BlackCannon:\r
6354       EditPositionMenuEvent(BlackCannon, fromX, fromY);\r
6355       fromX = fromY = -1;\r
6356       break;\r
6357 \r
6358     case EP_BlackCardinal:\r
6359       EditPositionMenuEvent(BlackAngel, fromX, fromY);\r
6360       fromX = fromY = -1;\r
6361       break;\r
6362 \r
6363     case EP_BlackMarshall:\r
6364       EditPositionMenuEvent(BlackMarshall, fromX, fromY);\r
6365       fromX = fromY = -1;\r
6366       break;\r
6367 \r
6368     case EP_BlackKing:\r
6369       EditPositionMenuEvent(BlackKing, fromX, fromY);\r
6370       fromX = fromY = -1;\r
6371       break;\r
6372 \r
6373     case EP_EmptySquare:\r
6374       EditPositionMenuEvent(EmptySquare, fromX, fromY);\r
6375       fromX = fromY = -1;\r
6376       break;\r
6377 \r
6378     case EP_ClearBoard:\r
6379       EditPositionMenuEvent(ClearBoard, fromX, fromY);\r
6380       fromX = fromY = -1;\r
6381       break;\r
6382 \r
6383     case EP_White:\r
6384       EditPositionMenuEvent(WhitePlay, fromX, fromY);\r
6385       fromX = fromY = -1;\r
6386       break;\r
6387 \r
6388     case EP_Black:\r
6389       EditPositionMenuEvent(BlackPlay, fromX, fromY);\r
6390       fromX = fromY = -1;\r
6391       break;\r
6392 \r
6393     case EP_Promote:\r
6394       EditPositionMenuEvent(PromotePiece, fromX, fromY);\r
6395       fromX = fromY = -1;\r
6396       break;\r
6397 \r
6398     case EP_Demote:\r
6399       EditPositionMenuEvent(DemotePiece, fromX, fromY);\r
6400       fromX = fromY = -1;\r
6401       break;\r
6402 \r
6403     case DP_Pawn:\r
6404       DropMenuEvent(WhitePawn, fromX, fromY);\r
6405       fromX = fromY = -1;\r
6406       break;\r
6407 \r
6408     case DP_Knight:\r
6409       DropMenuEvent(WhiteKnight, fromX, fromY);\r
6410       fromX = fromY = -1;\r
6411       break;\r
6412 \r
6413     case DP_Bishop:\r
6414       DropMenuEvent(WhiteBishop, fromX, fromY);\r
6415       fromX = fromY = -1;\r
6416       break;\r
6417 \r
6418     case DP_Rook:\r
6419       DropMenuEvent(WhiteRook, fromX, fromY);\r
6420       fromX = fromY = -1;\r
6421       break;\r
6422 \r
6423     case DP_Queen:\r
6424       DropMenuEvent(WhiteQueen, fromX, fromY);\r
6425       fromX = fromY = -1;\r
6426       break;\r
6427 \r
6428     default:\r
6429       return (DefWindowProc(hwnd, message, wParam, lParam));\r
6430     }\r
6431     break;\r
6432 \r
6433   case WM_TIMER:\r
6434     switch (wParam) {\r
6435     case CLOCK_TIMER_ID:\r
6436       KillTimer(hwnd, clockTimerEvent);  /* Simulate one-shot timer as in X */\r
6437       clockTimerEvent = 0;\r
6438       DecrementClocks(); /* call into back end */\r
6439       break;\r
6440     case LOAD_GAME_TIMER_ID:\r
6441       KillTimer(hwnd, loadGameTimerEvent); /* Simulate one-shot timer as in X*/\r
6442       loadGameTimerEvent = 0;\r
6443       AutoPlayGameLoop(); /* call into back end */\r
6444       break;\r
6445     case ANALYSIS_TIMER_ID:\r
6446       if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile\r
6447                  || appData.icsEngineAnalyze) && appData.periodicUpdates) {\r
6448         AnalysisPeriodicEvent(0);\r
6449       } else {\r
6450         KillTimer(hwnd, analysisTimerEvent);\r
6451         analysisTimerEvent = 0;\r
6452       }\r
6453       break;\r
6454     case DELAYED_TIMER_ID:\r
6455       KillTimer(hwnd, delayedTimerEvent);\r
6456       delayedTimerEvent = 0;\r
6457       delayedTimerCallback();\r
6458       break;\r
6459     }\r
6460     break;\r
6461 \r
6462   case WM_USER_Input:\r
6463     InputEvent(hwnd, message, wParam, lParam);\r
6464     break;\r
6465 \r
6466   /* [AS] Also move "attached" child windows */\r
6467   case WM_WINDOWPOSCHANGING:\r
6468 \r
6469     if( hwnd == hwndMain && appData.useStickyWindows ) {\r
6470         LPWINDOWPOS lpwp = (LPWINDOWPOS) lParam;\r
6471 \r
6472         if( ((lpwp->flags & SWP_NOMOVE) == 0) && ((lpwp->flags & SWP_NOSIZE) != 0) ) {\r
6473             /* Window is moving */\r
6474             RECT rcMain;\r
6475 \r
6476 //            GetWindowRect( hwnd, &rcMain ); //[HGM] sticky: in XP this returned new position, not old\r
6477             rcMain.left   = boardX;           //              replace by these 4 lines to reconstruct old rect\r
6478             rcMain.right  = boardX + winWidth;\r
6479             rcMain.top    = boardY;\r
6480             rcMain.bottom = boardY + winHeight;\r
6481             \r
6482             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, moveHistoryDialog, &wpMoveHistory );\r
6483             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, evalGraphDialog, &wpEvalGraph );\r
6484             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, engineOutputDialog, &wpEngineOutput );\r
6485             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, gameListDialog, &wpGameList );\r
6486             ReattachAfterMove( &rcMain, lpwp->x, lpwp->y, hwndConsole, &wpConsole );\r
6487             boardX = lpwp->x;\r
6488             boardY = lpwp->y;\r
6489         }\r
6490     }\r
6491     break;\r
6492 \r
6493   /* [AS] Snapping */\r
6494   case WM_ENTERSIZEMOVE:\r
6495     if(appData.debugMode) { fprintf(debugFP, "size-move\n"); }\r
6496     if (hwnd == hwndMain) {\r
6497       doingSizing = TRUE;\r
6498       lastSizing = 0;\r
6499     }\r
6500     return OnEnterSizeMove( &sd, hwnd, wParam, lParam );\r
6501     break;\r
6502 \r
6503   case WM_SIZING:\r
6504     if(appData.debugMode) { fprintf(debugFP, "sizing\n"); }\r
6505     if (hwnd == hwndMain) {\r
6506       lastSizing = wParam;\r
6507     }\r
6508     break;\r
6509 \r
6510   case WM_MOVING:\r
6511     if(appData.debugMode) { fprintf(debugFP, "moving\n"); }\r
6512       return OnMoving( &sd, hwnd, wParam, lParam );\r
6513 \r
6514   case WM_EXITSIZEMOVE:\r
6515     if(appData.debugMode) { fprintf(debugFP, "exit size-move, size = %d\n", squareSize); }\r
6516     if (hwnd == hwndMain) {\r
6517       RECT client;\r
6518       doingSizing = FALSE;\r
6519       InvalidateRect(hwnd, &boardRect, FALSE);\r
6520       GetClientRect(hwnd, &client);\r
6521       ResizeBoard(client.right, client.bottom, lastSizing);\r
6522       lastSizing = 0;\r
6523       if(appData.debugMode) { fprintf(debugFP, "square size = %d\n", squareSize); }\r
6524     }\r
6525     return OnExitSizeMove( &sd, hwnd, wParam, lParam );\r
6526     break;\r
6527 \r
6528   case WM_DESTROY: /* message: window being destroyed */\r
6529     PostQuitMessage(0);\r
6530     break;\r
6531 \r
6532   case WM_CLOSE:\r
6533     if (hwnd == hwndMain) {\r
6534       ExitEvent(0);\r
6535     }\r
6536     break;\r
6537 \r
6538   default:      /* Passes it on if unprocessed */\r
6539     return (DefWindowProc(hwnd, message, wParam, lParam));\r
6540   }\r
6541   return 0;\r
6542 }\r
6543 \r
6544 /*---------------------------------------------------------------------------*\\r
6545  *\r
6546  * Misc utility routines\r
6547  *\r
6548 \*---------------------------------------------------------------------------*/\r
6549 \r
6550 /*\r
6551  * Decent random number generator, at least not as bad as Windows\r
6552  * standard rand, which returns a value in the range 0 to 0x7fff.\r
6553  */\r
6554 unsigned int randstate;\r
6555 \r
6556 int\r
6557 myrandom(void)\r
6558 {\r
6559   randstate = randstate * 1664525 + 1013904223;\r
6560   return (int) randstate & 0x7fffffff;\r
6561 }\r
6562 \r
6563 void\r
6564 mysrandom(unsigned int seed)\r
6565 {\r
6566   randstate = seed;\r
6567 }\r
6568 \r
6569 \r
6570 /* \r
6571  * returns TRUE if user selects a different color, FALSE otherwise \r
6572  */\r
6573 \r
6574 BOOL\r
6575 ChangeColor(HWND hwnd, COLORREF *which)\r
6576 {\r
6577   static BOOL firstTime = TRUE;\r
6578   static DWORD customColors[16];\r
6579   CHOOSECOLOR cc;\r
6580   COLORREF newcolor;\r
6581   int i;\r
6582   ColorClass ccl;\r
6583 \r
6584   if (firstTime) {\r
6585     /* Make initial colors in use available as custom colors */\r
6586     /* Should we put the compiled-in defaults here instead? */\r
6587     i = 0;\r
6588     customColors[i++] = lightSquareColor & 0xffffff;\r
6589     customColors[i++] = darkSquareColor & 0xffffff;\r
6590     customColors[i++] = whitePieceColor & 0xffffff;\r
6591     customColors[i++] = blackPieceColor & 0xffffff;\r
6592     customColors[i++] = highlightSquareColor & 0xffffff;\r
6593     customColors[i++] = premoveHighlightColor & 0xffffff;\r
6594 \r
6595     for (ccl = (ColorClass) 0; ccl < NColorClasses && i < 16; ccl++) {\r
6596       customColors[i++] = textAttribs[ccl].color;\r
6597     }\r
6598     while (i < 16) customColors[i++] = RGB(255, 255, 255);\r
6599     firstTime = FALSE;\r
6600   }\r
6601 \r
6602   cc.lStructSize = sizeof(cc);\r
6603   cc.hwndOwner = hwnd;\r
6604   cc.hInstance = NULL;\r
6605   cc.rgbResult = (DWORD) (*which & 0xffffff);\r
6606   cc.lpCustColors = (LPDWORD) customColors;\r
6607   cc.Flags = CC_RGBINIT|CC_FULLOPEN;\r
6608 \r
6609   if (!ChooseColor(&cc)) return FALSE;\r
6610 \r
6611   newcolor = (COLORREF) (0x2000000 | cc.rgbResult);\r
6612   if (newcolor == *which) return FALSE;\r
6613   *which = newcolor;\r
6614   return TRUE;\r
6615 \r
6616   /*\r
6617   InitDrawingColors();\r
6618   InvalidateRect(hwnd, &boardRect, FALSE);\r
6619   */\r
6620 }\r
6621 \r
6622 BOOLEAN\r
6623 MyLoadSound(MySound *ms)\r
6624 {\r
6625   BOOL ok = FALSE;\r
6626   struct stat st;\r
6627   FILE *f;\r
6628 \r
6629   if (ms->data) free(ms->data);\r
6630   ms->data = NULL;\r
6631 \r
6632   switch (ms->name[0]) {\r
6633   case NULLCHAR:\r
6634     /* Silence */\r
6635     ok = TRUE;\r
6636     break;\r
6637   case '$':\r
6638     /* System sound from Control Panel.  Don't preload here. */\r
6639     ok = TRUE;\r
6640     break;\r
6641   case '!':\r
6642     if (ms->name[1] == NULLCHAR) {\r
6643       /* "!" alone = silence */\r
6644       ok = TRUE;\r
6645     } else {\r
6646       /* Builtin wave resource.  Error if not found. */\r
6647       HANDLE h = FindResource(hInst, ms->name + 1, "WAVE");\r
6648       if (h == NULL) break;\r
6649       ms->data = (void *)LoadResource(hInst, h);\r
6650       if (h == NULL) break;\r
6651       ok = TRUE;\r
6652     }\r
6653     break;\r
6654   default:\r
6655     /* .wav file.  Error if not found. */\r
6656     f = fopen(ms->name, "rb");\r
6657     if (f == NULL) break;\r
6658     if (fstat(fileno(f), &st) < 0) break;\r
6659     ms->data = malloc(st.st_size);\r
6660     if (fread(ms->data, st.st_size, 1, f) < 1) break;\r
6661     fclose(f);\r
6662     ok = TRUE;\r
6663     break;\r
6664   }\r
6665   if (!ok) {\r
6666     char buf[MSG_SIZ];\r
6667     sprintf(buf, "Error loading sound %s", ms->name);\r
6668     DisplayError(buf, GetLastError());\r
6669   }\r
6670   return ok;\r
6671 }\r
6672 \r
6673 BOOLEAN\r
6674 MyPlaySound(MySound *ms)\r
6675 {\r
6676   BOOLEAN ok = FALSE;\r
6677 \r
6678   switch (ms->name[0]) {\r
6679   case NULLCHAR:\r
6680         if(appData.debugMode) fprintf(debugFP, "silence\n");\r
6681     /* Silence */\r
6682     ok = TRUE;\r
6683     break;\r
6684   case '$':\r
6685     /* System sound from Control Panel (deprecated feature).\r
6686        "$" alone or an unset sound name gets default beep (still in use). */\r
6687     if (ms->name[1]) {\r
6688       ok = PlaySound(ms->name + 1, NULL, SND_ALIAS|SND_ASYNC);\r
6689     }\r
6690     if (!ok) ok = MessageBeep(MB_OK);\r
6691     break; \r
6692   case '!':\r
6693     /* Builtin wave resource, or "!" alone for silence */\r
6694     if (ms->name[1]) {\r
6695       if (ms->data == NULL) return FALSE;\r
6696       ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6697     } else {\r
6698       ok = TRUE;\r
6699     }\r
6700     break;\r
6701   default:\r
6702     /* .wav file.  Error if not found. */\r
6703     if (ms->data == NULL) return FALSE;\r
6704     ok = PlaySound(ms->data, NULL, SND_MEMORY|SND_ASYNC);\r
6705     break;\r
6706   }\r
6707   /* Don't print an error: this can happen innocently if the sound driver\r
6708      is busy; for instance, if another instance of WinBoard is playing\r
6709      a sound at about the same time. */\r
6710 #if 0\r
6711   if (!ok) {\r
6712     char buf[MSG_SIZ];\r
6713     sprintf(buf, "Error playing sound %s", ms->name);\r
6714     DisplayError(buf, GetLastError());\r
6715   }\r
6716 #endif\r
6717   return ok;\r
6718 }\r
6719 \r
6720 \r
6721 LRESULT CALLBACK\r
6722 OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
6723 {\r
6724   BOOL ok;\r
6725   OPENFILENAME *ofn;\r
6726   static UINT *number; /* gross that this is static */\r
6727 \r
6728   switch (message) {\r
6729   case WM_INITDIALOG: /* message: initialize dialog box */\r
6730     /* Center the dialog over the application window */\r
6731     ofn = (OPENFILENAME *) lParam;\r
6732     if (ofn->Flags & OFN_ENABLETEMPLATE) {\r
6733       number = (UINT *) ofn->lCustData;\r
6734       SendMessage(GetDlgItem(hDlg, edt2), WM_SETTEXT, 0, (LPARAM) "");\r
6735     } else {\r
6736       number = NULL;\r
6737     }\r
6738     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
6739     return FALSE;  /* Allow for further processing */\r
6740 \r
6741   case WM_COMMAND:\r
6742     if ((LOWORD(wParam) == IDOK) && (number != NULL)) {\r
6743       *number = GetDlgItemInt(hDlg, OPT_IndexNumberOld, &ok, FALSE);\r
6744     }\r
6745     return FALSE;  /* Allow for further processing */\r
6746   }\r
6747   return FALSE;\r
6748 }\r
6749 \r
6750 UINT APIENTRY\r
6751 OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)\r
6752 {\r
6753   static UINT *number;\r
6754   OPENFILENAME *ofname;\r
6755   OFNOTIFY *ofnot;\r
6756   switch (uiMsg) {\r
6757   case WM_INITDIALOG:\r
6758     ofname = (OPENFILENAME *)lParam;\r
6759     number = (UINT *)(ofname->lCustData);\r
6760     break;\r
6761   case WM_NOTIFY:\r
6762     ofnot = (OFNOTIFY *)lParam;\r
6763     if (ofnot->hdr.code == CDN_FILEOK) {\r
6764       *number = GetDlgItemInt(hdlg, OPT_IndexNumber, NULL, FALSE);\r
6765     }\r
6766     break;\r
6767   }\r
6768   return 0;\r
6769 }\r
6770 \r
6771 \r
6772 FILE *\r
6773 OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] diag: type of 'write' now string\r
6774                char *nameFilt, char *dlgTitle, UINT *number,\r
6775                char fileTitle[MSG_SIZ], char fileName[MSG_SIZ])\r
6776 {\r
6777   OPENFILENAME openFileName;\r
6778   char buf1[MSG_SIZ];\r
6779   FILE *f;\r
6780 \r
6781   if (fileName == NULL) fileName = buf1;\r
6782   if (defName == NULL) {\r
6783     strcpy(fileName, "*.");\r
6784     strcat(fileName, defExt);\r
6785   } else {\r
6786     strcpy(fileName, defName);\r
6787   }\r
6788   if (fileTitle) strcpy(fileTitle, "");\r
6789   if (number) *number = 0;\r
6790 \r
6791   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
6792   openFileName.hwndOwner         = hwnd;\r
6793   openFileName.hInstance         = (HANDLE) hInst;\r
6794   openFileName.lpstrFilter       = nameFilt;\r
6795   openFileName.lpstrCustomFilter = (LPSTR) NULL;\r
6796   openFileName.nMaxCustFilter    = 0L;\r
6797   openFileName.nFilterIndex      = 1L;\r
6798   openFileName.lpstrFile         = fileName;\r
6799   openFileName.nMaxFile          = MSG_SIZ;\r
6800   openFileName.lpstrFileTitle    = fileTitle;\r
6801   openFileName.nMaxFileTitle     = fileTitle ? MSG_SIZ : 0;\r
6802   openFileName.lpstrInitialDir   = NULL;\r
6803   openFileName.lpstrTitle        = dlgTitle;\r
6804   openFileName.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY \r
6805     | (write[0] != 'r' ? 0 : OFN_FILEMUSTEXIST) \r
6806     | (number ? OFN_ENABLETEMPLATE | OFN_ENABLEHOOK: 0)\r
6807     | (oldDialog ? 0 : OFN_EXPLORER);\r
6808   openFileName.nFileOffset       = 0;\r
6809   openFileName.nFileExtension    = 0;\r
6810   openFileName.lpstrDefExt       = defExt;\r
6811   openFileName.lCustData         = (LONG) number;\r
6812   openFileName.lpfnHook          = oldDialog ?\r
6813     (LPOFNHOOKPROC) OldOpenFileHook : (LPOFNHOOKPROC) OpenFileHook;\r
6814   openFileName.lpTemplateName    = (LPSTR)(oldDialog ? 1536 : DLG_IndexNumber);\r
6815 \r
6816   if (write[0] != 'r' ? GetSaveFileName(&openFileName) : \r
6817                         GetOpenFileName(&openFileName)) {\r
6818     /* open the file */\r
6819     f = fopen(openFileName.lpstrFile, write);\r
6820     if (f == NULL) {\r
6821       MessageBox(hwnd, "File open failed", NULL,\r
6822                  MB_OK|MB_ICONEXCLAMATION);\r
6823       return NULL;\r
6824     }\r
6825   } else {\r
6826     int err = CommDlgExtendedError();\r
6827     if (err != 0) DisplayError("Internal error in file dialog box", err);\r
6828     return FALSE;\r
6829   }\r
6830   return f;\r
6831 }\r
6832 \r
6833 \r
6834 \r
6835 VOID APIENTRY\r
6836 MenuPopup(HWND hwnd, POINT pt, HMENU hmenu, UINT def)\r
6837 {\r
6838   HMENU hmenuTrackPopup;        /* floating pop-up menu  */\r
6839 \r
6840   /*\r
6841    * Get the first pop-up menu in the menu template. This is the\r
6842    * menu that TrackPopupMenu displays.\r
6843    */\r
6844   hmenuTrackPopup = GetSubMenu(hmenu, 0);\r
6845 \r
6846   SetMenuDefaultItem(hmenuTrackPopup, def, FALSE);\r
6847 \r
6848   /*\r
6849    * TrackPopup uses screen coordinates, so convert the\r
6850    * coordinates of the mouse click to screen coordinates.\r
6851    */\r
6852   ClientToScreen(hwnd, (LPPOINT) &pt);\r
6853 \r
6854   /* Draw and track the floating pop-up menu. */\r
6855   TrackPopupMenu(hmenuTrackPopup, TPM_CENTERALIGN | TPM_RIGHTBUTTON,\r
6856                  pt.x, pt.y, 0, hwnd, NULL);\r
6857 \r
6858   /* Destroy the menu.*/\r
6859   DestroyMenu(hmenu);\r
6860 }\r
6861    \r
6862 typedef struct {\r
6863   HWND hDlg, hText;\r
6864   int sizeX, sizeY, newSizeX, newSizeY;\r
6865   HDWP hdwp;\r
6866 } ResizeEditPlusButtonsClosure;\r
6867 \r
6868 BOOL CALLBACK\r
6869 ResizeEditPlusButtonsCallback(HWND hChild, LPARAM lparam)\r
6870 {\r
6871   ResizeEditPlusButtonsClosure *cl = (ResizeEditPlusButtonsClosure *)lparam;\r
6872   RECT rect;\r
6873   POINT pt;\r
6874 \r
6875   if (hChild == cl->hText) return TRUE;\r
6876   GetWindowRect(hChild, &rect); /* gives screen coords */\r
6877   pt.x = rect.left + (cl->newSizeX - cl->sizeX)/2;\r
6878   pt.y = rect.top + cl->newSizeY - cl->sizeY;\r
6879   ScreenToClient(cl->hDlg, &pt);\r
6880   cl->hdwp = DeferWindowPos(cl->hdwp, hChild, NULL, \r
6881     pt.x, pt.y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
6882   return TRUE;\r
6883 }\r
6884 \r
6885 /* Resize a dialog that has a (rich) edit field filling most of\r
6886    the top, with a row of buttons below */\r
6887 VOID\r
6888 ResizeEditPlusButtons(HWND hDlg, HWND hText, int sizeX, int sizeY, int newSizeX, int newSizeY)\r
6889 {\r
6890   RECT rectText;\r
6891   int newTextHeight, newTextWidth;\r
6892   ResizeEditPlusButtonsClosure cl;\r
6893   \r
6894   /*if (IsIconic(hDlg)) return;*/\r
6895   if (newSizeX == sizeX && newSizeY == sizeY) return;\r
6896   \r
6897   cl.hdwp = BeginDeferWindowPos(8);\r
6898 \r
6899   GetWindowRect(hText, &rectText); /* gives screen coords */\r
6900   newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
6901   newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
6902   if (newTextHeight < 0) {\r
6903     newSizeY += -newTextHeight;\r
6904     newTextHeight = 0;\r
6905   }\r
6906   cl.hdwp = DeferWindowPos(cl.hdwp, hText, NULL, 0, 0, \r
6907     newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
6908 \r
6909   cl.hDlg = hDlg;\r
6910   cl.hText = hText;\r
6911   cl.sizeX = sizeX;\r
6912   cl.sizeY = sizeY;\r
6913   cl.newSizeX = newSizeX;\r
6914   cl.newSizeY = newSizeY;\r
6915   EnumChildWindows(hDlg, ResizeEditPlusButtonsCallback, (LPARAM)&cl);\r
6916 \r
6917   EndDeferWindowPos(cl.hdwp);\r
6918 }\r
6919 \r
6920 BOOL CenterWindowEx(HWND hwndChild, HWND hwndParent, int mode)\r
6921 {\r
6922     RECT    rChild, rParent;\r
6923     int     wChild, hChild, wParent, hParent;\r
6924     int     wScreen, hScreen, xNew, yNew;\r
6925     HDC     hdc;\r
6926 \r
6927     /* Get the Height and Width of the child window */\r
6928     GetWindowRect (hwndChild, &rChild);\r
6929     wChild = rChild.right - rChild.left;\r
6930     hChild = rChild.bottom - rChild.top;\r
6931 \r
6932     /* Get the Height and Width of the parent window */\r
6933     GetWindowRect (hwndParent, &rParent);\r
6934     wParent = rParent.right - rParent.left;\r
6935     hParent = rParent.bottom - rParent.top;\r
6936 \r
6937     /* Get the display limits */\r
6938     hdc = GetDC (hwndChild);\r
6939     wScreen = GetDeviceCaps (hdc, HORZRES);\r
6940     hScreen = GetDeviceCaps (hdc, VERTRES);\r
6941     ReleaseDC(hwndChild, hdc);\r
6942 \r
6943     /* Calculate new X position, then adjust for screen */\r
6944     xNew = rParent.left + ((wParent - wChild) /2);\r
6945     if (xNew < 0) {\r
6946         xNew = 0;\r
6947     } else if ((xNew+wChild) > wScreen) {\r
6948         xNew = wScreen - wChild;\r
6949     }\r
6950 \r
6951     /* Calculate new Y position, then adjust for screen */\r
6952     if( mode == 0 ) {\r
6953         yNew = rParent.top  + ((hParent - hChild) /2);\r
6954     }\r
6955     else {\r
6956         yNew = rParent.top + GetSystemMetrics( SM_CYCAPTION ) * 2 / 3;\r
6957     }\r
6958 \r
6959     if (yNew < 0) {\r
6960         yNew = 0;\r
6961     } else if ((yNew+hChild) > hScreen) {\r
6962         yNew = hScreen - hChild;\r
6963     }\r
6964 \r
6965     /* Set it, and return */\r
6966     return SetWindowPos (hwndChild, NULL,\r
6967                          xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);\r
6968 }\r
6969 \r
6970 /* Center one window over another */\r
6971 BOOL CenterWindow (HWND hwndChild, HWND hwndParent)\r
6972 {\r
6973     return CenterWindowEx( hwndChild, hwndParent, 0 );\r
6974 }\r
6975 \r
6976 /*---------------------------------------------------------------------------*\\r
6977  *\r
6978  * Startup Dialog functions\r
6979  *\r
6980 \*---------------------------------------------------------------------------*/\r
6981 void\r
6982 InitComboStrings(HANDLE hwndCombo, char **cd)\r
6983 {\r
6984   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
6985 \r
6986   while (*cd != NULL) {\r
6987     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
6988     cd++;\r
6989   }\r
6990 }\r
6991 \r
6992 void\r
6993 InitComboStringsFromOption(HANDLE hwndCombo, char *str)\r
6994 {\r
6995   char buf1[ARG_MAX];\r
6996   int len;\r
6997 \r
6998   if (str[0] == '@') {\r
6999     FILE* f = fopen(str + 1, "r");\r
7000     if (f == NULL) {\r
7001       DisplayFatalError(str + 1, errno, 2);\r
7002       return;\r
7003     }\r
7004     len = fread(buf1, 1, sizeof(buf1)-1, f);\r
7005     fclose(f);\r
7006     buf1[len] = NULLCHAR;\r
7007     str = buf1;\r
7008   }\r
7009 \r
7010   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
7011 \r
7012   for (;;) {\r
7013     char buf[MSG_SIZ];\r
7014     char *end = strchr(str, '\n');\r
7015     if (end == NULL) return;\r
7016     memcpy(buf, str, end - str);\r
7017     buf[end - str] = NULLCHAR;\r
7018     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) buf);\r
7019     str = end + 1;\r
7020   }\r
7021 }\r
7022 \r
7023 void\r
7024 SetStartupDialogEnables(HWND hDlg)\r
7025 {\r
7026   EnableWindow(GetDlgItem(hDlg, OPT_ChessEngineName),\r
7027     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7028     (appData.zippyPlay && IsDlgButtonChecked(hDlg, OPT_ChessServer)));\r
7029   EnableWindow(GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7030     IsDlgButtonChecked(hDlg, OPT_ChessEngine));\r
7031   EnableWindow(GetDlgItem(hDlg, OPT_ChessServerName),\r
7032     IsDlgButtonChecked(hDlg, OPT_ChessServer));\r
7033   EnableWindow(GetDlgItem(hDlg, OPT_AdditionalOptions),\r
7034     IsDlgButtonChecked(hDlg, OPT_AnyAdditional));\r
7035   EnableWindow(GetDlgItem(hDlg, IDOK),\r
7036     IsDlgButtonChecked(hDlg, OPT_ChessEngine) ||\r
7037     IsDlgButtonChecked(hDlg, OPT_ChessServer) ||\r
7038     IsDlgButtonChecked(hDlg, OPT_View));\r
7039 }\r
7040 \r
7041 char *\r
7042 QuoteForFilename(char *filename)\r
7043 {\r
7044   int dquote, space;\r
7045   dquote = strchr(filename, '"') != NULL;\r
7046   space = strchr(filename, ' ') != NULL;\r
7047   if (dquote || space) {\r
7048     if (dquote) {\r
7049       return "'";\r
7050     } else {\r
7051       return "\"";\r
7052     }\r
7053   } else {\r
7054     return "";\r
7055   }\r
7056 }\r
7057 \r
7058 VOID\r
7059 InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir, char *nthnames)\r
7060 {\r
7061   char buf[MSG_SIZ];\r
7062   char *q;\r
7063 \r
7064   InitComboStringsFromOption(hwndCombo, nthnames);\r
7065   q = QuoteForFilename(nthcp);\r
7066   sprintf(buf, "%s%s%s", q, nthcp, q);\r
7067   if (*nthdir != NULLCHAR) {\r
7068     q = QuoteForFilename(nthdir);\r
7069     sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
7070   }\r
7071   if (*nthcp == NULLCHAR) {\r
7072     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7073   } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7074     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7075     SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7076   }\r
7077 }\r
7078 \r
7079 LRESULT CALLBACK\r
7080 StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7081 {\r
7082   char buf[MSG_SIZ];\r
7083   HANDLE hwndCombo;\r
7084   char *p;\r
7085 \r
7086   switch (message) {\r
7087   case WM_INITDIALOG:\r
7088     /* Center the dialog */\r
7089     CenterWindow (hDlg, GetDesktopWindow());\r
7090     /* Initialize the dialog items */\r
7091     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
7092                   appData.firstChessProgram, "fd", appData.firstDirectory,\r
7093                   firstChessProgramNames);\r
7094     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_SecondChessEngineName),\r
7095                   appData.secondChessProgram, "sd", appData.secondDirectory,\r
7096                   secondChessProgramNames);\r
7097     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
7098     InitComboStringsFromOption(hwndCombo, icsNames);    \r
7099     sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
7100     if (*appData.icsHelper != NULLCHAR) {\r
7101       char *q = QuoteForFilename(appData.icsHelper);\r
7102       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
7103     }\r
7104     if (*appData.icsHost == NULLCHAR) {\r
7105       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
7106       /*SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, (LPARAM) 0); !!too soon */\r
7107     } else if (SendMessage(hwndCombo, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) buf) == CB_ERR) {\r
7108       SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0);\r
7109       SendMessage(hwndCombo, WM_SETTEXT, (WPARAM) 0, (LPARAM) buf);\r
7110     }\r
7111 \r
7112     if (appData.icsActive) {\r
7113       CheckDlgButton(hDlg, OPT_ChessServer, BST_CHECKED);\r
7114     }\r
7115     else if (appData.noChessProgram) {\r
7116       CheckDlgButton(hDlg, OPT_View, BST_CHECKED);\r
7117     }\r
7118     else {\r
7119       CheckDlgButton(hDlg, OPT_ChessEngine, BST_CHECKED);\r
7120     }\r
7121 \r
7122     SetStartupDialogEnables(hDlg);\r
7123     return TRUE;\r
7124 \r
7125   case WM_COMMAND:\r
7126     switch (LOWORD(wParam)) {\r
7127     case IDOK:\r
7128       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
7129         strcpy(buf, "/fcp=");\r
7130         GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7131         p = buf;\r
7132         ParseArgs(StringGet, &p);\r
7133         strcpy(buf, "/scp=");\r
7134         GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7135         p = buf;\r
7136         ParseArgs(StringGet, &p);\r
7137         appData.noChessProgram = FALSE;\r
7138         appData.icsActive = FALSE;\r
7139       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
7140         strcpy(buf, "/ics /icshost=");\r
7141         GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7142         p = buf;\r
7143         ParseArgs(StringGet, &p);\r
7144         if (appData.zippyPlay) {\r
7145           strcpy(buf, "/fcp=");\r
7146           GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
7147           p = buf;\r
7148           ParseArgs(StringGet, &p);\r
7149         }\r
7150       } else if (IsDlgButtonChecked(hDlg, OPT_View)) {\r
7151         appData.noChessProgram = TRUE;\r
7152         appData.icsActive = FALSE;\r
7153       } else {\r
7154         MessageBox(hDlg, "Choose an option, or cancel to exit",\r
7155                    "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
7156         return TRUE;\r
7157       }\r
7158       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
7159         GetDlgItemText(hDlg, OPT_AdditionalOptions, buf, sizeof(buf));\r
7160         p = buf;\r
7161         ParseArgs(StringGet, &p);\r
7162       }\r
7163       EndDialog(hDlg, TRUE);\r
7164       return TRUE;\r
7165 \r
7166     case IDCANCEL:\r
7167       ExitEvent(0);\r
7168       return TRUE;\r
7169 \r
7170     case IDM_HELPCONTENTS:\r
7171       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
7172         MessageBox (GetFocus(),\r
7173                     "Unable to activate help",\r
7174                     szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
7175       }\r
7176       break;\r
7177 \r
7178     default:\r
7179       SetStartupDialogEnables(hDlg);\r
7180       break;\r
7181     }\r
7182     break;\r
7183   }\r
7184   return FALSE;\r
7185 }\r
7186 \r
7187 /*---------------------------------------------------------------------------*\\r
7188  *\r
7189  * About box dialog functions\r
7190  *\r
7191 \*---------------------------------------------------------------------------*/\r
7192 \r
7193 /* Process messages for "About" dialog box */\r
7194 LRESULT CALLBACK\r
7195 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7196 {\r
7197   switch (message) {\r
7198   case WM_INITDIALOG: /* message: initialize dialog box */\r
7199     /* Center the dialog over the application window */\r
7200     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
7201     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
7202     JAWS_COPYRIGHT\r
7203     return (TRUE);\r
7204 \r
7205   case WM_COMMAND: /* message: received a command */\r
7206     if (LOWORD(wParam) == IDOK /* "OK" box selected? */\r
7207         || LOWORD(wParam) == IDCANCEL) { /* System menu close command? */\r
7208       EndDialog(hDlg, TRUE); /* Exit the dialog */\r
7209       return (TRUE);\r
7210     }\r
7211     break;\r
7212   }\r
7213   return (FALSE);\r
7214 }\r
7215 \r
7216 /*---------------------------------------------------------------------------*\\r
7217  *\r
7218  * Comment Dialog functions\r
7219  *\r
7220 \*---------------------------------------------------------------------------*/\r
7221 \r
7222 LRESULT CALLBACK\r
7223 CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7224 {\r
7225   static HANDLE hwndText = NULL;\r
7226   int len, newSizeX, newSizeY, flags;\r
7227   static int sizeX, sizeY;\r
7228   char *str;\r
7229   RECT rect;\r
7230   MINMAXINFO *mmi;\r
7231 \r
7232   switch (message) {\r
7233   case WM_INITDIALOG: /* message: initialize dialog box */\r
7234     /* Initialize the dialog items */\r
7235     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7236     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
7237     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
7238     EnableWindow(GetDlgItem(hDlg, OPT_ClearComment), editComment);\r
7239     EnableWindow(GetDlgItem(hDlg, OPT_EditComment), !editComment);\r
7240     SendMessage(hwndText, EM_SETREADONLY, !editComment, 0);\r
7241     SetWindowText(hDlg, commentTitle);\r
7242     if (editComment) {\r
7243       SetFocus(hwndText);\r
7244     } else {\r
7245       SetFocus(GetDlgItem(hDlg, IDOK));\r
7246     }\r
7247     SendMessage(GetDlgItem(hDlg, OPT_CommentText),\r
7248                 WM_SETFONT, (WPARAM)font[boardSize][COMMENT_FONT]->hf,\r
7249                 MAKELPARAM(FALSE, 0));\r
7250     /* Size and position the dialog */\r
7251     if (!commentDialog) {\r
7252       commentDialog = hDlg;\r
7253       flags = SWP_NOZORDER;\r
7254       GetClientRect(hDlg, &rect);\r
7255       sizeX = rect.right;\r
7256       sizeY = rect.bottom;\r
7257       if (commentX != CW_USEDEFAULT && commentY != CW_USEDEFAULT &&\r
7258           commentW != CW_USEDEFAULT && commentH != CW_USEDEFAULT) {\r
7259         WINDOWPLACEMENT wp;\r
7260         EnsureOnScreen(&commentX, &commentY, 0, 0);\r
7261         wp.length = sizeof(WINDOWPLACEMENT);\r
7262         wp.flags = 0;\r
7263         wp.showCmd = SW_SHOW;\r
7264         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
7265         wp.rcNormalPosition.left = commentX;\r
7266         wp.rcNormalPosition.right = commentX + commentW;\r
7267         wp.rcNormalPosition.top = commentY;\r
7268         wp.rcNormalPosition.bottom = commentY + commentH;\r
7269         SetWindowPlacement(hDlg, &wp);\r
7270 \r
7271         GetClientRect(hDlg, &rect);\r
7272         newSizeX = rect.right;\r
7273         newSizeY = rect.bottom;\r
7274         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
7275                               newSizeX, newSizeY);\r
7276         sizeX = newSizeX;\r
7277         sizeY = newSizeY;\r
7278       }\r
7279     }\r
7280     return FALSE;\r
7281 \r
7282   case WM_COMMAND: /* message: received a command */\r
7283     switch (LOWORD(wParam)) {\r
7284     case IDOK:\r
7285       if (editComment) {\r
7286         char *p, *q;\r
7287         /* Read changed options from the dialog box */\r
7288         hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
7289         len = GetWindowTextLength(hwndText);\r
7290         str = (char *) malloc(len + 1);\r
7291         GetWindowText(hwndText, str, len + 1);\r
7292         p = q = str;\r
7293         while (*q) {\r
7294           if (*q == '\r')\r
7295             q++;\r
7296           else\r
7297             *p++ = *q++;\r
7298         }\r
7299         *p = NULLCHAR;\r
7300         ReplaceComment(commentIndex, str);\r
7301         free(str);\r
7302       }\r
7303       CommentPopDown();\r
7304       return TRUE;\r
7305 \r
7306     case IDCANCEL:\r
7307     case OPT_CancelComment:\r
7308       CommentPopDown();\r
7309       return TRUE;\r
7310 \r
7311     case OPT_ClearComment:\r
7312       SetDlgItemText(hDlg, OPT_CommentText, "");\r
7313       break;\r
7314 \r
7315     case OPT_EditComment:\r
7316       EditCommentEvent();\r
7317       return TRUE;\r
7318 \r
7319     default:\r
7320       break;\r
7321     }\r
7322     break;\r
7323 \r
7324   case WM_SIZE:\r
7325     newSizeX = LOWORD(lParam);\r
7326     newSizeY = HIWORD(lParam);\r
7327     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
7328     sizeX = newSizeX;\r
7329     sizeY = newSizeY;\r
7330     break;\r
7331 \r
7332   case WM_GETMINMAXINFO:\r
7333     /* Prevent resizing window too small */\r
7334     mmi = (MINMAXINFO *) lParam;\r
7335     mmi->ptMinTrackSize.x = 100;\r
7336     mmi->ptMinTrackSize.y = 100;\r
7337     break;\r
7338   }\r
7339   return FALSE;\r
7340 }\r
7341 \r
7342 VOID\r
7343 EitherCommentPopUp(int index, char *title, char *str, BOOLEAN edit)\r
7344 {\r
7345   FARPROC lpProc;\r
7346   char *p, *q;\r
7347 \r
7348   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, edit ? MF_CHECKED : MF_UNCHECKED);\r
7349 \r
7350   if (str == NULL) str = "";\r
7351   p = (char *) malloc(2 * strlen(str) + 2);\r
7352   q = p;\r
7353   while (*str) {\r
7354     if (*str == '\n') *q++ = '\r';\r
7355     *q++ = *str++;\r
7356   }\r
7357   *q = NULLCHAR;\r
7358   if (commentText != NULL) free(commentText);\r
7359 \r
7360   commentIndex = index;\r
7361   commentTitle = title;\r
7362   commentText = p;\r
7363   editComment = edit;\r
7364 \r
7365   if (commentDialog) {\r
7366     SendMessage(commentDialog, WM_INITDIALOG, 0, 0);\r
7367     if (!commentDialogUp) ShowWindow(commentDialog, SW_SHOW);\r
7368   } else {\r
7369     lpProc = MakeProcInstance((FARPROC)CommentDialog, hInst);\r
7370     CreateDialog(hInst, MAKEINTRESOURCE(DLG_EditComment),\r
7371                  hwndMain, (DLGPROC)lpProc);\r
7372     FreeProcInstance(lpProc);\r
7373   }\r
7374   commentDialogUp = TRUE;\r
7375 }\r
7376 \r
7377 \r
7378 /*---------------------------------------------------------------------------*\\r
7379  *\r
7380  * Type-in move dialog functions\r
7381  * \r
7382 \*---------------------------------------------------------------------------*/\r
7383 \r
7384 LRESULT CALLBACK\r
7385 TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7386 {\r
7387   char move[MSG_SIZ];\r
7388   HWND hInput;\r
7389   ChessMove moveType;\r
7390   int fromX, fromY, toX, toY;\r
7391   char promoChar;\r
7392 \r
7393   switch (message) {\r
7394   case WM_INITDIALOG:\r
7395     move[0] = (char) lParam;\r
7396     move[1] = NULLCHAR;\r
7397     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7398     hInput = GetDlgItem(hDlg, OPT_Move);\r
7399     SetWindowText(hInput, move);\r
7400     SetFocus(hInput);\r
7401     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7402     return FALSE;\r
7403 \r
7404   case WM_COMMAND:\r
7405     switch (LOWORD(wParam)) {\r
7406     case IDOK:\r
7407       GetDlgItemText(hDlg, OPT_Move, move, sizeof(move));\r
7408       { int n; Board board;\r
7409         // [HGM] FENedit\r
7410         if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {\r
7411                 EditPositionPasteFEN(move);\r
7412                 EndDialog(hDlg, TRUE);\r
7413                 return TRUE;\r
7414         }\r
7415         // [HGM] movenum: allow move number to be typed in any mode\r
7416         if(sscanf(move, "%d", &n) == 1 && n != 0 ) {\r
7417           currentMove = 2*n-1;\r
7418           if(currentMove > forwardMostMove)  currentMove = forwardMostMove;\r
7419           if(currentMove < backwardMostMove) currentMove = backwardMostMove;\r
7420           EndDialog(hDlg, TRUE);\r
7421           DrawPosition(TRUE, boards[currentMove]);\r
7422           if(currentMove > backwardMostMove) DisplayMove(currentMove - 1);\r
7423           else DisplayMessage("", "");\r
7424           return TRUE;\r
7425         }\r
7426       }\r
7427       if (gameMode != EditGame && currentMove != forwardMostMove && \r
7428         gameMode != Training) {\r
7429         DisplayMoveError("Displayed move is not current");\r
7430       } else {\r
7431 //      GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
7432         int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7433           &moveType, &fromX, &fromY, &toX, &toY, &promoChar);\r
7434         if(!ok && move[0] >= 'a') { move[0] += 'A' - 'a'; ok = 2; } // [HGM] try also capitalized\r
7435         if (ok==1 || ok && ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
7436           &moveType, &fromX, &fromY, &toX, &toY, &promoChar)) {\r
7437           if (gameMode != Training)\r
7438               forwardMostMove = currentMove;\r
7439           UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
7440         } else {\r
7441           DisplayMoveError("Could not parse move");\r
7442         }\r
7443       }\r
7444       EndDialog(hDlg, TRUE);\r
7445       return TRUE;\r
7446     case IDCANCEL:\r
7447       EndDialog(hDlg, FALSE);\r
7448       return TRUE;\r
7449     default:\r
7450       break;\r
7451     }\r
7452     break;\r
7453   }\r
7454   return FALSE;\r
7455 }\r
7456 \r
7457 VOID\r
7458 PopUpMoveDialog(char firstchar)\r
7459 {\r
7460     FARPROC lpProc;\r
7461     \r
7462     if ((gameMode == BeginningOfGame && !appData.icsActive) || \r
7463         gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||\r
7464         gameMode == AnalyzeMode || gameMode == EditGame || \r
7465         gameMode == EditPosition || gameMode == IcsExamining ||\r
7466         gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||\r
7467         isdigit(firstchar) && // [HGM] movenum: allow typing in of move nr in 'passive' modes\r
7468                 ( gameMode == AnalyzeFile || gameMode == PlayFromGameFile ||\r
7469                   gameMode == IcsObserving || gameMode == TwoMachinesPlay    ) ||\r
7470         gameMode == Training) {\r
7471       lpProc = MakeProcInstance((FARPROC)TypeInMoveDialog, hInst);\r
7472       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInMove),\r
7473         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7474       FreeProcInstance(lpProc);\r
7475     }\r
7476 }\r
7477 \r
7478 /*---------------------------------------------------------------------------*\\r
7479  *\r
7480  * Type-in name dialog functions\r
7481  * \r
7482 \*---------------------------------------------------------------------------*/\r
7483 \r
7484 LRESULT CALLBACK\r
7485 TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7486 {\r
7487   char move[MSG_SIZ];\r
7488   HWND hInput;\r
7489 \r
7490   switch (message) {\r
7491   case WM_INITDIALOG:\r
7492     move[0] = (char) lParam;\r
7493     move[1] = NULLCHAR;\r
7494     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
7495     hInput = GetDlgItem(hDlg, OPT_Name);\r
7496     SetWindowText(hInput, move);\r
7497     SetFocus(hInput);\r
7498     SendMessage(hInput, EM_SETSEL, (WPARAM)9999, (LPARAM)9999);\r
7499     return FALSE;\r
7500 \r
7501   case WM_COMMAND:\r
7502     switch (LOWORD(wParam)) {\r
7503     case IDOK:\r
7504       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
7505       appData.userName = strdup(move);\r
7506       SetUserLogo();\r
7507 \r
7508       EndDialog(hDlg, TRUE);\r
7509       return TRUE;\r
7510     case IDCANCEL:\r
7511       EndDialog(hDlg, FALSE);\r
7512       return TRUE;\r
7513     default:\r
7514       break;\r
7515     }\r
7516     break;\r
7517   }\r
7518   return FALSE;\r
7519 }\r
7520 \r
7521 VOID\r
7522 PopUpNameDialog(char firstchar)\r
7523 {\r
7524     FARPROC lpProc;\r
7525     \r
7526       lpProc = MakeProcInstance((FARPROC)TypeInNameDialog, hInst);\r
7527       DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_TypeInName),\r
7528         hwndMain, (DLGPROC)lpProc, (LPARAM)firstchar);\r
7529       FreeProcInstance(lpProc);\r
7530 }\r
7531 \r
7532 /*---------------------------------------------------------------------------*\\r
7533  *\r
7534  *  Error dialogs\r
7535  * \r
7536 \*---------------------------------------------------------------------------*/\r
7537 \r
7538 /* Nonmodal error box */\r
7539 LRESULT CALLBACK ErrorDialog(HWND hDlg, UINT message,\r
7540                              WPARAM wParam, LPARAM lParam);\r
7541 \r
7542 VOID\r
7543 ErrorPopUp(char *title, char *content)\r
7544 {\r
7545   FARPROC lpProc;\r
7546   char *p, *q;\r
7547   BOOLEAN modal = hwndMain == NULL;\r
7548 \r
7549   p = content;\r
7550   q = errorMessage;\r
7551   while (*p) {\r
7552     if (*p == '\n') {\r
7553       if (modal) {\r
7554         *q++ = ' ';\r
7555         p++;\r
7556       } else {\r
7557         *q++ = '\r';\r
7558         *q++ = *p++;\r
7559       }\r
7560     } else {\r
7561       *q++ = *p++;\r
7562     }\r
7563   }\r
7564   *q = NULLCHAR;\r
7565   strncpy(errorTitle, title, sizeof(errorTitle));\r
7566   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7567   \r
7568   if (modal) {\r
7569     MessageBox(NULL, errorMessage, errorTitle, MB_OK|MB_ICONEXCLAMATION);\r
7570   } else {\r
7571     lpProc = MakeProcInstance((FARPROC)ErrorDialog, hInst);\r
7572     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7573                  hwndMain, (DLGPROC)lpProc);\r
7574     FreeProcInstance(lpProc);\r
7575   }\r
7576 }\r
7577 \r
7578 VOID\r
7579 ErrorPopDown()\r
7580 {\r
7581   if (!appData.popupMoveErrors && moveErrorMessageUp) DisplayMessage("", "");\r
7582   if (errorDialog == NULL) return;\r
7583   DestroyWindow(errorDialog);\r
7584   errorDialog = NULL;\r
7585 }\r
7586 \r
7587 LRESULT CALLBACK\r
7588 ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7589 {\r
7590   HANDLE hwndText;\r
7591   RECT rChild;\r
7592 \r
7593   switch (message) {\r
7594   case WM_INITDIALOG:\r
7595     GetWindowRect(hDlg, &rChild);\r
7596 \r
7597     /*\r
7598     SetWindowPos(hDlg, NULL, rChild.left,\r
7599       rChild.top + boardRect.top - (rChild.bottom - rChild.top), \r
7600       0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7601     */\r
7602 \r
7603     /* \r
7604         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7605         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7606         and it doesn't work when you resize the dialog.\r
7607         For now, just give it a default position.\r
7608     */\r
7609     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
7610 \r
7611     errorDialog = hDlg;\r
7612     SetWindowText(hDlg, errorTitle);\r
7613     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7614     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7615     return FALSE;\r
7616 \r
7617   case WM_COMMAND:\r
7618     switch (LOWORD(wParam)) {\r
7619     case IDOK:\r
7620     case IDCANCEL:\r
7621       if (errorDialog == hDlg) errorDialog = NULL;\r
7622       DestroyWindow(hDlg);\r
7623       return TRUE;\r
7624 \r
7625     default:\r
7626       break;\r
7627     }\r
7628     break;\r
7629   }\r
7630   return FALSE;\r
7631 }\r
7632 \r
7633 #ifdef GOTHIC\r
7634 HWND gothicDialog = NULL;\r
7635 \r
7636 LRESULT CALLBACK\r
7637 GothicDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
7638 {\r
7639   HANDLE hwndText;\r
7640   RECT rChild;\r
7641   int height = GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYFRAME);\r
7642 \r
7643   switch (message) {\r
7644   case WM_INITDIALOG:\r
7645     GetWindowRect(hDlg, &rChild);\r
7646 \r
7647     SetWindowPos(hDlg, NULL, boardX, boardY-height, winWidth, height,\r
7648                                                              SWP_NOZORDER);\r
7649 \r
7650     /* \r
7651         [AS] It seems that the above code wants to move the dialog up in the "caption\r
7652         area" of the main window, but it uses the dialog height as an hard-coded constant,\r
7653         and it doesn't work when you resize the dialog.\r
7654         For now, just give it a default position.\r
7655     */\r
7656     gothicDialog = hDlg;\r
7657     SetWindowText(hDlg, errorTitle);\r
7658     hwndText = GetDlgItem(hDlg, OPT_ErrorText);\r
7659     SetDlgItemText(hDlg, OPT_ErrorText, errorMessage);\r
7660     return FALSE;\r
7661 \r
7662   case WM_COMMAND:\r
7663     switch (LOWORD(wParam)) {\r
7664     case IDOK:\r
7665     case IDCANCEL:\r
7666       if (errorDialog == hDlg) errorDialog = NULL;\r
7667       DestroyWindow(hDlg);\r
7668       return TRUE;\r
7669 \r
7670     default:\r
7671       break;\r
7672     }\r
7673     break;\r
7674   }\r
7675   return FALSE;\r
7676 }\r
7677 \r
7678 VOID\r
7679 GothicPopUp(char *title, VariantClass variant)\r
7680 {\r
7681   FARPROC lpProc;\r
7682   static char *lastTitle;\r
7683 \r
7684   strncpy(errorTitle, title, sizeof(errorTitle));\r
7685   errorTitle[sizeof(errorTitle) - 1] = '\0';\r
7686 \r
7687   if(lastTitle != title && gothicDialog != NULL) {\r
7688     DestroyWindow(gothicDialog);\r
7689     gothicDialog = NULL;\r
7690   }\r
7691   if(variant != VariantNormal && gothicDialog == NULL) {\r
7692     title = lastTitle;\r
7693     lpProc = MakeProcInstance((FARPROC)GothicDialog, hInst);\r
7694     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Error),\r
7695                  hwndMain, (DLGPROC)lpProc);\r
7696     FreeProcInstance(lpProc);\r
7697   }\r
7698 }\r
7699 #endif\r
7700 \r
7701 /*---------------------------------------------------------------------------*\\r
7702  *\r
7703  *  Ics Interaction console functions\r
7704  *\r
7705 \*---------------------------------------------------------------------------*/\r
7706 \r
7707 #define HISTORY_SIZE 64\r
7708 static char *history[HISTORY_SIZE];\r
7709 int histIn = 0, histP = 0;\r
7710 \r
7711 VOID\r
7712 SaveInHistory(char *cmd)\r
7713 {\r
7714   if (history[histIn] != NULL) {\r
7715     free(history[histIn]);\r
7716     history[histIn] = NULL;\r
7717   }\r
7718   if (*cmd == NULLCHAR) return;\r
7719   history[histIn] = StrSave(cmd);\r
7720   histIn = (histIn + 1) % HISTORY_SIZE;\r
7721   if (history[histIn] != NULL) {\r
7722     free(history[histIn]);\r
7723     history[histIn] = NULL;\r
7724   }\r
7725   histP = histIn;\r
7726 }\r
7727 \r
7728 char *\r
7729 PrevInHistory(char *cmd)\r
7730 {\r
7731   int newhp;\r
7732   if (histP == histIn) {\r
7733     if (history[histIn] != NULL) free(history[histIn]);\r
7734     history[histIn] = StrSave(cmd);\r
7735   }\r
7736   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
7737   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
7738   histP = newhp;\r
7739   return history[histP];\r
7740 }\r
7741 \r
7742 char *\r
7743 NextInHistory()\r
7744 {\r
7745   if (histP == histIn) return NULL;\r
7746   histP = (histP + 1) % HISTORY_SIZE;\r
7747   return history[histP];\r
7748 }\r
7749 \r
7750 typedef struct {\r
7751   char *item;\r
7752   char *command;\r
7753   BOOLEAN getname;\r
7754   BOOLEAN immediate;\r
7755 } IcsTextMenuEntry;\r
7756 #define ICS_TEXT_MENU_SIZE (IDM_CommandXLast - IDM_CommandX + 1)\r
7757 IcsTextMenuEntry icsTextMenuEntry[ICS_TEXT_MENU_SIZE];\r
7758 \r
7759 void\r
7760 ParseIcsTextMenu(char *icsTextMenuString)\r
7761 {\r
7762 //  int flags = 0;\r
7763   IcsTextMenuEntry *e = icsTextMenuEntry;\r
7764   char *p = icsTextMenuString;\r
7765   while (e->item != NULL && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7766     free(e->item);\r
7767     e->item = NULL;\r
7768     if (e->command != NULL) {\r
7769       free(e->command);\r
7770       e->command = NULL;\r
7771     }\r
7772     e++;\r
7773   }\r
7774   e = icsTextMenuEntry;\r
7775   while (*p && e < icsTextMenuEntry + ICS_TEXT_MENU_SIZE) {\r
7776     if (*p == ';' || *p == '\n') {\r
7777       e->item = strdup("-");\r
7778       e->command = NULL;\r
7779       p++;\r
7780     } else if (*p == '-') {\r
7781       e->item = strdup("-");\r
7782       e->command = NULL;\r
7783       p++;\r
7784       if (*p) p++;\r
7785     } else {\r
7786       char *q, *r, *s, *t;\r
7787       char c;\r
7788       q = strchr(p, ',');\r
7789       if (q == NULL) break;\r
7790       *q = NULLCHAR;\r
7791       r = strchr(q + 1, ',');\r
7792       if (r == NULL) break;\r
7793       *r = NULLCHAR;\r
7794       s = strchr(r + 1, ',');\r
7795       if (s == NULL) break;\r
7796       *s = NULLCHAR;\r
7797       c = ';';\r
7798       t = strchr(s + 1, c);\r
7799       if (t == NULL) {\r
7800         c = '\n';\r
7801         t = strchr(s + 1, c);\r
7802       }\r
7803       if (t != NULL) *t = NULLCHAR;\r
7804       e->item = strdup(p);\r
7805       e->command = strdup(q + 1);\r
7806       e->getname = *(r + 1) != '0';\r
7807       e->immediate = *(s + 1) != '0';\r
7808       *q = ',';\r
7809       *r = ',';\r
7810       *s = ',';\r
7811       if (t == NULL) break;\r
7812       *t = c;\r
7813       p = t + 1;\r
7814     }\r
7815     e++;\r
7816   } \r
7817 }\r
7818 \r
7819 HMENU\r
7820 LoadIcsTextMenu(IcsTextMenuEntry *e)\r
7821 {\r
7822   HMENU hmenu, h;\r
7823   int i = 0;\r
7824   hmenu = LoadMenu(hInst, "TextMenu");\r
7825   h = GetSubMenu(hmenu, 0);\r
7826   while (e->item) {\r
7827     if (strcmp(e->item, "-") == 0) {\r
7828       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
7829     } else {\r
7830       if (e->item[0] == '|') {\r
7831         AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
7832                    IDM_CommandX + i, &e->item[1]);\r
7833       } else {\r
7834         AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
7835       }\r
7836     }\r
7837     e++;\r
7838     i++;\r
7839   } \r
7840   return hmenu;\r
7841 }\r
7842 \r
7843 WNDPROC consoleTextWindowProc;\r
7844 \r
7845 void\r
7846 CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)\r
7847 {\r
7848   char buf[MSG_SIZ], name[MSG_SIZ];\r
7849   HWND hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7850   CHARRANGE sel;\r
7851 \r
7852   if (!getname) {\r
7853     SetWindowText(hInput, command);\r
7854     if (immediate) {\r
7855       SendMessage(hInput, WM_CHAR, '\r', 0);\r
7856     } else {\r
7857       sel.cpMin = 999999;\r
7858       sel.cpMax = 999999;\r
7859       SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7860       SetFocus(hInput);\r
7861     }\r
7862     return;\r
7863   }    \r
7864   SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7865   if (sel.cpMin == sel.cpMax) {\r
7866     /* Expand to surrounding word */\r
7867     TEXTRANGE tr;\r
7868     do {\r
7869       tr.chrg.cpMax = sel.cpMin;\r
7870       tr.chrg.cpMin = --sel.cpMin;\r
7871       if (sel.cpMin < 0) break;\r
7872       tr.lpstrText = name;\r
7873       SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7874     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7875     sel.cpMin++;\r
7876 \r
7877     do {\r
7878       tr.chrg.cpMin = sel.cpMax;\r
7879       tr.chrg.cpMax = ++sel.cpMax;\r
7880       tr.lpstrText = name;\r
7881       if (SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr) < 1) break;\r
7882     } while (isalpha(name[0]) || isdigit(name[0]) || name[0] == '-');\r
7883     sel.cpMax--;\r
7884 \r
7885     if (sel.cpMax == sel.cpMin || sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7886       MessageBeep(MB_ICONEXCLAMATION);\r
7887       return;\r
7888     }\r
7889     tr.chrg = sel;\r
7890     tr.lpstrText = name;\r
7891     SendMessage(hwnd, EM_GETTEXTRANGE, 0, (LPARAM) &tr);\r
7892   } else {\r
7893     if (sel.cpMax - sel.cpMin > MSG_SIZ/2) {\r
7894       MessageBeep(MB_ICONEXCLAMATION);\r
7895       return;\r
7896     }\r
7897     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
7898   }\r
7899   if (immediate) {\r
7900     sprintf(buf, "%s %s", command, name);\r
7901     SetWindowText(hInput, buf);\r
7902     SendMessage(hInput, WM_CHAR, '\r', 0);\r
7903   } else {\r
7904     sprintf(buf, "%s %s ", command, name); /* trailing space */\r
7905     SetWindowText(hInput, buf);\r
7906     sel.cpMin = 999999;\r
7907     sel.cpMax = 999999;\r
7908     SendMessage(hInput, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7909     SetFocus(hInput);\r
7910   }\r
7911 }\r
7912 \r
7913 LRESULT CALLBACK \r
7914 ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
7915 {\r
7916   HWND hInput;\r
7917   CHARRANGE sel;\r
7918 \r
7919   switch (message) {\r
7920   case WM_KEYDOWN:\r
7921     if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
7922     switch (wParam) {\r
7923     case VK_PRIOR:\r
7924       SendMessage(hwnd, EM_LINESCROLL, 0, -999999);\r
7925       return 0;\r
7926     case VK_NEXT:\r
7927       sel.cpMin = 999999;\r
7928       sel.cpMax = 999999;\r
7929       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7930       SendMessage(hwnd, EM_SCROLLCARET, 0, 0);\r
7931       return 0;\r
7932     }\r
7933     break;\r
7934   case WM_CHAR:\r
7935    if(wParam != '\022') {\r
7936     if (wParam == '\t') {\r
7937       if (GetKeyState(VK_SHIFT) < 0) {\r
7938         /* shifted */\r
7939         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
7940         if (buttonDesc[0].hwnd) {\r
7941           SetFocus(buttonDesc[0].hwnd);\r
7942         } else {\r
7943           SetFocus(hwndMain);\r
7944         }\r
7945       } else {\r
7946         /* unshifted */\r
7947         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput));\r
7948       }\r
7949     } else {\r
7950       hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7951       JAWS_DELETE( SetFocus(hInput); )\r
7952       SendMessage(hInput, message, wParam, lParam);\r
7953     }\r
7954     return 0;\r
7955    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
7956   case WM_RBUTTONUP:\r
7957     if (GetKeyState(VK_SHIFT) & ~1) {\r
7958       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
7959         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7960     } else {\r
7961       POINT pt;\r
7962       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
7963       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7964       if (sel.cpMin == sel.cpMax) {\r
7965         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
7966         EnableMenuItem(hmenu, IDM_QuickPaste, MF_BYCOMMAND|MF_GRAYED);\r
7967       }\r
7968       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
7969         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
7970       }\r
7971       pt.x = LOWORD(lParam);\r
7972       pt.y = HIWORD(lParam);\r
7973       MenuPopup(hwnd, pt, hmenu, -1);\r
7974     }\r
7975     return 0;\r
7976   case WM_PASTE:\r
7977     hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
7978     SetFocus(hInput);\r
7979     return SendMessage(hInput, message, wParam, lParam);\r
7980   case WM_MBUTTONDOWN:\r
7981     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
7982   case WM_RBUTTONDOWN:\r
7983     if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
7984       /* Move selection here if it was empty */\r
7985       POINT pt;\r
7986       pt.x = LOWORD(lParam);\r
7987       pt.y = HIWORD(lParam);\r
7988       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
7989       if (sel.cpMin == sel.cpMax) {\r
7990         sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
7991         sel.cpMax = sel.cpMin;\r
7992         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
7993       }\r
7994       SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
7995     }\r
7996     return 0;\r
7997   case WM_COMMAND:\r
7998     switch (LOWORD(wParam)) {\r
7999     case IDM_QuickPaste:\r
8000       {\r
8001         SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8002         if (sel.cpMin == sel.cpMax) {\r
8003           MessageBeep(MB_ICONEXCLAMATION);\r
8004           return 0;\r
8005         }\r
8006         SendMessage(hwnd, WM_COPY, 0, 0);\r
8007         hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
8008         SendMessage(hInput, WM_PASTE, 0, 0);\r
8009         SetFocus(hInput);\r
8010         return 0;\r
8011       }\r
8012     case IDM_Cut:\r
8013       SendMessage(hwnd, WM_CUT, 0, 0);\r
8014       return 0;\r
8015     case IDM_Paste:\r
8016       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8017       return 0;\r
8018     case IDM_Copy:\r
8019       SendMessage(hwnd, WM_COPY, 0, 0);\r
8020       return 0;\r
8021     default:\r
8022       {\r
8023         int i = LOWORD(wParam) - IDM_CommandX;\r
8024         if (i >= 0 && i < ICS_TEXT_MENU_SIZE &&\r
8025             icsTextMenuEntry[i].command != NULL) {\r
8026           CommandX(hwnd, icsTextMenuEntry[i].command,\r
8027                    icsTextMenuEntry[i].getname,\r
8028                    icsTextMenuEntry[i].immediate);\r
8029           return 0;\r
8030         }\r
8031       }\r
8032       break;\r
8033     }\r
8034     break;\r
8035   }\r
8036   return (*consoleTextWindowProc)(hwnd, message, wParam, lParam);\r
8037 }\r
8038 \r
8039 WNDPROC consoleInputWindowProc;\r
8040 \r
8041 LRESULT CALLBACK\r
8042 ConsoleInputSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8043 {\r
8044   char buf[MSG_SIZ];\r
8045   char *p;\r
8046   static BOOL sendNextChar = FALSE;\r
8047   static BOOL quoteNextChar = FALSE;\r
8048   InputSource *is = consoleInputSource;\r
8049   CHARFORMAT cf;\r
8050   CHARRANGE sel;\r
8051 \r
8052   switch (message) {\r
8053   case WM_CHAR:\r
8054     if (!appData.localLineEditing || sendNextChar) {\r
8055       is->buf[0] = (CHAR) wParam;\r
8056       is->count = 1;\r
8057       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8058       sendNextChar = FALSE;\r
8059       return 0;\r
8060     }\r
8061     if (quoteNextChar) {\r
8062       buf[0] = (char) wParam;\r
8063       buf[1] = NULLCHAR;\r
8064       SendMessage(hwnd, EM_REPLACESEL, TRUE, (LPARAM) buf);\r
8065       quoteNextChar = FALSE;\r
8066       return 0;\r
8067     }\r
8068     switch (wParam) {\r
8069     case '\r':   /* Enter key */\r
8070       is->count = GetWindowText(hwnd, is->buf, INPUT_SOURCE_BUF_SIZE-1);     \r
8071       if (consoleEcho) SaveInHistory(is->buf);\r
8072       is->buf[is->count++] = '\n';\r
8073       is->buf[is->count] = NULLCHAR;\r
8074       SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8075       if (consoleEcho) {\r
8076         ConsoleOutput(is->buf, is->count, TRUE);\r
8077       } else if (appData.localLineEditing) {\r
8078         ConsoleOutput("\n", 1, TRUE);\r
8079       }\r
8080       /* fall thru */\r
8081     case '\033': /* Escape key */\r
8082       SetWindowText(hwnd, "");\r
8083       cf.cbSize = sizeof(CHARFORMAT);\r
8084       cf.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
8085       if (consoleEcho) {\r
8086         cf.crTextColor = textAttribs[ColorNormal].color;\r
8087       } else {\r
8088         cf.crTextColor = COLOR_ECHOOFF;\r
8089       }\r
8090       cf.dwEffects = textAttribs[ColorNormal].effects;\r
8091       SendMessage(hwnd, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
8092       return 0;\r
8093     case '\t':   /* Tab key */\r
8094       if (GetKeyState(VK_SHIFT) < 0) {\r
8095         /* shifted */\r
8096         SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleText));\r
8097       } else {\r
8098         /* unshifted */\r
8099         if (IsIconic(hwndMain)) ShowWindow(hwndMain, SW_RESTORE);\r
8100         if (buttonDesc[0].hwnd) {\r
8101           SetFocus(buttonDesc[0].hwnd);\r
8102         } else {\r
8103           SetFocus(hwndMain);\r
8104         }\r
8105       }\r
8106       return 0;\r
8107     case '\023': /* Ctrl+S */\r
8108       sendNextChar = TRUE;\r
8109       return 0;\r
8110     case '\021': /* Ctrl+Q */\r
8111       quoteNextChar = TRUE;\r
8112       return 0;\r
8113     JAWS_REPLAY\r
8114     default:\r
8115       break;\r
8116     }\r
8117     break;\r
8118   case WM_KEYDOWN:\r
8119     switch (wParam) {\r
8120     case VK_UP:\r
8121       GetWindowText(hwnd, buf, MSG_SIZ);\r
8122       p = PrevInHistory(buf);\r
8123       if (p != NULL) {\r
8124         SetWindowText(hwnd, p);\r
8125         sel.cpMin = 999999;\r
8126         sel.cpMax = 999999;\r
8127         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8128         return 0;\r
8129       }\r
8130       break;\r
8131     case VK_DOWN:\r
8132       p = NextInHistory();\r
8133       if (p != NULL) {\r
8134         SetWindowText(hwnd, p);\r
8135         sel.cpMin = 999999;\r
8136         sel.cpMax = 999999;\r
8137         SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8138         return 0;\r
8139       }\r
8140       break;\r
8141     case VK_HOME:\r
8142     case VK_END:\r
8143       if (!(GetKeyState(VK_CONTROL) & ~1)) break;\r
8144       /* fall thru */\r
8145     case VK_PRIOR:\r
8146     case VK_NEXT:\r
8147       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, message, wParam, lParam);\r
8148       return 0;\r
8149     }\r
8150     break;\r
8151   case WM_MBUTTONDOWN:\r
8152     SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8153       WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8154     break;\r
8155   case WM_RBUTTONUP:\r
8156     if (GetKeyState(VK_SHIFT) & ~1) {\r
8157       SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
8158         WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
8159     } else {\r
8160       POINT pt;\r
8161       HMENU hmenu;\r
8162       hmenu = LoadMenu(hInst, "InputMenu");\r
8163       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
8164       if (sel.cpMin == sel.cpMax) {\r
8165         EnableMenuItem(hmenu, IDM_Copy, MF_BYCOMMAND|MF_GRAYED);\r
8166         EnableMenuItem(hmenu, IDM_Cut, MF_BYCOMMAND|MF_GRAYED);\r
8167       }\r
8168       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
8169         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
8170       }\r
8171       pt.x = LOWORD(lParam);\r
8172       pt.y = HIWORD(lParam);\r
8173       MenuPopup(hwnd, pt, hmenu, -1);\r
8174     }\r
8175     return 0;\r
8176   case WM_COMMAND:\r
8177     switch (LOWORD(wParam)) { \r
8178     case IDM_Undo:\r
8179       SendMessage(hwnd, EM_UNDO, 0, 0);\r
8180       return 0;\r
8181     case IDM_SelectAll:\r
8182       sel.cpMin = 0;\r
8183       sel.cpMax = -1; /*999999?*/\r
8184       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8185       return 0;\r
8186     case IDM_Cut:\r
8187       SendMessage(hwnd, WM_CUT, 0, 0);\r
8188       return 0;\r
8189     case IDM_Paste:\r
8190       SendMessage(hwnd, WM_PASTE, 0, 0);\r
8191       return 0;\r
8192     case IDM_Copy:\r
8193       SendMessage(hwnd, WM_COPY, 0, 0);\r
8194       return 0;\r
8195     }\r
8196     break;\r
8197   }\r
8198   return (*consoleInputWindowProc)(hwnd, message, wParam, lParam);\r
8199 }\r
8200 \r
8201 #define CO_MAX  100000\r
8202 #define CO_TRIM   1000\r
8203 \r
8204 LRESULT CALLBACK\r
8205 ConsoleWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
8206 {\r
8207   static SnapData sd;\r
8208   static HWND hText, hInput /*, hFocus*/;\r
8209 //  InputSource *is = consoleInputSource;\r
8210   RECT rect;\r
8211   static int sizeX, sizeY;\r
8212   int newSizeX, newSizeY;\r
8213   MINMAXINFO *mmi;\r
8214 \r
8215   switch (message) {\r
8216   case WM_INITDIALOG: /* message: initialize dialog box */\r
8217     hwndConsole = hDlg;\r
8218     hText = GetDlgItem(hDlg, OPT_ConsoleText);\r
8219     hInput = GetDlgItem(hDlg, OPT_ConsoleInput);\r
8220     SetFocus(hInput);\r
8221     consoleTextWindowProc = (WNDPROC)\r
8222       SetWindowLong(hText, GWL_WNDPROC, (LONG) ConsoleTextSubclass);\r
8223     SendMessage(hText, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8224     consoleInputWindowProc = (WNDPROC)\r
8225       SetWindowLong(hInput, GWL_WNDPROC, (LONG) ConsoleInputSubclass);\r
8226     SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
8227     Colorize(ColorNormal, TRUE);\r
8228     SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM) &consoleCF);\r
8229     ChangedConsoleFont();\r
8230     GetClientRect(hDlg, &rect);\r
8231     sizeX = rect.right;\r
8232     sizeY = rect.bottom;\r
8233     if (wpConsole.x != CW_USEDEFAULT && wpConsole.y != CW_USEDEFAULT &&\r
8234         wpConsole.width != CW_USEDEFAULT && wpConsole.height != CW_USEDEFAULT) {\r
8235       WINDOWPLACEMENT wp;\r
8236       EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8237       wp.length = sizeof(WINDOWPLACEMENT);\r
8238       wp.flags = 0;\r
8239       wp.showCmd = SW_SHOW;\r
8240       wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8241       wp.rcNormalPosition.left = wpConsole.x;\r
8242       wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8243       wp.rcNormalPosition.top = wpConsole.y;\r
8244       wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8245       SetWindowPlacement(hDlg, &wp);\r
8246     }\r
8247 #if 1\r
8248    // [HGM] Chessknight's change 2004-07-13\r
8249    else { /* Determine Defaults */\r
8250        WINDOWPLACEMENT wp;\r
8251        wpConsole.x = winWidth + 1;\r
8252        wpConsole.y = boardY;\r
8253        wpConsole.width = screenWidth -  winWidth;\r
8254        wpConsole.height = winHeight;\r
8255        EnsureOnScreen(&wpConsole.x, &wpConsole.y, 0, 0);\r
8256        wp.length = sizeof(WINDOWPLACEMENT);\r
8257        wp.flags = 0;\r
8258        wp.showCmd = SW_SHOW;\r
8259        wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
8260        wp.rcNormalPosition.left = wpConsole.x;\r
8261        wp.rcNormalPosition.right = wpConsole.x + wpConsole.width;\r
8262        wp.rcNormalPosition.top = wpConsole.y;\r
8263        wp.rcNormalPosition.bottom = wpConsole.y + wpConsole.height;\r
8264        SetWindowPlacement(hDlg, &wp);\r
8265     }\r
8266 #endif\r
8267     return FALSE;\r
8268 \r
8269   case WM_SETFOCUS:\r
8270     SetFocus(hInput);\r
8271     return 0;\r
8272 \r
8273   case WM_CLOSE:\r
8274     ExitEvent(0);\r
8275     /* not reached */\r
8276     break;\r
8277 \r
8278   case WM_SIZE:\r
8279     if (IsIconic(hDlg)) break;\r
8280     newSizeX = LOWORD(lParam);\r
8281     newSizeY = HIWORD(lParam);\r
8282     if (sizeX != newSizeX || sizeY != newSizeY) {\r
8283       RECT rectText, rectInput;\r
8284       POINT pt;\r
8285       int newTextHeight, newTextWidth;\r
8286       GetWindowRect(hText, &rectText);\r
8287       newTextWidth = rectText.right - rectText.left + newSizeX - sizeX;\r
8288       newTextHeight = rectText.bottom - rectText.top + newSizeY - sizeY;\r
8289       if (newTextHeight < 0) {\r
8290         newSizeY += -newTextHeight;\r
8291         newTextHeight = 0;\r
8292       }\r
8293       SetWindowPos(hText, NULL, 0, 0,\r
8294         newTextWidth, newTextHeight, SWP_NOZORDER|SWP_NOMOVE);\r
8295       GetWindowRect(hInput, &rectInput); /* gives screen coords */\r
8296       pt.x = rectInput.left;\r
8297       pt.y = rectInput.top + newSizeY - sizeY;\r
8298       ScreenToClient(hDlg, &pt);\r
8299       SetWindowPos(hInput, NULL, \r
8300         pt.x, pt.y, /* needs client coords */   \r
8301         rectInput.right - rectInput.left + newSizeX - sizeX,\r
8302         rectInput.bottom - rectInput.top, SWP_NOZORDER);\r
8303     }\r
8304     sizeX = newSizeX;\r
8305     sizeY = newSizeY;\r
8306     break;\r
8307 \r
8308   case WM_GETMINMAXINFO:\r
8309     /* Prevent resizing window too small */\r
8310     mmi = (MINMAXINFO *) lParam;\r
8311     mmi->ptMinTrackSize.x = 100;\r
8312     mmi->ptMinTrackSize.y = 100;\r
8313     break;\r
8314 \r
8315   /* [AS] Snapping */\r
8316   case WM_ENTERSIZEMOVE:\r
8317     return OnEnterSizeMove( &sd, hDlg, wParam, lParam );\r
8318 \r
8319   case WM_SIZING:\r
8320     return OnSizing( &sd, hDlg, wParam, lParam );\r
8321 \r
8322   case WM_MOVING:\r
8323     return OnMoving( &sd, hDlg, wParam, lParam );\r
8324 \r
8325   case WM_EXITSIZEMOVE:\r
8326     return OnExitSizeMove( &sd, hDlg, wParam, lParam );\r
8327   }\r
8328 \r
8329   return DefWindowProc(hDlg, message, wParam, lParam);\r
8330 }\r
8331 \r
8332 \r
8333 VOID\r
8334 ConsoleCreate()\r
8335 {\r
8336   HWND hCons;\r
8337   if (hwndConsole) return;\r
8338   hCons = CreateDialog(hInst, szConsoleName, 0, NULL);\r
8339   SendMessage(hCons, WM_INITDIALOG, 0, 0);\r
8340 }\r
8341 \r
8342 \r
8343 VOID\r
8344 ConsoleOutput(char* data, int length, int forceVisible)\r
8345 {\r
8346   HWND hText;\r
8347   int trim, exlen;\r
8348   char *p, *q;\r
8349   char buf[CO_MAX+1];\r
8350   POINT pEnd;\r
8351   RECT rect;\r
8352   static int delayLF = 0;\r
8353   CHARRANGE savesel, sel;\r
8354 \r
8355   if (hwndConsole == NULL || length > CO_MAX-100 || length == 0) return;\r
8356   p = data;\r
8357   q = buf;\r
8358   if (delayLF) {\r
8359     *q++ = '\r';\r
8360     *q++ = '\n';\r
8361     delayLF = 0;\r
8362   }\r
8363   while (length--) {\r
8364     if (*p == '\n') {\r
8365       if (*++p) {\r
8366         *q++ = '\r';\r
8367         *q++ = '\n';\r
8368       } else {\r
8369         delayLF = 1;\r
8370       }\r
8371     } else if (*p == '\007') {\r
8372        MyPlaySound(&sounds[(int)SoundBell]);\r
8373        p++;\r
8374     } else {\r
8375       *q++ = *p++;\r
8376     }\r
8377   }\r
8378   *q = NULLCHAR;\r
8379   hText = GetDlgItem(hwndConsole, OPT_ConsoleText);\r
8380   SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8381   /* Save current selection */\r
8382   SendMessage(hText, EM_EXGETSEL, 0, (LPARAM)&savesel);\r
8383   exlen = GetWindowTextLength(hText);\r
8384   /* Find out whether current end of text is visible */\r
8385   SendMessage(hText, EM_GETRECT, 0, (LPARAM) &rect);\r
8386   SendMessage(hText, EM_POSFROMCHAR, (WPARAM) &pEnd, exlen);\r
8387   /* Trim existing text if it's too long */\r
8388   if (exlen + (q - buf) > CO_MAX) {\r
8389     trim = (CO_TRIM > (q - buf)) ? CO_TRIM : (q - buf);\r
8390     sel.cpMin = 0;\r
8391     sel.cpMax = trim;\r
8392     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8393     SendMessage(hText, EM_REPLACESEL, 0, (LPARAM)"");\r
8394     exlen -= trim;\r
8395     savesel.cpMin -= trim;\r
8396     savesel.cpMax -= trim;\r
8397     if (exlen < 0) exlen = 0;\r
8398     if (savesel.cpMin < 0) savesel.cpMin = 0;\r
8399     if (savesel.cpMax < savesel.cpMin) savesel.cpMax = savesel.cpMin;\r
8400   }\r
8401   /* Append the new text */\r
8402   sel.cpMin = exlen;\r
8403   sel.cpMax = exlen;\r
8404   SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8405   SendMessage(hText, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&consoleCF);\r
8406   SendMessage(hText, EM_REPLACESEL, 0, (LPARAM) buf);\r
8407   if (forceVisible || exlen == 0 ||\r
8408       (rect.left <= pEnd.x && pEnd.x < rect.right &&\r
8409        rect.top <= pEnd.y && pEnd.y < rect.bottom)) {\r
8410     /* Scroll to make new end of text visible if old end of text\r
8411        was visible or new text is an echo of user typein */\r
8412     sel.cpMin = 9999999;\r
8413     sel.cpMax = 9999999;\r
8414     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8415     SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8416     SendMessage(hText, EM_SCROLLCARET, 0, 0);\r
8417     SendMessage(hText, EM_HIDESELECTION, TRUE, FALSE);\r
8418   }\r
8419   if (savesel.cpMax == exlen || forceVisible) {\r
8420     /* Move insert point to new end of text if it was at the old\r
8421        end of text or if the new text is an echo of user typein */\r
8422     sel.cpMin = 9999999;\r
8423     sel.cpMax = 9999999;\r
8424     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&sel);\r
8425   } else {\r
8426     /* Restore previous selection */\r
8427     SendMessage(hText, EM_EXSETSEL, 0, (LPARAM)&savesel);\r
8428   }\r
8429   SendMessage(hText, EM_HIDESELECTION, FALSE, FALSE);\r
8430 }\r
8431 \r
8432 /*---------*/\r
8433 \r
8434 \r
8435 void\r
8436 DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)\r
8437 {\r
8438   char buf[100];\r
8439   char *str;\r
8440   COLORREF oldFg, oldBg;\r
8441   HFONT oldFont;\r
8442   RECT rect;\r
8443 \r
8444   if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
8445 \r
8446   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8447   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8448   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8449 \r
8450   rect.left = x;\r
8451   rect.right = x + squareSize;\r
8452   rect.top  = y;\r
8453   rect.bottom = y + squareSize;\r
8454   str = buf;\r
8455 \r
8456   ExtTextOut(hdc, x + MESSAGE_LINE_LEFTMARGIN\r
8457                     + (rightAlign ? (squareSize*2)/3 : 0),\r
8458              y, ETO_CLIPPED|ETO_OPAQUE,\r
8459              &rect, str, strlen(str), NULL);\r
8460 \r
8461   (void) SetTextColor(hdc, oldFg);\r
8462   (void) SetBkColor(hdc, oldBg);\r
8463   (void) SelectObject(hdc, oldFont);\r
8464 }\r
8465 \r
8466 void\r
8467 DisplayAClock(HDC hdc, int timeRemaining, int highlight,\r
8468               RECT *rect, char *color, char *flagFell)\r
8469 {\r
8470   char buf[100];\r
8471   char *str;\r
8472   COLORREF oldFg, oldBg;\r
8473   HFONT oldFont;\r
8474 \r
8475   if (appData.clockMode) {\r
8476     if (tinyLayout)\r
8477       sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
8478     else\r
8479       sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
8480     str = buf;\r
8481   } else {\r
8482     str = color;\r
8483   }\r
8484 \r
8485   if (highlight) {\r
8486     oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
8487     oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
8488   } else {\r
8489     oldFg = SetTextColor(hdc, RGB(0, 0, 0)); /* black */\r
8490     oldBg = SetBkColor(hdc, RGB(255, 255, 255)); /* white */\r
8491   }\r
8492   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
8493 \r
8494   JAWS_SILENCE\r
8495 \r
8496   ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8497              rect->top, ETO_CLIPPED|ETO_OPAQUE,\r
8498              rect, str, strlen(str), NULL);\r
8499   if(logoHeight > 0 && appData.clockMode) {\r
8500       RECT r;\r
8501       sprintf(buf, "%s %s", buf+7, flagFell);\r
8502       r.top = rect->top + logoHeight/2;\r
8503       r.left = rect->left;\r
8504       r.right = rect->right;\r
8505       r.bottom = rect->bottom;\r
8506       ExtTextOut(hdc, rect->left + MESSAGE_LINE_LEFTMARGIN,\r
8507                  r.top, ETO_CLIPPED|ETO_OPAQUE,\r
8508                  &r, str, strlen(str), NULL);\r
8509   }\r
8510   (void) SetTextColor(hdc, oldFg);\r
8511   (void) SetBkColor(hdc, oldBg);\r
8512   (void) SelectObject(hdc, oldFont);\r
8513 }\r
8514 \r
8515 \r
8516 int\r
8517 DoReadFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8518            OVERLAPPED *ovl)\r
8519 {\r
8520   int ok, err;\r
8521 \r
8522   /* [AS]  */\r
8523   if( count <= 0 ) {\r
8524     if (appData.debugMode) {\r
8525       fprintf( debugFP, "DoReadFile: trying to read past end of buffer, overflow = %d\n", count );\r
8526     }\r
8527 \r
8528     return ERROR_INVALID_USER_BUFFER;\r
8529   }\r
8530 \r
8531   ResetEvent(ovl->hEvent);\r
8532   ovl->Offset = ovl->OffsetHigh = 0;\r
8533   ok = ReadFile(hFile, buf, count, outCount, ovl);\r
8534   if (ok) {\r
8535     err = NO_ERROR;\r
8536   } else {\r
8537     err = GetLastError();\r
8538     if (err == ERROR_IO_PENDING) {\r
8539       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8540       if (ok)\r
8541         err = NO_ERROR;\r
8542       else\r
8543         err = GetLastError();\r
8544     }\r
8545   }\r
8546   return err;\r
8547 }\r
8548 \r
8549 int\r
8550 DoWriteFile(HANDLE hFile, char *buf, int count, DWORD *outCount,\r
8551             OVERLAPPED *ovl)\r
8552 {\r
8553   int ok, err;\r
8554 \r
8555   ResetEvent(ovl->hEvent);\r
8556   ovl->Offset = ovl->OffsetHigh = 0;\r
8557   ok = WriteFile(hFile, buf, count, outCount, ovl);\r
8558   if (ok) {\r
8559     err = NO_ERROR;\r
8560   } else {\r
8561     err = GetLastError();\r
8562     if (err == ERROR_IO_PENDING) {\r
8563       ok = GetOverlappedResult(hFile, ovl, outCount, TRUE);\r
8564       if (ok)\r
8565         err = NO_ERROR;\r
8566       else\r
8567         err = GetLastError();\r
8568     }\r
8569   }\r
8570   return err;\r
8571 }\r
8572 \r
8573 /* [AS] If input is line by line and a line exceed the buffer size, force an error */\r
8574 void CheckForInputBufferFull( InputSource * is )\r
8575 {\r
8576     if( is->lineByLine && (is->next - is->buf) >= INPUT_SOURCE_BUF_SIZE ) {\r
8577         /* Look for end of line */\r
8578         char * p = is->buf;\r
8579         \r
8580         while( p < is->next && *p != '\n' ) {\r
8581             p++;\r
8582         }\r
8583 \r
8584         if( p >= is->next ) {\r
8585             if (appData.debugMode) {\r
8586                 fprintf( debugFP, "Input line exceeded buffer size (source id=%lu)\n", is->id );\r
8587             }\r
8588 \r
8589             is->error = ERROR_BROKEN_PIPE; /* [AS] Just any non-successful code! */\r
8590             is->count = (DWORD) -1;\r
8591             is->next = is->buf;\r
8592         }\r
8593     }\r
8594 }\r
8595 \r
8596 DWORD\r
8597 InputThread(LPVOID arg)\r
8598 {\r
8599   InputSource *is;\r
8600   OVERLAPPED ovl;\r
8601 \r
8602   is = (InputSource *) arg;\r
8603   ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
8604   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
8605   while (is->hThread != NULL) {\r
8606     is->error = DoReadFile(is->hFile, is->next,\r
8607                            INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8608                            &is->count, &ovl);\r
8609     if (is->error == NO_ERROR) {\r
8610       is->next += is->count;\r
8611     } else {\r
8612       if (is->error == ERROR_BROKEN_PIPE) {\r
8613         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8614         is->count = 0;\r
8615       } else {\r
8616         is->count = (DWORD) -1;\r
8617         /* [AS] The (is->count <= 0) check below is not useful for unsigned values! */\r
8618         break; \r
8619       }\r
8620     }\r
8621 \r
8622     CheckForInputBufferFull( is );\r
8623 \r
8624     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8625 \r
8626     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8627 \r
8628     if (is->count <= 0) break;  /* Quit on EOF or error */\r
8629   }\r
8630 \r
8631   CloseHandle(ovl.hEvent);\r
8632   CloseHandle(is->hFile);\r
8633 \r
8634   if (appData.debugMode) {\r
8635     fprintf( debugFP, "Input thread terminated (id=%lu, error=%d, count=%ld)\n", is->id, is->error, is->count );\r
8636   }\r
8637 \r
8638   return 0;\r
8639 }\r
8640 \r
8641 \r
8642 /* Windows 95 beta 2 won't let you do overlapped i/o on a console or pipe */\r
8643 DWORD\r
8644 NonOvlInputThread(LPVOID arg)\r
8645 {\r
8646   InputSource *is;\r
8647   char *p, *q;\r
8648   int i;\r
8649   char prev;\r
8650 \r
8651   is = (InputSource *) arg;\r
8652   while (is->hThread != NULL) {\r
8653     is->error = ReadFile(is->hFile, is->next,\r
8654                          INPUT_SOURCE_BUF_SIZE - (is->next - is->buf),\r
8655                          &is->count, NULL) ? NO_ERROR : GetLastError();\r
8656     if (is->error == NO_ERROR) {\r
8657       /* Change CRLF to LF */\r
8658       if (is->next > is->buf) {\r
8659         p = is->next - 1;\r
8660         i = is->count + 1;\r
8661       } else {\r
8662         p = is->next;\r
8663         i = is->count;\r
8664       }\r
8665       q = p;\r
8666       prev = NULLCHAR;\r
8667       while (i > 0) {\r
8668         if (prev == '\r' && *p == '\n') {\r
8669           *(q-1) = '\n';\r
8670           is->count--;\r
8671         } else { \r
8672           *q++ = *p;\r
8673         }\r
8674         prev = *p++;\r
8675         i--;\r
8676       }\r
8677       *q = NULLCHAR;\r
8678       is->next = q;\r
8679     } else {\r
8680       if (is->error == ERROR_BROKEN_PIPE) {\r
8681         /* Correct for MS brain damage.  EOF reading a pipe is not an error. */\r
8682         is->count = 0; \r
8683       } else {\r
8684         is->count = (DWORD) -1;\r
8685       }\r
8686     }\r
8687 \r
8688     CheckForInputBufferFull( is );\r
8689 \r
8690     SendMessage(hwndMain, WM_USER_Input, 0, (LPARAM) is);\r
8691 \r
8692     if( is->count == ((DWORD) -1) ) break; /* [AS] */\r
8693 \r
8694     if (is->count < 0) break;  /* Quit on error */\r
8695   }\r
8696   CloseHandle(is->hFile);\r
8697   return 0;\r
8698 }\r
8699 \r
8700 DWORD\r
8701 SocketInputThread(LPVOID arg)\r
8702 {\r
8703   InputSource *is;\r
8704 \r
8705   is = (InputSource *) arg;\r
8706   while (is->hThread != NULL) {\r
8707     is->count = recv(is->sock, is->buf, INPUT_SOURCE_BUF_SIZE, 0);\r
8708     if ((int)is->count == SOCKET_ERROR) {\r
8709       is->count = (DWORD) -1;\r
8710       is->error = WSAGetLastError();\r
8711     } else {\r
8712       is->error = NO_ERROR;\r
8713       is->next += is->count;\r
8714       if (is->count == 0 && is->second == is) {\r
8715         /* End of file on stderr; quit with no message */\r
8716         break;\r
8717       }\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 EOF or error */\r
8724   }\r
8725   return 0;\r
8726 }\r
8727 \r
8728 VOID\r
8729 InputEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
8730 {\r
8731   InputSource *is;\r
8732 \r
8733   is = (InputSource *) lParam;\r
8734   if (is->lineByLine) {\r
8735     /* Feed in lines one by one */\r
8736     char *p = is->buf;\r
8737     char *q = p;\r
8738     while (q < is->next) {\r
8739       if (*q++ == '\n') {\r
8740         (is->func)(is, is->closure, p, q - p, NO_ERROR);\r
8741         p = q;\r
8742       }\r
8743     }\r
8744     \r
8745     /* Move any partial line to the start of the buffer */\r
8746     q = is->buf;\r
8747     while (p < is->next) {\r
8748       *q++ = *p++;\r
8749     }\r
8750     is->next = q;\r
8751 \r
8752     if (is->error != NO_ERROR || is->count == 0) {\r
8753       /* Notify backend of the error.  Note: If there was a partial\r
8754          line at the end, it is not flushed through. */\r
8755       (is->func)(is, is->closure, is->buf, is->count, is->error);   \r
8756     }\r
8757   } else {\r
8758     /* Feed in the whole chunk of input at once */\r
8759     (is->func)(is, is->closure, is->buf, is->count, is->error);\r
8760     is->next = is->buf;\r
8761   }\r
8762 }\r
8763 \r
8764 /*---------------------------------------------------------------------------*\\r
8765  *\r
8766  *  Menu enables. Used when setting various modes.\r
8767  *\r
8768 \*---------------------------------------------------------------------------*/\r
8769 \r
8770 typedef struct {\r
8771   int item;\r
8772   int flags;\r
8773 } Enables;\r
8774 \r
8775 VOID\r
8776 SetMenuEnables(HMENU hmenu, Enables *enab)\r
8777 {\r
8778   while (enab->item > 0) {\r
8779     (void) EnableMenuItem(hmenu, enab->item, enab->flags);\r
8780     enab++;\r
8781   }\r
8782 }\r
8783 \r
8784 Enables gnuEnables[] = {\r
8785   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8786   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8787   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8788   { IDM_Accept, MF_BYCOMMAND|MF_GRAYED },\r
8789   { IDM_Decline, MF_BYCOMMAND|MF_GRAYED },\r
8790   { IDM_Rematch, MF_BYCOMMAND|MF_GRAYED },\r
8791   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8792   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
8793   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
8794   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8795   { -1, -1 }\r
8796 };\r
8797 \r
8798 Enables icsEnables[] = {\r
8799   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8800   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8801   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8802   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8803   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8804   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
8805   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8806   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8807   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8808   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8809   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8810   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
8811   { -1, -1 }\r
8812 };\r
8813 \r
8814 #ifdef ZIPPY\r
8815 Enables zippyEnables[] = {\r
8816   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8817   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
8818   { IDM_Book, MF_BYCOMMAND|MF_ENABLED },\r
8819   { -1, -1 }\r
8820 };\r
8821 #endif\r
8822 \r
8823 Enables ncpEnables[] = {\r
8824   { IDM_MailMove, MF_BYCOMMAND|MF_GRAYED },\r
8825   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_GRAYED },\r
8826   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8827   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8828   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8829   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
8830   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
8831   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
8832   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
8833   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
8834   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8835   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8836   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
8837   { IDM_Hint, MF_BYCOMMAND|MF_GRAYED },\r
8838   { IDM_Book, MF_BYCOMMAND|MF_GRAYED },\r
8839   { -1, -1 }\r
8840 };\r
8841 \r
8842 Enables trainingOnEnables[] = {\r
8843   { IDM_EditComment, MF_BYCOMMAND|MF_GRAYED },\r
8844   { IDM_Pause, MF_BYCOMMAND|MF_GRAYED },\r
8845   { IDM_Forward, MF_BYCOMMAND|MF_GRAYED },\r
8846   { IDM_Backward, MF_BYCOMMAND|MF_GRAYED },\r
8847   { IDM_ToEnd, MF_BYCOMMAND|MF_GRAYED },\r
8848   { IDM_ToStart, MF_BYCOMMAND|MF_GRAYED },\r
8849   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
8850   { IDM_TruncateGame, MF_BYCOMMAND|MF_GRAYED },\r
8851   { -1, -1 }\r
8852 };\r
8853 \r
8854 Enables trainingOffEnables[] = {\r
8855   { IDM_EditComment, MF_BYCOMMAND|MF_ENABLED },\r
8856   { IDM_Pause, MF_BYCOMMAND|MF_ENABLED },\r
8857   { IDM_Forward, MF_BYCOMMAND|MF_ENABLED },\r
8858   { IDM_Backward, MF_BYCOMMAND|MF_ENABLED },\r
8859   { IDM_ToEnd, MF_BYCOMMAND|MF_ENABLED },\r
8860   { IDM_ToStart, MF_BYCOMMAND|MF_ENABLED },\r
8861   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
8862   { IDM_TruncateGame, MF_BYCOMMAND|MF_ENABLED },\r
8863   { -1, -1 }\r
8864 };\r
8865 \r
8866 /* These modify either ncpEnables or gnuEnables */\r
8867 Enables cmailEnables[] = {\r
8868   { IDM_MailMove, MF_BYCOMMAND|MF_ENABLED },\r
8869   { IDM_ReloadCMailMsg, MF_BYCOMMAND|MF_ENABLED },\r
8870   { ACTION_POS, MF_BYPOSITION|MF_ENABLED },\r
8871   { IDM_CallFlag, MF_BYCOMMAND|MF_GRAYED },\r
8872   { IDM_Draw, MF_BYCOMMAND|MF_ENABLED },\r
8873   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
8874   { IDM_Abort, MF_BYCOMMAND|MF_GRAYED },\r
8875   { -1, -1 }\r
8876 };\r
8877 \r
8878 Enables machineThinkingEnables[] = {\r
8879   { IDM_LoadGame, MF_BYCOMMAND|MF_GRAYED },\r
8880   { IDM_LoadNextGame, MF_BYCOMMAND|MF_GRAYED },\r
8881   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_GRAYED },\r
8882   { IDM_ReloadGame, MF_BYCOMMAND|MF_GRAYED },\r
8883   { IDM_PasteGame, MF_BYCOMMAND|MF_GRAYED },\r
8884   { IDM_LoadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8885   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_GRAYED },\r
8886   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_GRAYED },\r
8887   { IDM_ReloadPosition, MF_BYCOMMAND|MF_GRAYED },\r
8888   { IDM_PastePosition, MF_BYCOMMAND|MF_GRAYED },\r
8889   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
8890   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
8891   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
8892   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
8893   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
8894   { -1, -1 }\r
8895 };\r
8896 \r
8897 Enables userThinkingEnables[] = {\r
8898   { IDM_LoadGame, MF_BYCOMMAND|MF_ENABLED },\r
8899   { IDM_LoadNextGame, MF_BYCOMMAND|MF_ENABLED },\r
8900   { IDM_LoadPrevGame, MF_BYCOMMAND|MF_ENABLED },\r
8901   { IDM_ReloadGame, MF_BYCOMMAND|MF_ENABLED },\r
8902   { IDM_PasteGame, MF_BYCOMMAND|MF_ENABLED },\r
8903   { IDM_LoadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8904   { IDM_LoadNextPosition, MF_BYCOMMAND|MF_ENABLED },\r
8905   { IDM_LoadPrevPosition, MF_BYCOMMAND|MF_ENABLED },\r
8906   { IDM_ReloadPosition, MF_BYCOMMAND|MF_ENABLED },\r
8907   { IDM_PastePosition, MF_BYCOMMAND|MF_ENABLED },\r
8908   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
8909   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
8910   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
8911   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
8912   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
8913   { -1, -1 }\r
8914 };\r
8915 \r
8916 /*---------------------------------------------------------------------------*\\r
8917  *\r
8918  *  Front-end interface functions exported by XBoard.\r
8919  *  Functions appear in same order as prototypes in frontend.h.\r
8920  * \r
8921 \*---------------------------------------------------------------------------*/\r
8922 VOID\r
8923 ModeHighlight()\r
8924 {\r
8925   static UINT prevChecked = 0;\r
8926   static int prevPausing = 0;\r
8927   UINT nowChecked;\r
8928 \r
8929   if (pausing != prevPausing) {\r
8930     prevPausing = pausing;\r
8931     (void) CheckMenuItem(GetMenu(hwndMain), IDM_Pause,\r
8932                          MF_BYCOMMAND|(pausing ? MF_CHECKED : MF_UNCHECKED));\r
8933     if (hwndPause) SetWindowText(hwndPause, pausing ? "C" : "P");\r
8934   }\r
8935 \r
8936   switch (gameMode) {\r
8937   case BeginningOfGame:\r
8938     if (appData.icsActive)\r
8939       nowChecked = IDM_IcsClient;\r
8940     else if (appData.noChessProgram)\r
8941       nowChecked = IDM_EditGame;\r
8942     else\r
8943       nowChecked = IDM_MachineBlack;\r
8944     break;\r
8945   case MachinePlaysBlack:\r
8946     nowChecked = IDM_MachineBlack;\r
8947     break;\r
8948   case MachinePlaysWhite:\r
8949     nowChecked = IDM_MachineWhite;\r
8950     break;\r
8951   case TwoMachinesPlay:\r
8952     nowChecked = IDM_TwoMachines;\r
8953     break;\r
8954   case AnalyzeMode:\r
8955     nowChecked = IDM_AnalysisMode;\r
8956     break;\r
8957   case AnalyzeFile:\r
8958     nowChecked = IDM_AnalyzeFile;\r
8959     break;\r
8960   case EditGame:\r
8961     nowChecked = IDM_EditGame;\r
8962     break;\r
8963   case PlayFromGameFile:\r
8964     nowChecked = IDM_LoadGame;\r
8965     break;\r
8966   case EditPosition:\r
8967     nowChecked = IDM_EditPosition;\r
8968     break;\r
8969   case Training:\r
8970     nowChecked = IDM_Training;\r
8971     break;\r
8972   case IcsPlayingWhite:\r
8973   case IcsPlayingBlack:\r
8974   case IcsObserving:\r
8975   case IcsIdle:\r
8976     nowChecked = IDM_IcsClient;\r
8977     break;\r
8978   default:\r
8979   case EndOfGame:\r
8980     nowChecked = 0;\r
8981     break;\r
8982   }\r
8983   if (prevChecked != 0)\r
8984     (void) CheckMenuItem(GetMenu(hwndMain),\r
8985                          prevChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
8986   if (nowChecked != 0)\r
8987     (void) CheckMenuItem(GetMenu(hwndMain),\r
8988                          nowChecked, MF_BYCOMMAND|MF_CHECKED);\r
8989 \r
8990   if (nowChecked == IDM_LoadGame || nowChecked == IDM_Training) {\r
8991     (void) EnableMenuItem(GetMenu(hwndMain), IDM_Training, \r
8992                           MF_BYCOMMAND|MF_ENABLED);\r
8993   } else {\r
8994     (void) EnableMenuItem(GetMenu(hwndMain), \r
8995                           IDM_Training, MF_BYCOMMAND|MF_GRAYED);\r
8996   }\r
8997 \r
8998   prevChecked = nowChecked;\r
8999 \r
9000   /* [DM] icsEngineAnalyze - Do a sceure check too */\r
9001   if (appData.icsActive) {\r
9002        if (appData.icsEngineAnalyze) {\r
9003                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9004                        MF_BYCOMMAND|MF_CHECKED);\r
9005        } else {\r
9006                (void) CheckMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9007                        MF_BYCOMMAND|MF_UNCHECKED);\r
9008        }\r
9009   }\r
9010 }\r
9011 \r
9012 VOID\r
9013 SetICSMode()\r
9014 {\r
9015   HMENU hmenu = GetMenu(hwndMain);\r
9016   SetMenuEnables(hmenu, icsEnables);\r
9017   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
9018     MF_BYPOSITION|MF_ENABLED);\r
9019 #ifdef ZIPPY\r
9020   if (appData.zippyPlay) {\r
9021     SetMenuEnables(hmenu, zippyEnables);\r
9022     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
9023          (void) EnableMenuItem(GetMenu(hwndMain), IDM_AnalysisMode,\r
9024           MF_BYCOMMAND|MF_ENABLED);\r
9025   }\r
9026 #endif\r
9027 }\r
9028 \r
9029 VOID\r
9030 SetGNUMode()\r
9031 {\r
9032   SetMenuEnables(GetMenu(hwndMain), gnuEnables);\r
9033 }\r
9034 \r
9035 VOID\r
9036 SetNCPMode()\r
9037 {\r
9038   HMENU hmenu = GetMenu(hwndMain);\r
9039   SetMenuEnables(hmenu, ncpEnables);\r
9040   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), SOUNDS_POS,\r
9041     MF_BYPOSITION|MF_GRAYED);\r
9042     DrawMenuBar(hwndMain);\r
9043 }\r
9044 \r
9045 VOID\r
9046 SetCmailMode()\r
9047 {\r
9048   SetMenuEnables(GetMenu(hwndMain), cmailEnables);\r
9049 }\r
9050 \r
9051 VOID \r
9052 SetTrainingModeOn()\r
9053 {\r
9054   int i;\r
9055   SetMenuEnables(GetMenu(hwndMain), trainingOnEnables);\r
9056   for (i = 0; i < N_BUTTONS; i++) {\r
9057     if (buttonDesc[i].hwnd != NULL)\r
9058       EnableWindow(buttonDesc[i].hwnd, FALSE);\r
9059   }\r
9060   CommentPopDown();\r
9061 }\r
9062 \r
9063 VOID SetTrainingModeOff()\r
9064 {\r
9065   int i;\r
9066   SetMenuEnables(GetMenu(hwndMain), trainingOffEnables);\r
9067   for (i = 0; i < N_BUTTONS; i++) {\r
9068     if (buttonDesc[i].hwnd != NULL)\r
9069       EnableWindow(buttonDesc[i].hwnd, TRUE);\r
9070   }\r
9071 }\r
9072 \r
9073 \r
9074 VOID\r
9075 SetUserThinkingEnables()\r
9076 {\r
9077   SetMenuEnables(GetMenu(hwndMain), userThinkingEnables);\r
9078 }\r
9079 \r
9080 VOID\r
9081 SetMachineThinkingEnables()\r
9082 {\r
9083   HMENU hMenu = GetMenu(hwndMain);\r
9084   int flags = MF_BYCOMMAND|MF_ENABLED;\r
9085 \r
9086   SetMenuEnables(hMenu, machineThinkingEnables);\r
9087 \r
9088   if (gameMode == MachinePlaysBlack) {\r
9089     (void)EnableMenuItem(hMenu, IDM_MachineBlack, flags);\r
9090   } else if (gameMode == MachinePlaysWhite) {\r
9091     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
9092   } else if (gameMode == TwoMachinesPlay) {\r
9093     (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
9094   }\r
9095 }\r
9096 \r
9097 \r
9098 VOID\r
9099 DisplayTitle(char *str)\r
9100 {\r
9101   char title[MSG_SIZ], *host;\r
9102   if (str[0] != NULLCHAR) {\r
9103     strcpy(title, str);\r
9104   } else if (appData.icsActive) {\r
9105     if (appData.icsCommPort[0] != NULLCHAR)\r
9106       host = "ICS";\r
9107     else \r
9108       host = appData.icsHost;\r
9109     sprintf(title, "%s: %s", szTitle, host);\r
9110   } else if (appData.noChessProgram) {\r
9111     strcpy(title, szTitle);\r
9112   } else {\r
9113     strcpy(title, szTitle);\r
9114     strcat(title, ": ");\r
9115     strcat(title, first.tidy);\r
9116   }\r
9117   SetWindowText(hwndMain, title);\r
9118 }\r
9119 \r
9120 \r
9121 VOID\r
9122 DisplayMessage(char *str1, char *str2)\r
9123 {\r
9124   HDC hdc;\r
9125   HFONT oldFont;\r
9126   int remain = MESSAGE_TEXT_MAX - 1;\r
9127   int len;\r
9128 \r
9129   moveErrorMessageUp = FALSE; /* turned on later by caller if needed */\r
9130   messageText[0] = NULLCHAR;\r
9131   if (*str1) {\r
9132     len = strlen(str1);\r
9133     if (len > remain) len = remain;\r
9134     strncpy(messageText, str1, len);\r
9135     messageText[len] = NULLCHAR;\r
9136     remain -= len;\r
9137   }\r
9138   if (*str2 && remain >= 2) {\r
9139     if (*str1) {\r
9140       strcat(messageText, "  ");\r
9141       remain -= 2;\r
9142     }\r
9143     len = strlen(str2);\r
9144     if (len > remain) len = remain;\r
9145     strncat(messageText, str2, len);\r
9146   }\r
9147   messageText[MESSAGE_TEXT_MAX - 1] = NULLCHAR;\r
9148 \r
9149   if (hwndMain == NULL || IsIconic(hwndMain)) return;\r
9150 \r
9151   SAYMACHINEMOVE();\r
9152 \r
9153   hdc = GetDC(hwndMain);\r
9154   oldFont = SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
9155   ExtTextOut(hdc, messageRect.left, messageRect.top, ETO_CLIPPED|ETO_OPAQUE,\r
9156              &messageRect, messageText, strlen(messageText), NULL);\r
9157   (void) SelectObject(hdc, oldFont);\r
9158   (void) ReleaseDC(hwndMain, hdc);\r
9159 }\r
9160 \r
9161 VOID\r
9162 DisplayError(char *str, int error)\r
9163 {\r
9164   char buf[MSG_SIZ*2], buf2[MSG_SIZ];\r
9165   int len;\r
9166 \r
9167   if (error == 0) {\r
9168     strcpy(buf, str);\r
9169   } else {\r
9170     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9171                         NULL, error, LANG_NEUTRAL,\r
9172                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9173     if (len > 0) {\r
9174       sprintf(buf, "%s:\n%s", str, buf2);\r
9175     } else {\r
9176       ErrorMap *em = errmap;\r
9177       while (em->err != 0 && em->err != error) em++;\r
9178       if (em->err != 0) {\r
9179         sprintf(buf, "%s:\n%s", str, em->msg);\r
9180       } else {\r
9181         sprintf(buf, "%s:\nError code %d", str, error);\r
9182       }\r
9183     }\r
9184   }\r
9185   \r
9186   ErrorPopUp("Error", buf);\r
9187 }\r
9188 \r
9189 \r
9190 VOID\r
9191 DisplayMoveError(char *str)\r
9192 {\r
9193   fromX = fromY = -1;\r
9194   ClearHighlights();\r
9195   DrawPosition(FALSE, NULL);\r
9196   if (appData.popupMoveErrors) {\r
9197     ErrorPopUp("Error", str);\r
9198   } else {\r
9199     DisplayMessage(str, "");\r
9200     moveErrorMessageUp = TRUE;\r
9201   }\r
9202 }\r
9203 \r
9204 VOID\r
9205 DisplayFatalError(char *str, int error, int exitStatus)\r
9206 {\r
9207   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
9208   int len;\r
9209   char *label = exitStatus ? "Fatal Error" : "Exiting";\r
9210 \r
9211   if (error != 0) {\r
9212     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
9213                         NULL, error, LANG_NEUTRAL,\r
9214                         (LPSTR) buf2, MSG_SIZ, NULL);\r
9215     if (len > 0) {\r
9216       sprintf(buf, "%s:\n%s", str, buf2);\r
9217     } else {\r
9218       ErrorMap *em = errmap;\r
9219       while (em->err != 0 && em->err != error) em++;\r
9220       if (em->err != 0) {\r
9221         sprintf(buf, "%s:\n%s", str, em->msg);\r
9222       } else {\r
9223         sprintf(buf, "%s:\nError code %d", str, error);\r
9224       }\r
9225     }\r
9226     str = buf;\r
9227   }\r
9228   if (appData.debugMode) {\r
9229     fprintf(debugFP, "%s: %s\n", label, str);\r
9230   }\r
9231   if (appData.popupExitMessage) {\r
9232     (void) MessageBox(hwndMain, str, label, MB_OK|\r
9233                       (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
9234   }\r
9235   ExitEvent(exitStatus);\r
9236 }\r
9237 \r
9238 \r
9239 VOID\r
9240 DisplayInformation(char *str)\r
9241 {\r
9242   (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
9243 }\r
9244 \r
9245 \r
9246 VOID\r
9247 DisplayNote(char *str)\r
9248 {\r
9249   ErrorPopUp("Note", str);\r
9250 }\r
9251 \r
9252 \r
9253 typedef struct {\r
9254   char *title, *question, *replyPrefix;\r
9255   ProcRef pr;\r
9256 } QuestionParams;\r
9257 \r
9258 LRESULT CALLBACK\r
9259 QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9260 {\r
9261   static QuestionParams *qp;\r
9262   char reply[MSG_SIZ];\r
9263   int len, err;\r
9264 \r
9265   switch (message) {\r
9266   case WM_INITDIALOG:\r
9267     qp = (QuestionParams *) lParam;\r
9268     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9269     SetWindowText(hDlg, qp->title);\r
9270     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
9271     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
9272     return FALSE;\r
9273 \r
9274   case WM_COMMAND:\r
9275     switch (LOWORD(wParam)) {\r
9276     case IDOK:\r
9277       strcpy(reply, qp->replyPrefix);\r
9278       if (*reply) strcat(reply, " ");\r
9279       len = strlen(reply);\r
9280       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
9281       strcat(reply, "\n");\r
9282       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
9283       EndDialog(hDlg, TRUE);\r
9284       if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
9285       return TRUE;\r
9286     case IDCANCEL:\r
9287       EndDialog(hDlg, FALSE);\r
9288       return TRUE;\r
9289     default:\r
9290       break;\r
9291     }\r
9292     break;\r
9293   }\r
9294   return FALSE;\r
9295 }\r
9296 \r
9297 VOID\r
9298 AskQuestion(char* title, char *question, char *replyPrefix, ProcRef pr)\r
9299 {\r
9300     QuestionParams qp;\r
9301     FARPROC lpProc;\r
9302     \r
9303     qp.title = title;\r
9304     qp.question = question;\r
9305     qp.replyPrefix = replyPrefix;\r
9306     qp.pr = pr;\r
9307     lpProc = MakeProcInstance((FARPROC)QuestionDialog, hInst);\r
9308     DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_Question),\r
9309       hwndMain, (DLGPROC)lpProc, (LPARAM)&qp);\r
9310     FreeProcInstance(lpProc);\r
9311 }\r
9312 \r
9313 /* [AS] Pick FRC position */\r
9314 LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9315 {\r
9316     static int * lpIndexFRC;\r
9317     BOOL index_is_ok;\r
9318     char buf[16];\r
9319 \r
9320     switch( message )\r
9321     {\r
9322     case WM_INITDIALOG:\r
9323         lpIndexFRC = (int *) lParam;\r
9324 \r
9325         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9326 \r
9327         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
9328         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
9329         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETSEL, 0, -1 );\r
9330         SetFocus(GetDlgItem(hDlg, IDC_NFG_Edit));\r
9331 \r
9332         break;\r
9333 \r
9334     case WM_COMMAND:\r
9335         switch( LOWORD(wParam) ) {\r
9336         case IDOK:\r
9337             *lpIndexFRC = GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9338             EndDialog( hDlg, 0 );\r
9339             shuffleOpenings = TRUE; /* [HGM] shuffle: switch shuffling on for as long as we stay in current variant */\r
9340             return TRUE;\r
9341         case IDCANCEL:\r
9342             EndDialog( hDlg, 1 );   \r
9343             return TRUE;\r
9344         case IDC_NFG_Edit:\r
9345             if( HIWORD(wParam) == EN_CHANGE ) {\r
9346                 GetDlgItemInt(hDlg, IDC_NFG_Edit, &index_is_ok, TRUE );\r
9347 \r
9348                 EnableWindow( GetDlgItem(hDlg, IDOK), index_is_ok );\r
9349             }\r
9350             return TRUE;\r
9351         case IDC_NFG_Random:\r
9352             sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
9353             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
9354             return TRUE;\r
9355         }\r
9356 \r
9357         break;\r
9358     }\r
9359 \r
9360     return FALSE;\r
9361 }\r
9362 \r
9363 int NewGameFRC()\r
9364 {\r
9365     int result;\r
9366     int index = appData.defaultFrcPosition;\r
9367     FARPROC lpProc = MakeProcInstance( (FARPROC) NewGameFRC_Proc, hInst );\r
9368 \r
9369     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_NewGameFRC), hwndMain, (DLGPROC)lpProc, (LPARAM)&index );\r
9370 \r
9371     if( result == 0 ) {\r
9372         appData.defaultFrcPosition = index;\r
9373     }\r
9374 \r
9375     return result;\r
9376 }\r
9377 \r
9378 /* [AS] Game list options */\r
9379 typedef struct {\r
9380     char id;\r
9381     char * name;\r
9382 } GLT_Item;\r
9383 \r
9384 static GLT_Item GLT_ItemInfo[] = {\r
9385     { GLT_EVENT,      "Event" },\r
9386     { GLT_SITE,       "Site" },\r
9387     { GLT_DATE,       "Date" },\r
9388     { GLT_ROUND,      "Round" },\r
9389     { GLT_PLAYERS,    "Players" },\r
9390     { GLT_RESULT,     "Result" },\r
9391     { GLT_WHITE_ELO,  "White Rating" },\r
9392     { GLT_BLACK_ELO,  "Black Rating" },\r
9393     { GLT_TIME_CONTROL,"Time Control" },\r
9394     { GLT_VARIANT,    "Variant" },\r
9395     { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
9396     { 0, 0 }\r
9397 };\r
9398 \r
9399 const char * GLT_FindItem( char id )\r
9400 {\r
9401     const char * result = 0;\r
9402 \r
9403     GLT_Item * list = GLT_ItemInfo;\r
9404 \r
9405     while( list->id != 0 ) {\r
9406         if( list->id == id ) {\r
9407             result = list->name;\r
9408             break;\r
9409         }\r
9410 \r
9411         list++;\r
9412     }\r
9413 \r
9414     return result;\r
9415 }\r
9416 \r
9417 void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
9418 {\r
9419     const char * name = GLT_FindItem( id );\r
9420 \r
9421     if( name != 0 ) {\r
9422         if( index >= 0 ) {\r
9423             SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
9424         }\r
9425         else {\r
9426             SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
9427         }\r
9428     }\r
9429 }\r
9430 \r
9431 void GLT_TagsToList( HWND hDlg, char * tags )\r
9432 {\r
9433     char * pc = tags;\r
9434 \r
9435     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
9436 \r
9437     while( *pc ) {\r
9438         GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9439         pc++;\r
9440     }\r
9441 \r
9442     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
9443 \r
9444     pc = GLT_ALL_TAGS;\r
9445 \r
9446     while( *pc ) {\r
9447         if( strchr( tags, *pc ) == 0 ) {\r
9448             GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
9449         }\r
9450         pc++;\r
9451     }\r
9452 \r
9453     SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
9454 }\r
9455 \r
9456 char GLT_ListItemToTag( HWND hDlg, int index )\r
9457 {\r
9458     char result = '\0';\r
9459     char name[128];\r
9460 \r
9461     GLT_Item * list = GLT_ItemInfo;\r
9462 \r
9463     if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
9464         while( list->id != 0 ) {\r
9465             if( strcmp( list->name, name ) == 0 ) {\r
9466                 result = list->id;\r
9467                 break;\r
9468             }\r
9469 \r
9470             list++;\r
9471         }\r
9472     }\r
9473 \r
9474     return result;\r
9475 }\r
9476 \r
9477 void GLT_MoveSelection( HWND hDlg, int delta )\r
9478 {\r
9479     int idx1 = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCURSEL, 0, 0 );\r
9480     int idx2 = idx1 + delta;\r
9481     int count = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9482 \r
9483     if( idx1 >=0 && idx1 < count && idx2 >= 0 && idx2 < count ) {\r
9484         char buf[128];\r
9485 \r
9486         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, idx1, (LPARAM) buf );\r
9487         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_DELETESTRING, idx1, 0 );\r
9488         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_INSERTSTRING, idx2, (LPARAM) buf );\r
9489         SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, idx2, 0 );\r
9490     }\r
9491 }\r
9492 \r
9493 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
9494 {\r
9495     static char glt[64];\r
9496     static char * lpUserGLT;\r
9497 \r
9498     switch( message )\r
9499     {\r
9500     case WM_INITDIALOG:\r
9501         lpUserGLT = (char *) lParam;\r
9502         \r
9503         strcpy( glt, lpUserGLT );\r
9504 \r
9505         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
9506 \r
9507         /* Initialize list */\r
9508         GLT_TagsToList( hDlg, glt );\r
9509 \r
9510         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
9511 \r
9512         break;\r
9513 \r
9514     case WM_COMMAND:\r
9515         switch( LOWORD(wParam) ) {\r
9516         case IDOK:\r
9517             {\r
9518                 char * pc = lpUserGLT;\r
9519                 int idx = 0;\r
9520 //                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
9521                 char id;\r
9522 \r
9523                 do {\r
9524                     id = GLT_ListItemToTag( hDlg, idx );\r
9525 \r
9526                     *pc++ = id;\r
9527                     idx++;\r
9528                 } while( id != '\0' );\r
9529             }\r
9530             EndDialog( hDlg, 0 );\r
9531             return TRUE;\r
9532         case IDCANCEL:\r
9533             EndDialog( hDlg, 1 );\r
9534             return TRUE;\r
9535 \r
9536         case IDC_GLT_Default:\r
9537             strcpy( glt, GLT_DEFAULT_TAGS );\r
9538             GLT_TagsToList( hDlg, glt );\r
9539             return TRUE;\r
9540 \r
9541         case IDC_GLT_Restore:\r
9542             strcpy( glt, lpUserGLT );\r
9543             GLT_TagsToList( hDlg, glt );\r
9544             return TRUE;\r
9545 \r
9546         case IDC_GLT_Up:\r
9547             GLT_MoveSelection( hDlg, -1 );\r
9548             return TRUE;\r
9549 \r
9550         case IDC_GLT_Down:\r
9551             GLT_MoveSelection( hDlg, +1 );\r
9552             return TRUE;\r
9553         }\r
9554 \r
9555         break;\r
9556     }\r
9557 \r
9558     return FALSE;\r
9559 }\r
9560 \r
9561 int GameListOptions()\r
9562 {\r
9563     char glt[64];\r
9564     int result;\r
9565     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
9566 \r
9567     strcpy( glt, appData.gameListTags );\r
9568 \r
9569     result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
9570 \r
9571     if( result == 0 ) {\r
9572         /* [AS] Memory leak here! */\r
9573         appData.gameListTags = strdup( glt ); \r
9574     }\r
9575 \r
9576     return result;\r
9577 }\r
9578 \r
9579 \r
9580 VOID\r
9581 DisplayIcsInteractionTitle(char *str)\r
9582 {\r
9583   char consoleTitle[MSG_SIZ];\r
9584 \r
9585   sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
9586   SetWindowText(hwndConsole, consoleTitle);\r
9587 }\r
9588 \r
9589 void\r
9590 DrawPosition(int fullRedraw, Board board)\r
9591 {\r
9592   HDCDrawPosition(NULL, (BOOLEAN) fullRedraw, board); \r
9593 }\r
9594 \r
9595 \r
9596 VOID\r
9597 ResetFrontEnd()\r
9598 {\r
9599   fromX = fromY = -1;\r
9600   if (dragInfo.pos.x != -1 || dragInfo.pos.y != -1) {\r
9601     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9602     dragInfo.pos.x = dragInfo.pos.y = -1;\r
9603     dragInfo.lastpos = dragInfo.pos;\r
9604     dragInfo.start.x = dragInfo.start.y = -1;\r
9605     dragInfo.from = dragInfo.start;\r
9606     ReleaseCapture();\r
9607     DrawPosition(TRUE, NULL);\r
9608   }\r
9609 }\r
9610 \r
9611 \r
9612 VOID\r
9613 CommentPopUp(char *title, char *str)\r
9614 {\r
9615   HWND hwnd = GetActiveWindow();\r
9616   EitherCommentPopUp(0, title, str, FALSE);\r
9617   SetActiveWindow(hwnd);\r
9618 }\r
9619 \r
9620 VOID\r
9621 CommentPopDown(void)\r
9622 {\r
9623   CheckMenuItem(GetMenu(hwndMain), IDM_EditComment, MF_UNCHECKED);\r
9624   if (commentDialog) {\r
9625     ShowWindow(commentDialog, SW_HIDE);\r
9626   }\r
9627   commentDialogUp = FALSE;\r
9628 }\r
9629 \r
9630 VOID\r
9631 EditCommentPopUp(int index, char *title, char *str)\r
9632 {\r
9633   EitherCommentPopUp(index, title, str, TRUE);\r
9634 }\r
9635 \r
9636 \r
9637 VOID\r
9638 RingBell()\r
9639 {\r
9640   MyPlaySound(&sounds[(int)SoundMove]);\r
9641 }\r
9642 \r
9643 VOID PlayIcsWinSound()\r
9644 {\r
9645   MyPlaySound(&sounds[(int)SoundIcsWin]);\r
9646 }\r
9647 \r
9648 VOID PlayIcsLossSound()\r
9649 {\r
9650   MyPlaySound(&sounds[(int)SoundIcsLoss]);\r
9651 }\r
9652 \r
9653 VOID PlayIcsDrawSound()\r
9654 {\r
9655   MyPlaySound(&sounds[(int)SoundIcsDraw]);\r
9656 }\r
9657 \r
9658 VOID PlayIcsUnfinishedSound()\r
9659 {\r
9660   MyPlaySound(&sounds[(int)SoundIcsUnfinished]);\r
9661 }\r
9662 \r
9663 VOID\r
9664 PlayAlarmSound()\r
9665 {\r
9666   MyPlaySound(&sounds[(int)SoundAlarm]);\r
9667 }\r
9668 \r
9669 \r
9670 VOID\r
9671 EchoOn()\r
9672 {\r
9673   HWND hInput;\r
9674   consoleEcho = TRUE;\r
9675   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9676   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&consoleCF);\r
9677   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, consoleBackgroundColor);\r
9678 }\r
9679 \r
9680 \r
9681 VOID\r
9682 EchoOff()\r
9683 {\r
9684   CHARFORMAT cf;\r
9685   HWND hInput;\r
9686   consoleEcho = FALSE;\r
9687   hInput = GetDlgItem(hwndConsole, OPT_ConsoleInput);\r
9688   /* This works OK: set text and background both to the same color */\r
9689   cf = consoleCF;\r
9690   cf.crTextColor = COLOR_ECHOOFF;\r
9691   SendMessage(hInput, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);\r
9692   SendMessage(hInput, EM_SETBKGNDCOLOR, FALSE, cf.crTextColor);\r
9693 }\r
9694 \r
9695 /* No Raw()...? */\r
9696 \r
9697 void Colorize(ColorClass cc, int continuation)\r
9698 {\r
9699   currentColorClass = cc;\r
9700   consoleCF.dwMask = CFM_COLOR|CFM_BOLD|CFM_ITALIC|CFM_UNDERLINE|CFM_STRIKEOUT;\r
9701   consoleCF.crTextColor = textAttribs[cc].color;\r
9702   consoleCF.dwEffects = textAttribs[cc].effects;\r
9703   if (!continuation) MyPlaySound(&textAttribs[cc].sound);\r
9704 }\r
9705 \r
9706 char *\r
9707 UserName()\r
9708 {\r
9709   static char buf[MSG_SIZ];\r
9710   DWORD bufsiz = MSG_SIZ;\r
9711 \r
9712   if(appData.userName != NULL && appData.userName[0] != 0) { \r
9713         return appData.userName; /* [HGM] username: prefer name selected by user over his system login */\r
9714   }\r
9715   if (!GetUserName(buf, &bufsiz)) {\r
9716     /*DisplayError("Error getting user name", GetLastError());*/\r
9717     strcpy(buf, "User");\r
9718   }\r
9719   return buf;\r
9720 }\r
9721 \r
9722 char *\r
9723 HostName()\r
9724 {\r
9725   static char buf[MSG_SIZ];\r
9726   DWORD bufsiz = MSG_SIZ;\r
9727 \r
9728   if (!GetComputerName(buf, &bufsiz)) {\r
9729     /*DisplayError("Error getting host name", GetLastError());*/\r
9730     strcpy(buf, "Unknown");\r
9731   }\r
9732   return buf;\r
9733 }\r
9734 \r
9735 \r
9736 int\r
9737 ClockTimerRunning()\r
9738 {\r
9739   return clockTimerEvent != 0;\r
9740 }\r
9741 \r
9742 int\r
9743 StopClockTimer()\r
9744 {\r
9745   if (clockTimerEvent == 0) return FALSE;\r
9746   KillTimer(hwndMain, clockTimerEvent);\r
9747   clockTimerEvent = 0;\r
9748   return TRUE;\r
9749 }\r
9750 \r
9751 void\r
9752 StartClockTimer(long millisec)\r
9753 {\r
9754   clockTimerEvent = SetTimer(hwndMain, (UINT) CLOCK_TIMER_ID,\r
9755                              (UINT) millisec, NULL);\r
9756 }\r
9757 \r
9758 void\r
9759 DisplayWhiteClock(long timeRemaining, int highlight)\r
9760 {\r
9761   HDC hdc;\r
9762   char *flag = whiteFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9763 \r
9764   if(appData.noGUI) return;\r
9765   hdc = GetDC(hwndMain);\r
9766   if (!IsIconic(hwndMain)) {\r
9767     DisplayAClock(hdc, timeRemaining, highlight, \r
9768                         flipClock ? &blackRect : &whiteRect, "White", flag);\r
9769   }\r
9770   if (highlight && iconCurrent == iconBlack) {\r
9771     iconCurrent = iconWhite;\r
9772     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9773     if (IsIconic(hwndMain)) {\r
9774       DrawIcon(hdc, 2, 2, iconCurrent);\r
9775     }\r
9776   }\r
9777   (void) ReleaseDC(hwndMain, hdc);\r
9778   if (hwndConsole)\r
9779     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9780 }\r
9781 \r
9782 void\r
9783 DisplayBlackClock(long timeRemaining, int highlight)\r
9784 {\r
9785   HDC hdc;\r
9786   char *flag = blackFlag && gameMode == TwoMachinesPlay ? "(!)" : "";\r
9787 \r
9788   if(appData.noGUI) return;\r
9789   hdc = GetDC(hwndMain);\r
9790   if (!IsIconic(hwndMain)) {\r
9791     DisplayAClock(hdc, timeRemaining, highlight, \r
9792                         flipClock ? &whiteRect : &blackRect, "Black", flag);\r
9793   }\r
9794   if (highlight && iconCurrent == iconWhite) {\r
9795     iconCurrent = iconBlack;\r
9796     PostMessage(hwndMain, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9797     if (IsIconic(hwndMain)) {\r
9798       DrawIcon(hdc, 2, 2, iconCurrent);\r
9799     }\r
9800   }\r
9801   (void) ReleaseDC(hwndMain, hdc);\r
9802   if (hwndConsole)\r
9803     PostMessage(hwndConsole, WM_SETICON, (WPARAM) TRUE, (LPARAM) iconCurrent);\r
9804 }\r
9805 \r
9806 \r
9807 int\r
9808 LoadGameTimerRunning()\r
9809 {\r
9810   return loadGameTimerEvent != 0;\r
9811 }\r
9812 \r
9813 int\r
9814 StopLoadGameTimer()\r
9815 {\r
9816   if (loadGameTimerEvent == 0) return FALSE;\r
9817   KillTimer(hwndMain, loadGameTimerEvent);\r
9818   loadGameTimerEvent = 0;\r
9819   return TRUE;\r
9820 }\r
9821 \r
9822 void\r
9823 StartLoadGameTimer(long millisec)\r
9824 {\r
9825   loadGameTimerEvent = SetTimer(hwndMain, (UINT) LOAD_GAME_TIMER_ID,\r
9826                                 (UINT) millisec, NULL);\r
9827 }\r
9828 \r
9829 void\r
9830 AutoSaveGame()\r
9831 {\r
9832   char *defName;\r
9833   FILE *f;\r
9834   char fileTitle[MSG_SIZ];\r
9835 \r
9836   defName = DefaultFileName(appData.oldSaveStyle ? "gam" : "pgn");\r
9837   f = OpenFileDialog(hwndMain, "a", defName,\r
9838                      appData.oldSaveStyle ? "gam" : "pgn",\r
9839                      GAME_FILT, \r
9840                      "Save Game to File", NULL, fileTitle, NULL);\r
9841   if (f != NULL) {\r
9842     SaveGame(f, 0, "");\r
9843     fclose(f);\r
9844   }\r
9845 }\r
9846 \r
9847 \r
9848 void\r
9849 ScheduleDelayedEvent(DelayedEventCallback cb, long millisec)\r
9850 {\r
9851   if (delayedTimerEvent != 0) {\r
9852     if (appData.debugMode) {\r
9853       fprintf(debugFP, "ScheduleDelayedEvent: event already scheduled\n");\r
9854     }\r
9855     KillTimer(hwndMain, delayedTimerEvent);\r
9856     delayedTimerEvent = 0;\r
9857     delayedTimerCallback();\r
9858   }\r
9859   delayedTimerCallback = cb;\r
9860   delayedTimerEvent = SetTimer(hwndMain, (UINT) DELAYED_TIMER_ID,\r
9861                                 (UINT) millisec, NULL);\r
9862 }\r
9863 \r
9864 DelayedEventCallback\r
9865 GetDelayedEvent()\r
9866 {\r
9867   if (delayedTimerEvent) {\r
9868     return delayedTimerCallback;\r
9869   } else {\r
9870     return NULL;\r
9871   }\r
9872 }\r
9873 \r
9874 void\r
9875 CancelDelayedEvent()\r
9876 {\r
9877   if (delayedTimerEvent) {\r
9878     KillTimer(hwndMain, delayedTimerEvent);\r
9879     delayedTimerEvent = 0;\r
9880   }\r
9881 }\r
9882 \r
9883 DWORD GetWin32Priority(int nice)\r
9884 { // [HGM] nice: translate Unix nice() value to indows priority class. (Code stolen from Polyglot 1.4w11)\r
9885 /*\r
9886 REALTIME_PRIORITY_CLASS     0x00000100\r
9887 HIGH_PRIORITY_CLASS         0x00000080\r
9888 ABOVE_NORMAL_PRIORITY_CLASS 0x00008000\r
9889 NORMAL_PRIORITY_CLASS       0x00000020\r
9890 BELOW_NORMAL_PRIORITY_CLASS 0x00004000\r
9891 IDLE_PRIORITY_CLASS         0x00000040\r
9892 */\r
9893         if (nice < -15) return 0x00000080;\r
9894         if (nice < 0)   return 0x00008000;\r
9895         if (nice == 0)  return 0x00000020;\r
9896         if (nice < 15)  return 0x00004000;\r
9897         return 0x00000040;\r
9898 }\r
9899 \r
9900 /* Start a child process running the given program.\r
9901    The process's standard output can be read from "from", and its\r
9902    standard input can be written to "to".\r
9903    Exit with fatal error if anything goes wrong.\r
9904    Returns an opaque pointer that can be used to destroy the process\r
9905    later.\r
9906 */\r
9907 int\r
9908 StartChildProcess(char *cmdLine, char *dir, ProcRef *pr)\r
9909 {\r
9910 #define BUFSIZE 4096\r
9911 \r
9912   HANDLE hChildStdinRd, hChildStdinWr,\r
9913     hChildStdoutRd, hChildStdoutWr;\r
9914   HANDLE hChildStdinWrDup, hChildStdoutRdDup;\r
9915   SECURITY_ATTRIBUTES saAttr;\r
9916   BOOL fSuccess;\r
9917   PROCESS_INFORMATION piProcInfo;\r
9918   STARTUPINFO siStartInfo;\r
9919   ChildProc *cp;\r
9920   char buf[MSG_SIZ];\r
9921   DWORD err;\r
9922 \r
9923   if (appData.debugMode) {\r
9924     fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n", dir, cmdLine);\r
9925   }\r
9926 \r
9927   *pr = NoProc;\r
9928 \r
9929   /* Set the bInheritHandle flag so pipe handles are inherited. */\r
9930   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);\r
9931   saAttr.bInheritHandle = TRUE;\r
9932   saAttr.lpSecurityDescriptor = NULL;\r
9933 \r
9934   /*\r
9935    * The steps for redirecting child's STDOUT:\r
9936    *     1. Create anonymous pipe to be STDOUT for child.\r
9937    *     2. Create a noninheritable duplicate of read handle,\r
9938    *         and close the inheritable read handle.\r
9939    */\r
9940 \r
9941   /* Create a pipe for the child's STDOUT. */\r
9942   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {\r
9943     return GetLastError();\r
9944   }\r
9945 \r
9946   /* Duplicate the read handle to the pipe, so it is not inherited. */\r
9947   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,\r
9948                              GetCurrentProcess(), &hChildStdoutRdDup, 0,\r
9949                              FALSE,     /* not inherited */\r
9950                              DUPLICATE_SAME_ACCESS);\r
9951   if (! fSuccess) {\r
9952     return GetLastError();\r
9953   }\r
9954   CloseHandle(hChildStdoutRd);\r
9955 \r
9956   /*\r
9957    * The steps for redirecting child's STDIN:\r
9958    *     1. Create anonymous pipe to be STDIN for child.\r
9959    *     2. Create a noninheritable duplicate of write handle,\r
9960    *         and close the inheritable write handle.\r
9961    */\r
9962 \r
9963   /* Create a pipe for the child's STDIN. */\r
9964   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {\r
9965     return GetLastError();\r
9966   }\r
9967 \r
9968   /* Duplicate the write handle to the pipe, so it is not inherited. */\r
9969   fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,\r
9970                              GetCurrentProcess(), &hChildStdinWrDup, 0,\r
9971                              FALSE,     /* not inherited */\r
9972                              DUPLICATE_SAME_ACCESS);\r
9973   if (! fSuccess) {\r
9974     return GetLastError();\r
9975   }\r
9976   CloseHandle(hChildStdinWr);\r
9977 \r
9978   /* Arrange to (1) look in dir for the child .exe file, and\r
9979    * (2) have dir be the child's working directory.  Interpret\r
9980    * dir relative to the directory WinBoard loaded from. */\r
9981   GetCurrentDirectory(MSG_SIZ, buf);\r
9982   SetCurrentDirectory(installDir);\r
9983   SetCurrentDirectory(dir);\r
9984 \r
9985   /* Now create the child process. */\r
9986 \r
9987   siStartInfo.cb = sizeof(STARTUPINFO);\r
9988   siStartInfo.lpReserved = NULL;\r
9989   siStartInfo.lpDesktop = NULL;\r
9990   siStartInfo.lpTitle = NULL;\r
9991   siStartInfo.dwFlags = STARTF_USESTDHANDLES;\r
9992   siStartInfo.cbReserved2 = 0;\r
9993   siStartInfo.lpReserved2 = NULL;\r
9994   siStartInfo.hStdInput = hChildStdinRd;\r
9995   siStartInfo.hStdOutput = hChildStdoutWr;\r
9996   siStartInfo.hStdError = hChildStdoutWr;\r
9997 \r
9998   fSuccess = CreateProcess(NULL,\r
9999                            cmdLine,        /* command line */\r
10000                            NULL,           /* process security attributes */\r
10001                            NULL,           /* primary thread security attrs */\r
10002                            TRUE,           /* handles are inherited */\r
10003                            DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,\r
10004                            NULL,           /* use parent's environment */\r
10005                            NULL,\r
10006                            &siStartInfo, /* STARTUPINFO pointer */\r
10007                            &piProcInfo); /* receives PROCESS_INFORMATION */\r
10008 \r
10009   err = GetLastError();\r
10010   SetCurrentDirectory(buf); /* return to prev directory */\r
10011   if (! fSuccess) {\r
10012     return err;\r
10013   }\r
10014 \r
10015   if (appData.niceEngines){ // [HGM] nice: adjust engine proc priority\r
10016     if(appData.debugMode) fprintf(debugFP, "nice engine proc to %d\n", appData.niceEngines);\r
10017     SetPriorityClass(piProcInfo.hProcess, GetWin32Priority(appData.niceEngines));\r
10018   }\r
10019 \r
10020   /* Close the handles we don't need in the parent */\r
10021   CloseHandle(piProcInfo.hThread);\r
10022   CloseHandle(hChildStdinRd);\r
10023   CloseHandle(hChildStdoutWr);\r
10024 \r
10025   /* Prepare return value */\r
10026   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10027   cp->kind = CPReal;\r
10028   cp->hProcess = piProcInfo.hProcess;\r
10029   cp->pid = piProcInfo.dwProcessId;\r
10030   cp->hFrom = hChildStdoutRdDup;\r
10031   cp->hTo = hChildStdinWrDup;\r
10032 \r
10033   *pr = (void *) cp;\r
10034 \r
10035   /* Klaus Friedel says that this Sleep solves a problem under Windows\r
10036      2000 where engines sometimes don't see the initial command(s)\r
10037      from WinBoard and hang.  I don't understand how that can happen,\r
10038      but the Sleep is harmless, so I've put it in.  Others have also\r
10039      reported what may be the same problem, so hopefully this will fix\r
10040      it for them too.  */\r
10041   Sleep(500);\r
10042 \r
10043   return NO_ERROR;\r
10044 }\r
10045 \r
10046 \r
10047 void\r
10048 DestroyChildProcess(ProcRef pr, int/*boolean*/ signal)\r
10049 {\r
10050   ChildProc *cp; int result;\r
10051 \r
10052   cp = (ChildProc *) pr;\r
10053   if (cp == NULL) return;\r
10054 \r
10055   switch (cp->kind) {\r
10056   case CPReal:\r
10057     /* TerminateProcess is considered harmful, so... */\r
10058     CloseHandle(cp->hTo); /* Closing this will give the child an EOF and hopefully kill it */\r
10059     if (cp->hFrom) CloseHandle(cp->hFrom);  /* if NULL, InputThread will close it */\r
10060     /* The following doesn't work because the chess program\r
10061        doesn't "have the same console" as WinBoard.  Maybe\r
10062        we could arrange for this even though neither WinBoard\r
10063        nor the chess program uses a console for stdio? */\r
10064     /*!!if (signal) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, cp->pid);*/\r
10065 \r
10066     /* [AS] Special termination modes for misbehaving programs... */\r
10067     if( signal == 9 ) { \r
10068         result = TerminateProcess( cp->hProcess, 0 );\r
10069 \r
10070         if ( appData.debugMode) {\r
10071             fprintf( debugFP, "Terminating process %lu, result=%d\n", cp->pid, result );\r
10072         }\r
10073     }\r
10074     else if( signal == 10 ) {\r
10075         DWORD dw = WaitForSingleObject( cp->hProcess, 3*1000 ); // Wait 3 seconds at most\r
10076 \r
10077         if( dw != WAIT_OBJECT_0 ) {\r
10078             result = TerminateProcess( cp->hProcess, 0 );\r
10079 \r
10080             if ( appData.debugMode) {\r
10081                 fprintf( debugFP, "Process %lu still alive after timeout, killing... result=%d\n", cp->pid, result );\r
10082             }\r
10083 \r
10084         }\r
10085     }\r
10086 \r
10087     CloseHandle(cp->hProcess);\r
10088     break;\r
10089 \r
10090   case CPComm:\r
10091     if (cp->hFrom) CloseHandle(cp->hFrom);\r
10092     break;\r
10093 \r
10094   case CPSock:\r
10095     closesocket(cp->sock);\r
10096     WSACleanup();\r
10097     break;\r
10098 \r
10099   case CPRcmd:\r
10100     if (signal) send(cp->sock2, "\017", 1, 0);  /* 017 = 15 = SIGTERM */\r
10101     closesocket(cp->sock);\r
10102     closesocket(cp->sock2);\r
10103     WSACleanup();\r
10104     break;\r
10105   }\r
10106   free(cp);\r
10107 }\r
10108 \r
10109 void\r
10110 InterruptChildProcess(ProcRef pr)\r
10111 {\r
10112   ChildProc *cp;\r
10113 \r
10114   cp = (ChildProc *) pr;\r
10115   if (cp == NULL) return;\r
10116   switch (cp->kind) {\r
10117   case CPReal:\r
10118     /* The following doesn't work because the chess program\r
10119        doesn't "have the same console" as WinBoard.  Maybe\r
10120        we could arrange for this even though neither WinBoard\r
10121        nor the chess program uses a console for stdio */\r
10122     /*!!GenerateConsoleCtrlEvent(CTRL_C_EVENT, cp->pid);*/\r
10123     break;\r
10124 \r
10125   case CPComm:\r
10126   case CPSock:\r
10127     /* Can't interrupt */\r
10128     break;\r
10129 \r
10130   case CPRcmd:\r
10131     send(cp->sock2, "\002", 1, 0);  /* 2 = SIGINT */\r
10132     break;\r
10133   }\r
10134 }\r
10135 \r
10136 \r
10137 int\r
10138 OpenTelnet(char *host, char *port, ProcRef *pr)\r
10139 {\r
10140   char cmdLine[MSG_SIZ];\r
10141 \r
10142   if (port[0] == NULLCHAR) {\r
10143     sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
10144   } else {\r
10145     sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
10146   }\r
10147   return StartChildProcess(cmdLine, "", pr);\r
10148 }\r
10149 \r
10150 \r
10151 /* Code to open TCP sockets */\r
10152 \r
10153 int\r
10154 OpenTCP(char *host, char *port, ProcRef *pr)\r
10155 {\r
10156   ChildProc *cp;\r
10157   int err;\r
10158   SOCKET s;\r
10159   struct sockaddr_in sa, mysa;\r
10160   struct hostent FAR *hp;\r
10161   unsigned short uport;\r
10162   WORD wVersionRequested;\r
10163   WSADATA wsaData;\r
10164 \r
10165   /* Initialize socket DLL */\r
10166   wVersionRequested = MAKEWORD(1, 1);\r
10167   err = WSAStartup(wVersionRequested, &wsaData);\r
10168   if (err != 0) return err;\r
10169 \r
10170   /* Make socket */\r
10171   if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10172     err = WSAGetLastError();\r
10173     WSACleanup();\r
10174     return err;\r
10175   }\r
10176 \r
10177   /* Bind local address using (mostly) don't-care values.\r
10178    */\r
10179   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10180   mysa.sin_family = AF_INET;\r
10181   mysa.sin_addr.s_addr = INADDR_ANY;\r
10182   uport = (unsigned short) 0;\r
10183   mysa.sin_port = htons(uport);\r
10184   if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10185       == SOCKET_ERROR) {\r
10186     err = WSAGetLastError();\r
10187     WSACleanup();\r
10188     return err;\r
10189   }\r
10190 \r
10191   /* Resolve remote host name */\r
10192   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10193   if (!(hp = gethostbyname(host))) {\r
10194     unsigned int b0, b1, b2, b3;\r
10195 \r
10196     err = WSAGetLastError();\r
10197 \r
10198     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10199       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10200       hp->h_addrtype = AF_INET;\r
10201       hp->h_length = 4;\r
10202       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10203       hp->h_addr_list[0] = (char *) malloc(4);\r
10204       hp->h_addr_list[0][0] = (char) b0;\r
10205       hp->h_addr_list[0][1] = (char) b1;\r
10206       hp->h_addr_list[0][2] = (char) b2;\r
10207       hp->h_addr_list[0][3] = (char) b3;\r
10208     } else {\r
10209       WSACleanup();\r
10210       return err;\r
10211     }\r
10212   }\r
10213   sa.sin_family = hp->h_addrtype;\r
10214   uport = (unsigned short) atoi(port);\r
10215   sa.sin_port = htons(uport);\r
10216   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10217 \r
10218   /* Make connection */\r
10219   if (connect(s, (struct sockaddr *) &sa,\r
10220               sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10221     err = WSAGetLastError();\r
10222     WSACleanup();\r
10223     return err;\r
10224   }\r
10225 \r
10226   /* Prepare return value */\r
10227   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10228   cp->kind = CPSock;\r
10229   cp->sock = s;\r
10230   *pr = (ProcRef *) cp;\r
10231 \r
10232   return NO_ERROR;\r
10233 }\r
10234 \r
10235 int\r
10236 OpenCommPort(char *name, ProcRef *pr)\r
10237 {\r
10238   HANDLE h;\r
10239   COMMTIMEOUTS ct;\r
10240   ChildProc *cp;\r
10241   char fullname[MSG_SIZ];\r
10242 \r
10243   if (*name != '\\')\r
10244     sprintf(fullname, "\\\\.\\%s", name);\r
10245   else\r
10246     strcpy(fullname, name);\r
10247 \r
10248   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
10249                  0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
10250   if (h == (HANDLE) -1) {\r
10251     return GetLastError();\r
10252   }\r
10253   hCommPort = h;\r
10254 \r
10255   if (!SetCommState(h, (LPDCB) &dcb)) return GetLastError();\r
10256 \r
10257   /* Accumulate characters until a 100ms pause, then parse */\r
10258   ct.ReadIntervalTimeout = 100;\r
10259   ct.ReadTotalTimeoutMultiplier = 0;\r
10260   ct.ReadTotalTimeoutConstant = 0;\r
10261   ct.WriteTotalTimeoutMultiplier = 0;\r
10262   ct.WriteTotalTimeoutConstant = 0;\r
10263   if (!SetCommTimeouts(h, (LPCOMMTIMEOUTS) &ct)) return GetLastError();\r
10264 \r
10265   /* Prepare return value */\r
10266   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10267   cp->kind = CPComm;\r
10268   cp->hFrom = h;\r
10269   cp->hTo = h;\r
10270   *pr = (ProcRef *) cp;\r
10271 \r
10272   return NO_ERROR;\r
10273 }\r
10274 \r
10275 int\r
10276 OpenLoopback(ProcRef *pr)\r
10277 {\r
10278   DisplayFatalError("Not implemented", 0, 1);\r
10279   return NO_ERROR;\r
10280 }\r
10281 \r
10282 \r
10283 int\r
10284 OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)\r
10285 {\r
10286   ChildProc *cp;\r
10287   int err;\r
10288   SOCKET s, s2, s3;\r
10289   struct sockaddr_in sa, mysa;\r
10290   struct hostent FAR *hp;\r
10291   unsigned short uport;\r
10292   WORD wVersionRequested;\r
10293   WSADATA wsaData;\r
10294   int fromPort;\r
10295   char stderrPortStr[MSG_SIZ];\r
10296 \r
10297   /* Initialize socket DLL */\r
10298   wVersionRequested = MAKEWORD(1, 1);\r
10299   err = WSAStartup(wVersionRequested, &wsaData);\r
10300   if (err != 0) return err;\r
10301 \r
10302   /* Resolve remote host name */\r
10303   memset((char *) &sa, 0, sizeof(struct sockaddr_in));\r
10304   if (!(hp = gethostbyname(host))) {\r
10305     unsigned int b0, b1, b2, b3;\r
10306 \r
10307     err = WSAGetLastError();\r
10308 \r
10309     if (sscanf(host, "%u.%u.%u.%u", &b0, &b1, &b2, &b3) == 4) {\r
10310       hp = (struct hostent *) calloc(1, sizeof(struct hostent));\r
10311       hp->h_addrtype = AF_INET;\r
10312       hp->h_length = 4;\r
10313       hp->h_addr_list = (char **) calloc(2, sizeof(char *));\r
10314       hp->h_addr_list[0] = (char *) malloc(4);\r
10315       hp->h_addr_list[0][0] = (char) b0;\r
10316       hp->h_addr_list[0][1] = (char) b1;\r
10317       hp->h_addr_list[0][2] = (char) b2;\r
10318       hp->h_addr_list[0][3] = (char) b3;\r
10319     } else {\r
10320       WSACleanup();\r
10321       return err;\r
10322     }\r
10323   }\r
10324   sa.sin_family = hp->h_addrtype;\r
10325   uport = (unsigned short) 514;\r
10326   sa.sin_port = htons(uport);\r
10327   memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);\r
10328 \r
10329   /* Bind local socket to unused "privileged" port address\r
10330    */\r
10331   s = INVALID_SOCKET;\r
10332   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10333   mysa.sin_family = AF_INET;\r
10334   mysa.sin_addr.s_addr = INADDR_ANY;\r
10335   for (fromPort = 1023;; fromPort--) {\r
10336     if (fromPort < 0) {\r
10337       WSACleanup();\r
10338       return WSAEADDRINUSE;\r
10339     }\r
10340     if (s == INVALID_SOCKET) {\r
10341       if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10342         err = WSAGetLastError();\r
10343         WSACleanup();\r
10344         return err;\r
10345       }\r
10346     }\r
10347     uport = (unsigned short) fromPort;\r
10348     mysa.sin_port = htons(uport);\r
10349     if (bind(s, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10350         == SOCKET_ERROR) {\r
10351       err = WSAGetLastError();\r
10352       if (err == WSAEADDRINUSE) continue;\r
10353       WSACleanup();\r
10354       return err;\r
10355     }\r
10356     if (connect(s, (struct sockaddr *) &sa,\r
10357       sizeof(struct sockaddr_in)) == SOCKET_ERROR) {\r
10358       err = WSAGetLastError();\r
10359       if (err == WSAEADDRINUSE) {\r
10360         closesocket(s);\r
10361         s = -1;\r
10362         continue;\r
10363       }\r
10364       WSACleanup();\r
10365       return err;\r
10366     }\r
10367     break;\r
10368   }\r
10369 \r
10370   /* Bind stderr local socket to unused "privileged" port address\r
10371    */\r
10372   s2 = INVALID_SOCKET;\r
10373   memset((char *) &mysa, 0, sizeof(struct sockaddr_in));\r
10374   mysa.sin_family = AF_INET;\r
10375   mysa.sin_addr.s_addr = INADDR_ANY;\r
10376   for (fromPort = 1023;; fromPort--) {\r
10377     if (fromPort == prevStderrPort) continue; // don't reuse port\r
10378     if (fromPort < 0) {\r
10379       (void) closesocket(s);\r
10380       WSACleanup();\r
10381       return WSAEADDRINUSE;\r
10382     }\r
10383     if (s2 == INVALID_SOCKET) {\r
10384       if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {\r
10385         err = WSAGetLastError();\r
10386         closesocket(s);\r
10387         WSACleanup();\r
10388         return err;\r
10389       }\r
10390     }\r
10391     uport = (unsigned short) fromPort;\r
10392     mysa.sin_port = htons(uport);\r
10393     if (bind(s2, (struct sockaddr *) &mysa, sizeof(struct sockaddr_in))\r
10394         == SOCKET_ERROR) {\r
10395       err = WSAGetLastError();\r
10396       if (err == WSAEADDRINUSE) continue;\r
10397       (void) closesocket(s);\r
10398       WSACleanup();\r
10399       return err;\r
10400     }\r
10401     if (listen(s2, 1) == SOCKET_ERROR) {\r
10402       err = WSAGetLastError();\r
10403       if (err == WSAEADDRINUSE) {\r
10404         closesocket(s2);\r
10405         s2 = INVALID_SOCKET;\r
10406         continue;\r
10407       }\r
10408       (void) closesocket(s);\r
10409       (void) closesocket(s2);\r
10410       WSACleanup();\r
10411       return err;\r
10412     }\r
10413     break;\r
10414   }\r
10415   prevStderrPort = fromPort; // remember port used\r
10416   sprintf(stderrPortStr, "%d", fromPort);\r
10417 \r
10418   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
10419     err = WSAGetLastError();\r
10420     (void) closesocket(s);\r
10421     (void) closesocket(s2);\r
10422     WSACleanup();\r
10423     return err;\r
10424   }\r
10425 \r
10426   if (send(s, UserName(), strlen(UserName()) + 1, 0) == SOCKET_ERROR) {\r
10427     err = WSAGetLastError();\r
10428     (void) closesocket(s);\r
10429     (void) closesocket(s2);\r
10430     WSACleanup();\r
10431     return err;\r
10432   }\r
10433   if (*user == NULLCHAR) user = UserName();\r
10434   if (send(s, user, strlen(user) + 1, 0) == SOCKET_ERROR) {\r
10435     err = WSAGetLastError();\r
10436     (void) closesocket(s);\r
10437     (void) closesocket(s2);\r
10438     WSACleanup();\r
10439     return err;\r
10440   }\r
10441   if (send(s, cmd, strlen(cmd) + 1, 0) == SOCKET_ERROR) {\r
10442     err = WSAGetLastError();\r
10443     (void) closesocket(s);\r
10444     (void) closesocket(s2);\r
10445     WSACleanup();\r
10446     return err;\r
10447   }\r
10448 \r
10449   if ((s3 = accept(s2, NULL, NULL)) == INVALID_SOCKET) {\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   (void) closesocket(s2);  /* Stop listening */\r
10457 \r
10458   /* Prepare return value */\r
10459   cp = (ChildProc *) calloc(1, sizeof(ChildProc));\r
10460   cp->kind = CPRcmd;\r
10461   cp->sock = s;\r
10462   cp->sock2 = s3;\r
10463   *pr = (ProcRef *) cp;\r
10464 \r
10465   return NO_ERROR;\r
10466 }\r
10467 \r
10468 \r
10469 InputSourceRef\r
10470 AddInputSource(ProcRef pr, int lineByLine,\r
10471                InputCallback func, VOIDSTAR closure)\r
10472 {\r
10473   InputSource *is, *is2 = NULL;\r
10474   ChildProc *cp = (ChildProc *) pr;\r
10475 \r
10476   is = (InputSource *) calloc(1, sizeof(InputSource));\r
10477   is->lineByLine = lineByLine;\r
10478   is->func = func;\r
10479   is->closure = closure;\r
10480   is->second = NULL;\r
10481   is->next = is->buf;\r
10482   if (pr == NoProc) {\r
10483     is->kind = CPReal;\r
10484     consoleInputSource = is;\r
10485   } else {\r
10486     is->kind = cp->kind;\r
10487     /* \r
10488         [AS] Try to avoid a race condition if the thread is given control too early:\r
10489         we create all threads suspended so that the is->hThread variable can be\r
10490         safely assigned, then let the threads start with ResumeThread.\r
10491     */\r
10492     switch (cp->kind) {\r
10493     case CPReal:\r
10494       is->hFile = cp->hFrom;\r
10495       cp->hFrom = NULL; /* now owned by InputThread */\r
10496       is->hThread =\r
10497         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) NonOvlInputThread,\r
10498                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10499       break;\r
10500 \r
10501     case CPComm:\r
10502       is->hFile = cp->hFrom;\r
10503       cp->hFrom = NULL; /* now owned by InputThread */\r
10504       is->hThread =\r
10505         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) InputThread,\r
10506                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10507       break;\r
10508 \r
10509     case CPSock:\r
10510       is->sock = cp->sock;\r
10511       is->hThread =\r
10512         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10513                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10514       break;\r
10515 \r
10516     case CPRcmd:\r
10517       is2 = (InputSource *) calloc(1, sizeof(InputSource));\r
10518       *is2 = *is;\r
10519       is->sock = cp->sock;\r
10520       is->second = is2;\r
10521       is2->sock = cp->sock2;\r
10522       is2->second = is2;\r
10523       is->hThread =\r
10524         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10525                      (LPVOID) is, CREATE_SUSPENDED, &is->id);\r
10526       is2->hThread =\r
10527         CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SocketInputThread,\r
10528                      (LPVOID) is2, CREATE_SUSPENDED, &is2->id);\r
10529       break;\r
10530     }\r
10531 \r
10532     if( is->hThread != NULL ) {\r
10533         ResumeThread( is->hThread );\r
10534     }\r
10535 \r
10536     if( is2 != NULL && is2->hThread != NULL ) {\r
10537         ResumeThread( is2->hThread );\r
10538     }\r
10539   }\r
10540 \r
10541   return (InputSourceRef) is;\r
10542 }\r
10543 \r
10544 void\r
10545 RemoveInputSource(InputSourceRef isr)\r
10546 {\r
10547   InputSource *is;\r
10548 \r
10549   is = (InputSource *) isr;\r
10550   is->hThread = NULL;  /* tell thread to stop */\r
10551   CloseHandle(is->hThread);\r
10552   if (is->second != NULL) {\r
10553     is->second->hThread = NULL;\r
10554     CloseHandle(is->second->hThread);\r
10555   }\r
10556 }\r
10557 \r
10558 \r
10559 int\r
10560 OutputToProcess(ProcRef pr, char *message, int count, int *outError)\r
10561 {\r
10562   DWORD dOutCount;\r
10563   int outCount = SOCKET_ERROR;\r
10564   ChildProc *cp = (ChildProc *) pr;\r
10565   static OVERLAPPED ovl;\r
10566 \r
10567   if (pr == NoProc) {\r
10568     ConsoleOutput(message, count, FALSE);\r
10569     return count;\r
10570   } \r
10571 \r
10572   if (ovl.hEvent == NULL) {\r
10573     ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r
10574   }\r
10575   ovl.Internal = ovl.InternalHigh = ovl.Offset = ovl.OffsetHigh = 0;\r
10576 \r
10577   switch (cp->kind) {\r
10578   case CPSock:\r
10579   case CPRcmd:\r
10580     outCount = send(cp->sock, message, count, 0);\r
10581     if (outCount == SOCKET_ERROR) {\r
10582       *outError = WSAGetLastError();\r
10583     } else {\r
10584       *outError = NO_ERROR;\r
10585     }\r
10586     break;\r
10587 \r
10588   case CPReal:\r
10589     if (WriteFile(((ChildProc *)pr)->hTo, message, count,\r
10590                   &dOutCount, NULL)) {\r
10591       *outError = NO_ERROR;\r
10592       outCount = (int) dOutCount;\r
10593     } else {\r
10594       *outError = GetLastError();\r
10595     }\r
10596     break;\r
10597 \r
10598   case CPComm:\r
10599     *outError = DoWriteFile(((ChildProc *)pr)->hTo, message, count,\r
10600                             &dOutCount, &ovl);\r
10601     if (*outError == NO_ERROR) {\r
10602       outCount = (int) dOutCount;\r
10603     }\r
10604     break;\r
10605   }\r
10606   return outCount;\r
10607 }\r
10608 \r
10609 int\r
10610 OutputToProcessDelayed(ProcRef pr, char *message, int count, int *outError,\r
10611                        long msdelay)\r
10612 {\r
10613   /* Ignore delay, not implemented for WinBoard */\r
10614   return OutputToProcess(pr, message, count, outError);\r
10615 }\r
10616 \r
10617 \r
10618 void\r
10619 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
10620                         char *buf, int count, int error)\r
10621 {\r
10622   DisplayFatalError("Not implemented", 0, 1);\r
10623 }\r
10624 \r
10625 /* see wgamelist.c for Game List functions */\r
10626 /* see wedittags.c for Edit Tags functions */\r
10627 \r
10628 \r
10629 VOID\r
10630 ICSInitScript()\r
10631 {\r
10632   FILE *f;\r
10633   char buf[MSG_SIZ];\r
10634   char *dummy;\r
10635 \r
10636   if (SearchPath(installDir, appData.icsLogon, NULL, MSG_SIZ, buf, &dummy)) {\r
10637     f = fopen(buf, "r");\r
10638     if (f != NULL) {\r
10639       ProcessICSInitScript(f);\r
10640       fclose(f);\r
10641     }\r
10642   }\r
10643 }\r
10644 \r
10645 \r
10646 VOID\r
10647 StartAnalysisClock()\r
10648 {\r
10649   if (analysisTimerEvent) return;\r
10650   analysisTimerEvent = SetTimer(hwndMain, (UINT) ANALYSIS_TIMER_ID,\r
10651                                         (UINT) 2000, NULL);\r
10652 }\r
10653 \r
10654 LRESULT CALLBACK\r
10655 AnalysisDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
10656 {\r
10657   static HANDLE hwndText;\r
10658   RECT rect;\r
10659   static int sizeX, sizeY;\r
10660   int newSizeX, newSizeY, flags;\r
10661   MINMAXINFO *mmi;\r
10662 \r
10663   switch (message) {\r
10664   case WM_INITDIALOG: /* message: initialize dialog box */\r
10665     /* Initialize the dialog items */\r
10666     hwndText = GetDlgItem(hDlg, OPT_AnalysisText);\r
10667     SetWindowText(hDlg, analysisTitle);\r
10668     SetDlgItemText(hDlg, OPT_AnalysisText, analysisText);\r
10669     /* Size and position the dialog */\r
10670     if (!analysisDialog) {\r
10671       analysisDialog = hDlg;\r
10672       flags = SWP_NOZORDER;\r
10673       GetClientRect(hDlg, &rect);\r
10674       sizeX = rect.right;\r
10675       sizeY = rect.bottom;\r
10676       if (analysisX != CW_USEDEFAULT && analysisY != CW_USEDEFAULT &&\r
10677           analysisW != CW_USEDEFAULT && analysisH != CW_USEDEFAULT) {\r
10678         WINDOWPLACEMENT wp;\r
10679         EnsureOnScreen(&analysisX, &analysisY, 0, 0);\r
10680         wp.length = sizeof(WINDOWPLACEMENT);\r
10681         wp.flags = 0;\r
10682         wp.showCmd = SW_SHOW;\r
10683         wp.ptMaxPosition.x = wp.ptMaxPosition.y = 0;\r
10684         wp.rcNormalPosition.left = analysisX;\r
10685         wp.rcNormalPosition.right = analysisX + analysisW;\r
10686         wp.rcNormalPosition.top = analysisY;\r
10687         wp.rcNormalPosition.bottom = analysisY + analysisH;\r
10688         SetWindowPlacement(hDlg, &wp);\r
10689 \r
10690         GetClientRect(hDlg, &rect);\r
10691         newSizeX = rect.right;\r
10692         newSizeY = rect.bottom;\r
10693         ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY,\r
10694                               newSizeX, newSizeY);\r
10695         sizeX = newSizeX;\r
10696         sizeY = newSizeY;\r
10697       }\r
10698     }\r
10699     return FALSE;\r
10700 \r
10701   case WM_COMMAND: /* message: received a command */\r
10702     switch (LOWORD(wParam)) {\r
10703     case IDCANCEL:\r
10704       if (appData.icsActive && appData.icsEngineAnalyze) { /* [DM] icsEngineAnalyze */\r
10705           ExitAnalyzeMode();\r
10706           ModeHighlight();\r
10707           return TRUE;\r
10708       }\r
10709       EditGameEvent();\r
10710       return TRUE;\r
10711     default:\r
10712       break;\r
10713     }\r
10714     break;\r
10715 \r
10716   case WM_SIZE:\r
10717     newSizeX = LOWORD(lParam);\r
10718     newSizeY = HIWORD(lParam);\r
10719     ResizeEditPlusButtons(hDlg, hwndText, sizeX, sizeY, newSizeX, newSizeY);\r
10720     sizeX = newSizeX;\r
10721     sizeY = newSizeY;\r
10722     break;\r
10723 \r
10724   case WM_GETMINMAXINFO:\r
10725     /* Prevent resizing window too small */\r
10726     mmi = (MINMAXINFO *) lParam;\r
10727     mmi->ptMinTrackSize.x = 100;\r
10728     mmi->ptMinTrackSize.y = 100;\r
10729     break;\r
10730   }\r
10731   return FALSE;\r
10732 }\r
10733 \r
10734 VOID\r
10735 AnalysisPopUp(char* title, char* str)\r
10736 {\r
10737   FARPROC lpProc;\r
10738   char *p, *q;\r
10739 \r
10740   /* [AS] */\r
10741   EngineOutputPopUp();\r
10742   return;\r
10743 \r
10744   if (str == NULL) str = "";\r
10745   p = (char *) malloc(2 * strlen(str) + 2);\r
10746   q = p;\r
10747   while (*str) {\r
10748     if (*str == '\n') *q++ = '\r';\r
10749     *q++ = *str++;\r
10750   }\r
10751   *q = NULLCHAR;\r
10752   if (analysisText != NULL) free(analysisText);\r
10753   analysisText = p;\r
10754 \r
10755   if (analysisDialog) {\r
10756     SetWindowText(analysisDialog, title);\r
10757     SetDlgItemText(analysisDialog, OPT_AnalysisText, analysisText);\r
10758     ShowWindow(analysisDialog, SW_SHOW);\r
10759   } else {\r
10760     analysisTitle = title;\r
10761     lpProc = MakeProcInstance((FARPROC)AnalysisDialog, hInst);\r
10762     CreateDialog(hInst, MAKEINTRESOURCE(DLG_Analysis),\r
10763                  hwndMain, (DLGPROC)lpProc);\r
10764     FreeProcInstance(lpProc);\r
10765   }\r
10766   analysisDialogUp = TRUE;  \r
10767 }\r
10768 \r
10769 VOID\r
10770 AnalysisPopDown()\r
10771 {\r
10772   if (analysisDialog) {\r
10773     ShowWindow(analysisDialog, SW_HIDE);\r
10774   }\r
10775   analysisDialogUp = FALSE;  \r
10776 }\r
10777 \r
10778 \r
10779 VOID\r
10780 SetHighlights(int fromX, int fromY, int toX, int toY)\r
10781 {\r
10782   highlightInfo.sq[0].x = fromX;\r
10783   highlightInfo.sq[0].y = fromY;\r
10784   highlightInfo.sq[1].x = toX;\r
10785   highlightInfo.sq[1].y = toY;\r
10786 }\r
10787 \r
10788 VOID\r
10789 ClearHighlights()\r
10790 {\r
10791   highlightInfo.sq[0].x = highlightInfo.sq[0].y = \r
10792     highlightInfo.sq[1].x = highlightInfo.sq[1].y = -1;\r
10793 }\r
10794 \r
10795 VOID\r
10796 SetPremoveHighlights(int fromX, int fromY, int toX, int toY)\r
10797 {\r
10798   premoveHighlightInfo.sq[0].x = fromX;\r
10799   premoveHighlightInfo.sq[0].y = fromY;\r
10800   premoveHighlightInfo.sq[1].x = toX;\r
10801   premoveHighlightInfo.sq[1].y = toY;\r
10802 }\r
10803 \r
10804 VOID\r
10805 ClearPremoveHighlights()\r
10806 {\r
10807   premoveHighlightInfo.sq[0].x = premoveHighlightInfo.sq[0].y = \r
10808     premoveHighlightInfo.sq[1].x = premoveHighlightInfo.sq[1].y = -1;\r
10809 }\r
10810 \r
10811 VOID\r
10812 ShutDownFrontEnd()\r
10813 {\r
10814   if (saveSettingsOnExit) SaveSettings(settingsFileName);\r
10815   DeleteClipboardTempFiles();\r
10816 }\r
10817 \r
10818 void\r
10819 BoardToTop()\r
10820 {\r
10821     if (IsIconic(hwndMain))\r
10822       ShowWindow(hwndMain, SW_RESTORE);\r
10823 \r
10824     SetActiveWindow(hwndMain);\r
10825 }\r
10826 \r
10827 /*\r
10828  * Prototypes for animation support routines\r
10829  */\r
10830 static void ScreenSquare(int column, int row, POINT * pt);\r
10831 static void Tween( POINT * start, POINT * mid, POINT * finish, int factor,\r
10832      POINT frames[], int * nFrames);\r
10833 \r
10834 \r
10835 void\r
10836 AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames)\r
10837 {       // [HGM] atomic: animate blast wave\r
10838         int i;\r
10839 if(appData.debugMode) fprintf(debugFP, "exploding (%d,%d)\n", toX, toY);\r
10840         explodeInfo.fromX = fromX;\r
10841         explodeInfo.fromY = fromY;\r
10842         explodeInfo.toX = toX;\r
10843         explodeInfo.toY = toY;\r
10844         for(i=1; i<nFrames; i++) {\r
10845             explodeInfo.radius = (i*180)/(nFrames-1);\r
10846             DrawPosition(FALSE, NULL);\r
10847             Sleep(appData.animSpeed);\r
10848         }\r
10849         explodeInfo.radius = 0;\r
10850         DrawPosition(TRUE, NULL);\r
10851 }\r
10852 \r
10853 #define kFactor 4\r
10854 \r
10855 void\r
10856 AnimateMove(board, fromX, fromY, toX, toY)\r
10857      Board board;\r
10858      int fromX;\r
10859      int fromY;\r
10860      int toX;\r
10861      int toY;\r
10862 {\r
10863   ChessSquare piece;\r
10864   POINT start, finish, mid;\r
10865   POINT frames[kFactor * 2 + 1];\r
10866   int nFrames, n;\r
10867 \r
10868   if (!appData.animate) return;\r
10869   if (doingSizing) return;\r
10870   if (fromY < 0 || fromX < 0) return;\r
10871   piece = board[fromY][fromX];\r
10872   if (piece >= EmptySquare) return;\r
10873 \r
10874   ScreenSquare(fromX, fromY, &start);\r
10875   ScreenSquare(toX, toY, &finish);\r
10876 \r
10877   /* All pieces except knights move in straight line */\r
10878   if (piece != WhiteKnight && piece != BlackKnight) {\r
10879     mid.x = start.x + (finish.x - start.x) / 2;\r
10880     mid.y = start.y + (finish.y - start.y) / 2;\r
10881   } else {\r
10882     /* Knight: make diagonal movement then straight */\r
10883     if (abs(toY - fromY) < abs(toX - fromX)) {\r
10884        mid.x = start.x + (finish.x - start.x) / 2;\r
10885        mid.y = finish.y;\r
10886      } else {\r
10887        mid.x = finish.x;\r
10888        mid.y = start.y + (finish.y - start.y) / 2;\r
10889      }\r
10890   }\r
10891   \r
10892   /* Don't use as many frames for very short moves */\r
10893   if (abs(toY - fromY) + abs(toX - fromX) <= 2)\r
10894     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);\r
10895   else\r
10896     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
10897 \r
10898   animInfo.from.x = fromX;\r
10899   animInfo.from.y = fromY;\r
10900   animInfo.to.x = toX;\r
10901   animInfo.to.y = toY;\r
10902   animInfo.lastpos = start;\r
10903   animInfo.piece = piece;\r
10904   for (n = 0; n < nFrames; n++) {\r
10905     animInfo.pos = frames[n];\r
10906     DrawPosition(FALSE, NULL);\r
10907     animInfo.lastpos = animInfo.pos;\r
10908     Sleep(appData.animSpeed);\r
10909   }\r
10910   animInfo.pos = finish;\r
10911   DrawPosition(FALSE, NULL);\r
10912   animInfo.piece = EmptySquare;\r
10913   if(gameInfo.variant == VariantAtomic && \r
10914      (board[toY][toX] != EmptySquare || fromX != toX && (piece == WhitePawn || piece == BlackPawn) ) )\r
10915         AnimateAtomicCapture(fromX, fromY, toX, toY, 2*nFrames);\r
10916 }\r
10917 \r
10918 /*      Convert board position to corner of screen rect and color       */\r
10919 \r
10920 static void\r
10921 ScreenSquare(column, row, pt)\r
10922      int column; int row; POINT * pt;\r
10923 {\r
10924   if (flipView) {\r
10925     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);\r
10926     pt->y = lineGap + row * (squareSize + lineGap);\r
10927   } else {\r
10928     pt->x = lineGap + column * (squareSize + lineGap);\r
10929     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);\r
10930   }\r
10931 }\r
10932 \r
10933 /*      Generate a series of frame coords from start->mid->finish.\r
10934         The movement rate doubles until the half way point is\r
10935         reached, then halves back down to the final destination,\r
10936         which gives a nice slow in/out effect. The algorithmn\r
10937         may seem to generate too many intermediates for short\r
10938         moves, but remember that the purpose is to attract the\r
10939         viewers attention to the piece about to be moved and\r
10940         then to where it ends up. Too few frames would be less\r
10941         noticeable.                                             */\r
10942 \r
10943 static void\r
10944 Tween(start, mid, finish, factor, frames, nFrames)\r
10945      POINT * start; POINT * mid;\r
10946      POINT * finish; int factor;\r
10947      POINT frames[]; int * nFrames;\r
10948 {\r
10949   int n, fraction = 1, count = 0;\r
10950 \r
10951   /* Slow in, stepping 1/16th, then 1/8th, ... */\r
10952   for (n = 0; n < factor; n++)\r
10953     fraction *= 2;\r
10954   for (n = 0; n < factor; n++) {\r
10955     frames[count].x = start->x + (mid->x - start->x) / fraction;\r
10956     frames[count].y = start->y + (mid->y - start->y) / fraction;\r
10957     count ++;\r
10958     fraction = fraction / 2;\r
10959   }\r
10960   \r
10961   /* Midpoint */\r
10962   frames[count] = *mid;\r
10963   count ++;\r
10964   \r
10965   /* Slow out, stepping 1/2, then 1/4, ... */\r
10966   fraction = 2;\r
10967   for (n = 0; n < factor; n++) {\r
10968     frames[count].x = finish->x - (finish->x - mid->x) / fraction;\r
10969     frames[count].y = finish->y - (finish->y - mid->y) / fraction;\r
10970     count ++;\r
10971     fraction = fraction * 2;\r
10972   }\r
10973   *nFrames = count;\r
10974 }\r
10975 \r
10976 void\r
10977 HistorySet( char movelist[][2*MOVE_LEN], int first, int last, int current )\r
10978 {\r
10979 #if 0\r
10980     char buf[256];\r
10981 \r
10982     sprintf( buf, "HistorySet: first=%d, last=%d, current=%d (%s)\n",\r
10983         first, last, current, current >= 0 ? movelist[current] : "n/a" );\r
10984 \r
10985     OutputDebugString( buf );\r
10986 #endif\r
10987 \r
10988     MoveHistorySet( movelist, first, last, current, pvInfoList );\r
10989 \r
10990     EvalGraphSet( first, last, current, pvInfoList );\r
10991 }\r
10992 \r
10993 void SetProgramStats( FrontEndProgramStats * stats )\r
10994 {\r
10995 #if 0\r
10996     char buf[1024];\r
10997 \r
10998     sprintf( buf, "SetStats for %d: depth=%d, nodes=%lu, score=%5.2f, time=%5.2f, pv=%s\n",\r
10999         stats->which, stats->depth, stats->nodes, stats->score / 100.0, stats->time / 100.0, stats->pv == 0 ? "n/a" : stats->pv );\r
11000 \r
11001     OutputDebugString( buf );\r
11002 #endif\r
11003 \r
11004     EngineOutputUpdate( stats );\r
11005 }\r